JS pane: Run-on-demand scripts, no auto-run on every visit
A JustZix JS rule runs automatically when the URL matches. Great for things that should always work — auto-skip cookie banners, custom shortcuts. But some scripts are destructive: "empty cart", "delete email draft", "reset form". You don't want those on auto-run. You want to click ▶ only when you want to. That's a JS pane (v2.13.56+).
Three JS windows — which for what
| Window type | When it runs | Use case |
|---|---|---|
| JS rule | Auto on every matching URL load | Auto-skip cookies, custom shortcuts, GTM logger |
| JS Console | Every Ctrl+Enter = new eval | Ad-hoc REPL, one-off checks |
| JS pane | Only on Ctrl+Enter / click ▶ | Persistent code, destructive actions, bulk ops |
A JS pane looks almost like a CSS pane (textarea, draggable, coloured dot), but has an extra ▶ Run button in the header. Code lives in the textarea, never auto-executes — the one exception is resume after reload (if there was something in sessionStorage before F5, JustZix runs it again to restore pre-refresh state).
First use
- JustZix options → folder/group/rule → "Windows" → "+ JS pane".
- Name: "Empty cart", colour: amber (default #D65D0E).
- Visit
shop.com/cart. The pane appears top-right with a header ("• Empty cart [▶]") and an empty textarea. - Type:
document.querySelectorAll('.cart-item .remove-btn') .forEach(btn => btn.click()); - Ctrl+Enter (or click ▶). All "Remove" buttons clicked.
Dirty state — you visually know there's a change
After typing / editing code, until you run it (or revert to the last-run version), the Run button shows a "dirty" state (with a custom runColor — from v2.13.64 you can pick your own). It's a visual cue: "you have unrun changes".
Click ▶ → code runs, dirty state clears. Revert the textarea to an earlier value → dirty appears again. JustZix compares current content with el.dataset.jzLastRun.
Error overlay — you don't need DevTools open
When eval throws an exception, a red bar appears at the bottom of the pane (.jz-pane-error):
JS error: Cannot read properties of null (reading 'click')
Click ▶ again with fixed code → the error clears. No need to flap F12 open. For async errors (setTimeout(...) throwing) the overlay won't catch them — known limitation, async errors must be tracked via DevTools or JustZix JS Console.
Persistent vs ephemeral — which scenario
JS pane content persists in sessionStorage['jz_pane_{id}_content'] per tab. Consequences:
- F5 / in-tab navigation — pane re-mounts, textarea reads back the code, auto-resume runs it again. So page state after reload = state after first run.
- New tab on the same domain — pane appears empty. Each tab has its own sessionStorage.
- Close the tab — pane content is gone. The pane definition itself (name, colour, scope) stays because that's chrome.storage, but the textarea returns to empty.
If you want code permanently (even after closing the tab) — that's not a pane, that's a JS rule. Pane = scratchpad with a Run button.
Use case 1 — state-specific destructive actions
Cleanup in a shop admin panel. Action "Clear all demo products from cart":
// Only runs when you click ▶. Auto-run = disaster.
if (!location.href.includes('/admin/demo')) {
throw new Error('Only for /admin/demo');
}
const rows = document.querySelectorAll('tr.product');
console.log(`Removing ${rows.length} products...`);
for (const row of rows) {
await fetch('/api/products/' + row.dataset.id, { method: 'DELETE' });
row.remove();
}
console.log('Done.');
Defence: URL guard + pane name "CLEANUP DEMO" + amber dot. Hard to do accidentally.
Use case 2 — bulk operations
50 users have to be marked "verified". UI allows click-per-user. JS pane:
const rows = document.querySelectorAll('.user-row:not(.verified)');
let count = 0;
for (const row of rows) {
row.querySelector('.btn-verify')?.click();
await new Promise(r => setTimeout(r, 200)); // throttle so the API doesn't yell
count++;
}
console.log(`Verified ${count} users.`);
One click ▶, 50 users. 200 ms sleep so we don't flood the API. Three times faster than the real bulk endpoint that doesn't exist.
Use case 3 — scripted demo
You're showing a client the "add 3 products + go to checkout + fill test data" flow. 30 seconds each time. A pane script does it all in 2 seconds:
// Demo flow
[1, 2, 3].forEach(i => document.querySelector(`[data-product-id="${i}"] .add-btn`)?.click());
await new Promise(r => setTimeout(r, 500));
document.querySelector('.checkout-btn').click();
await new Promise(r => setTimeout(r, 1000));
Object.entries({
email: 'demo@example.com', name: 'Demo', address: 'Test 1'
}).forEach(([k, v]) => {
const el = document.querySelector(`[name="${k}"]`);
if (el) { el.value = v; el.dispatchEvent(new Event('input', { bubbles: true })); }
});
The client sees an "auto-pilot" flow. Professional. Without typing in real time.
Pitfalls
- Async errors don't show in the overlay —
setTimeout(() => { throw new Error('x') }, 100)ends up in global console, not the pane error box. Workaround: wrap try/catch inside async functions. - Resume after reload can surprise you — if you typed something destructive yesterday, today's reload re-runs it. Best practice: panes you don't want auto-resumed → clear contents before closing the tab (Ctrl+A, Delete, ▶ on empty = clears
jzLastRun). - Top-level await does NOT work. Wrap in an IIFE:
(async () => { await ... })(). - MAIN world access — JS pane runs in page context (via
chrome.scripting.executeScriptworld=MAIN), so it sees page globals (React, Redux store, jQuery, etc.). Same as JS rules.
What's next
JS pane is the 2nd "window on the frontend" type in JustZix. We've previously written about CSS pane (live CSS editor) and JS Console (REPL). The three together are a mini-IDE right inside your browser tab, scoped per domain.
Install JustZix and get Run-on-demand scripts on every page, without the auto-run risk.
Rate this post
No ratings yet — be the first.