Hello.
I'm building a script UI panel as part of an extension that involves keyboard controls. I believe I've encountered a fundamental bug with the .preventDefault function/method, at least when assigned as an event listener to an edittext control object. In short, it only works on Mac—the method appears to be non-functional on Windows.
I've built a stripped-down derivative of the script I'm building that you can use to reproduce this bug at will. Below are screenshots of the panel on Mac and Windows and the differing behaviours.


Mac:
After Effects 24.6.1 - Ventura 13.7.3 (Intel)
Windows:
After Effects 25.1.0 - Windows 10
Example .jsx Script UI Panel:
/* ----------------------------------------------------------------------
* PreventDefaultTest.jsx
*
* ---------------------------------------------------------------------- */
// Initialize globals and panelNSs (namespace)
if (typeof $.globals === "undefined") {
$.globals = {};
}
if (typeof $.globals.debuggingPanel === "undefined") {
$.globals.debuggingPanel = {};
}
if (typeof $.globals.debuggingPanel.testPanel === "undefined") {
$.globals.debuggingPanel.testPanel = {};
}
if (typeof $.globals.preventDefaultTest !== "object") {
$.globals.preventDefaultTest = {};
}
// Use shorthand reference for consistency
var panelNS = $.globals.debuggingPanel.testPanel;
// Global flag to track if panel is open
$.globals.testPanelIsOpen = false;
// Define Current Adobe App
var currentAdobeApp = BridgeTalk.appName;
// Platform detection
panelNS.osString = $.os && typeof $.os === "string" ? $.os : "";
panelNS.osStringLower = panelNS.osString.toLowerCase();
panelNS.isWindows = (panelNS.osStringLower.indexOf("windows") === 0);
// Array of alphabetical keys to block
panelNS.alphabetKeys = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"];
/* -------------------------------
* Logging
* ------------------------------- */
// Enable logging
// Default to false
panelNS.LOGGING_ENABLED = false;
// Log file path - optional for additional debugging
if (panelNS.isWindows) {
var logFolder = new Folder("C:\\Users\\Public");
if (logFolder.exists) {
panelNS.logFilePath = "C:\\Users\\Public\\PreventDefaultTest.txt";
}
} else {
panelNS.logFilePath = "~/Documents/PreventDefaultTest.txt"; // User's Documents folder
}
// Pad numbers for date formatting
$.globals.preventDefaultTest.pad = function(n) {
return n < 10 ? "0" + n : "" + n;
};
// Format date as ISO-like string
$.globals.preventDefaultTest.formatDateISO = function(date) {
return (
date.getFullYear() +
"-" +
$.globals.preventDefaultTest.pad(date.getMonth() + 1) +
"-" +
$.globals.preventDefaultTest.pad(date.getDate()) +
"T" +
$.globals.preventDefaultTest.pad(date.getHours()) +
":" +
$.globals.preventDefaultTest.pad(date.getMinutes()) +
":" +
$.globals.preventDefaultTest.pad(date.getSeconds())
);
};
// Function to log messages to a file for debugging purposes
$.globals.preventDefaultTest.logMessage = function(message) {
if (!panelNS.LOGGING_ENABLED || !panelNS.logFilePath) return;
try {
var logFile = new File(panelNS.logFilePath);
if (!logFile.exists) {
// Create the file if it doesn't exist
logFile.open("w");
logFile.writeln("--- PreventDefault Test Log ---");
logFile.close();
}
var openSuccess = logFile.open("a");
if (!openSuccess) return;
try {
logFile.writeln($.globals.preventDefaultTest.formatDateISO(new Date()) + " - " + message);
} finally {
logFile.close();
}
} catch (e) {
// Silent failure
}
};
// Log initial platform info
$.globals.preventDefaultTest.logMessage("==========================================");
$.globals.preventDefaultTest.logMessage("Starting PreventDefault Test Panel");
$.globals.preventDefaultTest.logMessage("Platform: " + panelNS.osString + ", isWindows: " + panelNS.isWindows);
$.globals.preventDefaultTest.logMessage("ScriptUI version: " + ScriptUI.version);
$.globals.preventDefaultTest.logMessage("==========================================");
/* -------------------------------
* Helper Functions
* ------------------------------- */
// Helper function to find index of item in array (ES3 compatible)
function indexOf(arr, item) {
for (var i = 0; i < arr.length; i++) {
if (arr[i] === item) {
return i;
}
}
return -1;
}
/* ----------------------------------------------------------------------
* Main UI
* ---------------------------------------------------------------------- */
// Function to build the main UI for the test panel
$.globals.preventDefaultTest.buildSimpleUI = function() {
try {
// Create a simple window
var win = new Window("palette", "PreventDefault Test", undefined, {
closeButton: true,
borderless: false
});
win.orientation = "column";
win.alignChildren = ["fill", "top"];
win.spacing = 10;
win.margins = 16;
win.preferredSize.width = 300;
// Add instructions label
var instructionsLabel = win.add("statictext", undefined, "Type in the text field below.\nAlphabetical keys should be blocked.");
instructionsLabel.alignment = ["fill", "top"];
// Add status text that will show keyboard event info
var statusText = win.add("statictext", undefined, "Status: Waiting for keyboard input...");
statusText.alignment = ["fill", "top"];
// Create text field with border
var textField = win.add("edittext", undefined, "");
textField.alignment = ["fill", "top"];
textField.preferredSize.height = 25;
// Add event log display area - multiline text field to show recent events
var eventLog = win.add("edittext", undefined, "", {multiline: true, readonly: true});
eventLog.alignment = ["fill", "fill"];
eventLog.preferredSize.height = 200;
// Function to append to the event log
function appendToEventLog(text) {
// Replace weird symbols with proper characters or remove them
text = text.replace(/[–¼]/g, "");
eventLog.text = eventLog.text + text + "\n";
// Keep only the last 15 lines
var lines = eventLog.text.split("\n");
if (lines.length > 15) {
eventLog.text = lines.slice(lines.length - 15).join("\n");
}
eventLog.active = false; // Prevent focus from moving to the log
}
// Log window creation
$.globals.preventDefaultTest.logMessage("Window created successfully");
// Add keyboard event listeners to the text field
textField.addEventListener("keydown", function(e) {
var pressedKey = String(e.keyName || "").toLowerCase();
var modifiers = (e.shiftKey ? "Shift+" : "") +
(e.ctrlKey ? "Ctrl+" : "") +
(e.altKey ? "Alt+" : "") +
(e.metaKey ? "Meta+" : "");
var eventInfo = "KeyDown: '" + pressedKey + "'" +
(modifiers ? " with modifiers: " + modifiers : "") +
", keyCode: " + (e.keyCode || "N/A") +
", charCode: " + (e.charCode || "N/A");
$.globals.preventDefaultTest.logMessage(eventInfo);
statusText.text = "Status: " + eventInfo;
appendToEventLog("Event info: " + eventInfo);
// Close with Escape key
if (pressedKey === "escape") {
$.globals.preventDefaultTest.logMessage("Escape key pressed - closing window");
appendToEventLog("Escape pressed - closing window");
e.preventDefault();
win.close();
return;
}
// Check for alphabetic keys and block them
var keyIndex = indexOf(panelNS.alphabetKeys, pressedKey);
var isAlpha = keyIndex !== -1 && !e.ctrlKey && !e.metaKey;
if (isAlpha) {
$.globals.preventDefaultTest.logMessage("BLOCKED alphabetic key: " + pressedKey);
appendToEventLog("BLOCKING alphabetic key: " + pressedKey);
e.preventDefault();
return false;
} else {
appendToEventLog("Allowed key: " + pressedKey);
}
});
textField.addEventListener("keyup", function(e) {
var pressedKey = String(e.keyName || "").toLowerCase();
var eventInfo = "KeyUp: '" + pressedKey + "'";
$.globals.preventDefaultTest.logMessage(eventInfo);
// Don't need to block on keyup usually, but adding for completeness
var keyIndex = indexOf(panelNS.alphabetKeys, pressedKey);
var isAlpha = keyIndex !== -1 && !e.ctrlKey && !e.metaKey;
if (isAlpha) {
e.preventDefault();
return false;
}
});
// Also add key listeners to the window for capturing all key events
win.addEventListener("keydown", function(e) {
var pressedKey = String(e.keyName || "").toLowerCase();
// Only log if not already caught by the text field
if (ScriptUI.focusControl !== textField) {
var modifiers = (e.shiftKey ? "Shift+" : "") +
(e.ctrlKey ? "Ctrl+" : "") +
(e.altKey ? "Alt+" : "") +
(e.metaKey ? "Meta+" : "");
var eventInfo = "Window KeyDown: '" + pressedKey + "'" +
(modifiers ? " with modifiers: " + modifiers : "");
$.globals.preventDefaultTest.logMessage(eventInfo);
statusText.text = "Status: " + eventInfo;
appendToEventLog("LOG:" + eventInfo);
// Close with Escape
if (pressedKey === "escape") {
$.globals.preventDefaultTest.logMessage("Escape key pressed in window - closing");
win.close();
return;
}
}
});
// Add close handler
win.onClose = function() {
$.globals.preventDefaultTest.logMessage("Window closed");
$.globals.testPanelIsOpen = false;
};
// Add an info section at the bottom
var infoGroup = win.add("group");
infoGroup.orientation = "column";
infoGroup.alignment = ["fill", "bottom"];
var platformInfo = infoGroup.add("statictext", undefined,
"Platform: " + (panelNS.isWindows ? "Windows" : "macOS") +
" | ScriptUI v" + ScriptUI.version);
platformInfo.alignment = ["fill", "bottom"];
return win;
} catch (e) {
$.globals.preventDefaultTest.logMessage("ERROR building UI: " + e + " at line " + e.line);
alert("Error building test panel: " + e.toString());
return null;
}
};
/* ----------------------------------------------------------------------
* Main Entry Point
* ---------------------------------------------------------------------- */
// Main entry point to initialize and display the test panel
$.globals.preventDefaultTest.main = function() {
// Check if already open
if ($.globals.testPanelIsOpen) {
$.globals.preventDefaultTest.logMessage("Test panel is already open, exiting");
return;
}
try {
var win = $.globals.preventDefaultTest.buildSimpleUI();
if (!win) return;
// Set panel flag
$.globals.testPanelIsOpen = true;
// Show window
$.globals.preventDefaultTest.logMessage("Showing window");
win.show();
win.update();
$.globals.preventDefaultTest.logMessage("Window shown successfully");
} catch (e) {
$.globals.preventDefaultTest.logMessage("ERROR in main: " + e + " at line " + e.line);
alert("Failed to open test panel: " + e.toString());
if ($.globals.testPanelIsOpen) {
delete $.globals.testPanelIsOpen;
}
}
};
/* -----------------
* Run Test Panel
* ----------------- */
// Ensure the script is running in a supported Adobe application (disable if needed)
if (currentAdobeApp === "aftereffects" || currentAdobeApp) {
// Run the test panel
$.globals.preventDefaultTest.main();
} else {
alert("This script is designed for Adobe applications.");
}