Skip to content

Commit 09e4234

Browse files
committed
Make DebugKit toolbar robust with JS frameworks like HTMX, Boost
- Persist toolbar iframe across body by placing it in a shadowRoot element instead of <body> - Streamline injection logic for efficiency and reliability - Handle potential error by logging it to the console
1 parent 976cffe commit 09e4234

File tree

1 file changed

+41
-26
lines changed

1 file changed

+41
-26
lines changed

webroot/js/inject-iframe.js

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,40 +7,46 @@ if (elem) {
77

88
((win, doc) => {
99
let iframe;
10-
let bodyOverflow;
10+
let shadowHost;
11+
let shadowRoot;
1112

1213
const onMessage = (event) => {
14+
if (!iframe) return;
1315
if (event.data === 'collapse') {
1416
iframe.height = 40;
1517
iframe.width = 40;
16-
doc.body.style.overflow = bodyOverflow;
1718
return;
1819
}
1920
if (event.data === 'toolbar') {
2021
iframe.height = 40;
2122
iframe.width = '100%';
22-
doc.body.style.overflow = bodyOverflow;
2323
return;
2424
}
2525
if (event.data === 'expand') {
2626
iframe.width = '100%';
2727
iframe.height = '100%';
28-
doc.body.style.overflow = 'hidden';
2928
}
3029
if (event.data === 'error') {
3130
iframe.width = '40%';
3231
iframe.height = '40%';
33-
doc.body.style.overflow = bodyOverflow;
3432
}
3533
};
3634

37-
const onReady = () => {
35+
function injectDebugKitIframe() {
3836
if (!win.debugKitId) {
3937
return;
4038
}
41-
const { body } = doc;
4239

43-
// Cannot use css text, because of CSP compatibility.
40+
if (!shadowHost) {
41+
shadowHost = doc.getElementById('debugkit-shadow-host');
42+
if (!shadowHost) {
43+
shadowHost = doc.createElement('div');
44+
shadowHost.id = 'debugkit-shadow-host';
45+
doc.documentElement.appendChild(shadowHost);
46+
}
47+
shadowRoot = shadowHost.shadowRoot || shadowHost.attachShadow({ mode: 'open' });
48+
}
49+
4450
iframe = doc.createElement('iframe');
4551
iframe.style.position = 'fixed';
4652
iframe.style.bottom = 0;
@@ -52,12 +58,10 @@ if (elem) {
5258
iframe.height = 40;
5359
iframe.width = 40;
5460
iframe.src = `${window.debugKitBaseUrl}debug-kit/toolbar/${window.debugKitId}`;
55-
56-
body.appendChild(iframe);
57-
bodyOverflow = body.style.overflow;
61+
shadowRoot.appendChild(iframe);
5862

5963
window.addEventListener('message', onMessage, false);
60-
};
64+
}
6165

6266
const logAjaxRequest = (original) => function ajaxRequest() {
6367
if (this.readyState === 4 && this.getResponseHeader('X-DEBUGKIT-ID')) {
@@ -69,7 +73,13 @@ if (elem) {
6973
url: this._arguments && this._arguments[1],
7074
type: this.getResponseHeader('Content-Type'),
7175
};
72-
iframe.contentWindow.postMessage(`ajax-completed$$${JSON.stringify(params)}`, window.location.origin);
76+
77+
if (iframe && iframe.contentWindow) {
78+
iframe.contentWindow.postMessage(`ajax-completed$$${JSON.stringify(params)}`, window.location.origin);
79+
}
80+
else {
81+
console.warn('DebugKit iframe not found.');
82+
}
7383
}
7484
if (original) {
7585
return original.apply(this, [].slice.call(arguments));
@@ -93,21 +103,26 @@ if (elem) {
93103
};
94104
};
95105

96-
// Bind on ready callbacks to DOMContentLoaded (native js)
97-
// Since the body is already loaded (DOMContentLoaded), the event is not triggered.
98-
if (doc.addEventListener) {
99-
// This ensures that all event listeners get applied only once.
100-
if (!win.debugKitListenersApplied) {
101-
// Add support for turbo DOMContentLoaded alternative
102-
// see https://turbo.hotwired.dev/reference/events#turbo%3Aload
103-
const loadedEvent = typeof Turbo !== 'undefined' && Turbo !== null ? 'turbo:load' : 'DOMContentLoaded';
104-
doc.addEventListener(loadedEvent, onReady, false);
105-
doc.addEventListener(loadedEvent, proxyAjaxOpen, false);
106-
doc.addEventListener(loadedEvent, proxyAjaxSend, false);
107-
win.debugKitListenersApplied = true;
106+
let listenersSetup = false;
107+
function setupDebugKitListeners() {
108+
if (listenersSetup) return;
109+
// Set up AJAX proxying
110+
proxyAjaxOpen();
111+
proxyAjaxSend();
112+
113+
// Set up ready handler
114+
if (doc.readyState === 'loading') {
115+
doc.addEventListener('DOMContentLoaded', injectDebugKitIframe, false);
116+
} else {
117+
injectDebugKitIframe();
108118
}
119+
listenersSetup = true;
120+
}
121+
122+
if (doc.addEventListener) {
123+
setupDebugKitListeners();
109124
} else {
110125
throw new Error('Unable to add event listener for DebugKit. Please use a browser'
111-
+ ' that supports addEventListener().');
126+
+ ' that supports addEventListener().');
112127
}
113128
})(window, document);

0 commit comments

Comments
 (0)