The Document beforescriptexecute event once fired before a static<script> started running in Firefox. Cancelling it could stop execution. Learn why dynamic scripts were excluded, how it differed from afterscriptexecute, and which portable approaches to use instead—with five examples and try-it labs.
01
Kind
Document event
02
Type
Event
03
Cancelable
Yes (legacy)
04
Status
Deprecated · Non-standard
05
Target scripts
Static only
06
Modern path
CSP / safe loading
Fundamentals
Introduction
Early Gecko experiments added a pair of proprietary Document events around script execution: beforescriptexecute (before run) and afterscriptexecute (after run). The “before” event was interesting because cancelling it could block the script.
On Document, it answered: is this static script about to start? That idea never became a web standard. Other browsers never shipped it, MDN warns not to rely on it, and modern Firefox stopped exposing useful web-facing support.
⚠️
Deprecated & non-standard
Learn this event for legacy Firefox code and interviews. For new apps, control scripts with CSP, trusted sources, modules, and careful DOM insertion—not Gecko-only events.
Concept
Understanding beforescriptexecute
A Document event listened on document in supporting Gecko. Per MDN, it fired when a static<script> was about to start executing—not when you insert a script later with appendChild() or similar.
Fires when a static script is about to be executed (legacy Firefox).
Does not fire for scripts added dynamically (for example appendChild()).
Cancelable — preventDefault() could stop execution in old Gecko.
Generic Event — no special payload; event.target is the script element.
In legacy Gecko, cancelling the event prevented the script from executing. Call event.preventDefault() inside the handler. Modern browsers typically never fire this event at all.
Typical listener (MDN style)
JavaScript
function starting(e) {
console.log(`Starting script with ID: ${e.target.id}`);
}
document.addEventListener("beforescriptexecute", starting);
// or
document.onbeforescriptexecute = starting;
Compare
⚖️ beforescriptexecute vs afterscriptexecute vs CSP
API
When it fires
Portable?
beforescriptexecute
Static script about to run (could cancel)
No — legacy Gecko only
afterscriptexecute
Static script just finished running
No — legacy Gecko only
Content Security Policy
Browser enforces which scripts may run
Yes — prefer this
scriptload / error
External script finished or failed
Yes — after-run hooks
document.currentScript
Points at the script currently executing
Yes — standard property
Replacement
🚀 What to Use Instead
There is no portable DOM event that cancels arbitrary scripts the way legacy beforescriptexecute did. Use layered, standard approaches:
CSP — declare which script sources (and inline policies) are allowed.
Do not inject untrusted scripts — fix the source, not the symptom.
ES modules — explicit imports instead of ad-hoc script tags.
MutationObserver — some legacy blockers changed a new script’s type before it ran (fragile; CSP is better).
JavaScript
Content-Security-Policy: script-src 'self'
That header (or a matching <meta> policy, where appropriate) is the modern, cross-browser way to control which scripts may execute—static or dynamic.
"onbeforescriptexecute" in document (weak; still prefer alternatives)
Modern replace
CSP + trusted script loading
MDN status
Deprecated · Non-standard
Snapshot
🔍 At a Glance
Four facts to remember about Document beforescriptexecute.
Event type
Event
Generic
Cancelable
yes*
*legacy Gecko
Scripts
static
Not dynamic
New code?
avoid
Use CSP
Hands-On
Examples Gallery
Examples follow MDN Document: beforescriptexecute event and show safe feature detection plus modern replacements. In most current browsers the proprietary event will never fire—the labs still teach the patterns clearly.
📚 Getting Started
Register handlers the MDN ways (legacy Gecko).
Example 1 — addEventListener("beforescriptexecute")
MDN style: listen on document and log the starting script’s id (where supported).
JavaScript
function starting(e) {
console.log(`Starting script with ID: ${e.target.id}`);
}
document.addEventListener("beforescriptexecute", starting);
// In legacy Firefox, a static <script id="demo"> could fire this before it ran.
// In modern browsers it typically never fires.
Listener attached (event may never fire in modern browsers)
How It Works
You register the same way as any other DOM event. Support is the hard part: without legacy Gecko behavior, the handler sits idle. When it did fire, event.target was the <script> element about to run.
Example 2 — onbeforescriptexecute Property
MDN’s alternate style using the event handler property on document.
JavaScript
document.onbeforescriptexecute = (event) => {
console.log("Script about to run (onbeforescriptexecute)");
console.log("target id:", event.target && event.target.id);
};
// Assigning again replaces the previous handler.
// Prefer addEventListener when you need multiple listeners.
Same event, different registration API. One property means one handler—easy to overwrite by accident.
📈 Cancel, Limits & Replace
Legacy cancel path, static-only MDN rule, and modern controls.
Example 3 — Cancel with preventDefault()
In old Gecko this could block a static script. Today the event usually never fires.
JavaScript
document.addEventListener("beforescriptexecute", (event) => {
console.log("Blocking script execution (legacy only)");
event.preventDefault();
});
// Historical: cancel stopped a static script from running.
// Modern browsers: listener is usually a no-op (event never fires).
console.log("Cancel listener ready (event may never fire)");
This was the unique selling point of beforescriptexecute versus afterscriptexecute. Without Gecko support, preventDefault() here does nothing useful—use CSP instead.
Example 4 — Static vs Dynamic (MDN Rule)
Dynamic appendChild() scripts would not trigger beforescriptexecute even in old Firefox.
JavaScript
document.addEventListener("beforescriptexecute", (e) => {
console.log("beforescriptexecute for:", e.target && e.target.id);
});
// Dynamic insert — MDN: beforescriptexecute does NOT fire for this path
const dynamic = document.createElement("script");
dynamic.id = "dynamic-demo";
dynamic.textContent = "console.log('dynamic script ran');";
document.body.appendChild(dynamic);
console.log("Dynamic script appended");
console.log("MDN: beforescriptexecute skips dynamically added scripts");
console.log("Prefer CSP / trusted loading instead");
The injected script still runs when appended. The proprietary Document event simply was not designed to intercept dynamic inserts—another reason CSP is a better teaching target for beginners.
Example 5 — Modern Thinking (CSP + Observer Demo)
Prefer CSP in production. This lab shows a fragile teaching demo some legacy blockers used: change an injected script’s type so it will not execute as JS.
JavaScript
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
if (node.nodeName === "SCRIPT" && node.dataset?.demo === "block") {
node.type = "text/plain";
console.log("Neutralized demo script");
}
}
}
});
observer.observe(document.documentElement, { childList: true, subtree: true });
const s = document.createElement("script");
s.dataset.demo = "block";
s.textContent = "window.__shouldStayUndefined = true;";
document.body.appendChild(s);
console.log("__shouldStayUndefined:", typeof window.__shouldStayUndefined);
console.log("(Prefer CSP in production; this is a teaching demo only.)");
Neutralized demo script
__shouldStayUndefined: undefined
(Prefer CSP in production; this is a teaching demo only.)
How It Works
Changing type to something other than a JS MIME type prevents execution for that demo script. Race conditions and incomplete coverage make this a poor security foundation—CSP belongs in the HTTP response (or carefully designed meta policies).
Applications
🚀 Common Use Cases
Understanding legacy Firefox / Gecko code that tried to cancel static scripts.
Migrating old extensions or pages off proprietary script-execute events.
Interview / history knowledge of early HTML script lifecycle proposals.
Teaching why static-vs-dynamic limits and standards matter for DOM events.
Choosing CSP, trusted loading, currentScript, or ES modules for real production work.
Under the Hood
🔧 How It Works
1
Static script is ready
A <script> already in the markup is about to run.
Prepare
2
beforescriptexecute (legacy)
Old Gecko dispatched a Document Event; dynamic inserts skipped.
Notify
3
Optional cancel
preventDefault() could stop that script in supporting Gecko.
Gate
4
✓
Prefer CSP today
Use Content Security Policy and trusted script loading in new code.
Important
📝 Notes
MDN: Deprecated and Non-standard — avoid in production.
Applies to static scripts only; dynamic appendChild() scripts do not fire it.
Was cancelable in legacy Gecko; sibling afterscriptexecute was not.
Proprietary to Gecko; never a cross-browser API; not part of any specification.
beforescriptexecute is a deprecated, non-standard Gecko Document event. Logos use the shared browser-image-sprite.png sprite from this project. Other engines never implemented it; modern Firefox no longer dispatches it to web content. Prefer CSP and trusted script loading.
✓ Deprecated · Non-standard
Document beforescriptexecute
Do not build features on this event. Use it only to understand or migrate legacy Firefox code.
LegacyNot for new apps
Google ChromeNever implemented
Unavailable
Mozilla FirefoxLegacy only · removed / disabled in modern versions
Avoid
Apple SafariNever implemented
Unavailable
Microsoft EdgeNever implemented (Chromium)
Unavailable
OperaNever implemented (Chromium)
Unavailable
Internet ExplorerNo beforescriptexecute support
Unavailable
beforescriptexecuteDeprecated
Bottom line: Feature-detect if you must touch legacy Gecko code. For new work, use CSP and trusted script loading. Remember: even historically, dynamically appended scripts did not fire this Document event.
Wrap Up
Conclusion
Document beforescriptexecute once told Firefox pages that a static script was about to run—and could cancel it. Today it is a deprecated, non-standard footnote: useful for reading old code, not for shipping new products.
Load only trusted scripts; use ES modules when possible
Feature-detect before touching legacy Gecko-only APIs
Remember the static-only MDN rule when reading old code
Plan migration off proprietary script-execute events
❌ Don’t
Build new features on beforescriptexecute
Assume Chrome, Safari, or Edge ever supported it
Expect it to fire for appendChild()-inserted scripts
Treat preventDefault() here as a portable security tool
Treat non-standard Gecko events as future-proof
Summary
Key Takeaways
Knowledge Unlocked
Five things to remember about Document beforescriptexecute
Legacy Gecko “static script about to run” gate — prefer CSP now.
5
Core concepts
📄01
Before static script
legacy gate
Event
⚠️02
Deprecated
avoid new use
Status
🚫03
Non-standard
Gecko only
Compat
🚫04
Cancelable*
*legacy only
API
🚀05
Replace
use CSP
Modern
❓ Frequently Asked Questions
It is a proprietary Gecko (Firefox) Document event that fired when a static <script> element was about to start executing. MDN marks it Deprecated and Non-standard. It was never a finished web standard.
No. Per MDN, it does not fire when a script element is added dynamically (for example with appendChild()). It only applied to static scripts already in the document markup in supporting legacy Firefox.
Yes, in legacy Gecko. Calling preventDefault() on beforescriptexecute could stop that static script from running. The sibling afterscriptexecute was not cancelable — it only notified after the script had already run.
No. Prefer Content Security Policy (CSP), avoid injecting untrusted scripts, ES modules with known imports, or careful DOM insertion. Do not rely on this Gecko-only cancel hook.
Historically only Firefox (Gecko). Other major browsers never shipped it. Modern Firefox stopped dispatching it to web content and later removed the implementation.
In supporting legacy Firefox: document.addEventListener("beforescriptexecute", handler) or document.onbeforescriptexecute = handler. Always feature-detect; do not assume the event exists.
Did you know?
Mozilla later unshipped beforescriptexecute and afterscriptexecute from web content because they were non-standard and unused by other browsers. Script blockers that once relied on them moved to portable techniques such as CSP or changing a script’s type via MutationObserver.