The technique can be useful in dashboards, CRM interfaces, internal tools, portals, and other web applications where you want to provide additional interactive content without changing the main page layout.
The basic idea is simple:
Find a specific grid or control using a
data-control-nameattribute.Wait until the element becomes available in the DOM.
Dynamically create a popup.
Add an iframe containing external content.
Display the popup when the mouse enters the grid.
Hide it when the mouse leaves the grid and the popup is no longer being hovered.
For example, our target element can be identified using:
<div data-control-name="casetypecode">
...
</div>
The JavaScript function can then be called with:
disableAllGrids("casetypecode");
Waiting for the Grid to Become Available
One of the first challenges is that the target element may not exist immediately when the JavaScript code runs.
This is particularly common in applications that dynamically generate their interface.
The script solves this by using setInterval():
const interval = setInterval(function () {
const parentDoc = window.parent.document;
const grid = parentDoc.querySelector(
'[data-control-name="' + dataControlName + '"]'
);
console.log("Looking for:", dataControlName);
console.log("Grid:", grid);
if (!grid) {
return;
}
clearInterval(interval);
// Continue with the setup...
}, 500);
Every 500 milliseconds, the script checks whether the requested element exists.
Once it finds the element, the interval is stopped with:
clearInterval(interval);
This prevents unnecessary DOM queries from continuing indefinitely.
Accessing the Parent Document
The script uses:
const parentDoc = window.parent.document;
This allows the code to work with the document containing the iframe rather than only the current document.
This technique requires the current page and its parent document to satisfy the browser's same-origin security rules. If they belong to different origins, browser security restrictions can prevent access to window.parent.document.
Creating the Popup Dynamically
Instead of requiring an existing popup in the HTML, the script creates one dynamically:
const popup = parentDoc.createElement("div");
We then configure its appearance using JavaScript:
popup.style.position = "fixed";
popup.style.width = "70vw";
popup.style.height = "70vh";
popup.style.backgroundColor = "#fff";
popup.style.border = "1px solid #ccc";
popup.style.borderRadius = "8px";
popup.style.boxShadow = "0 5px 20px rgba(0,0,0,.3)";
popup.style.zIndex = "999999";
popup.style.display = "none";
popup.style.overflow = "hidden";
A few properties are particularly important.
position: fixed
The popup is positioned relative to the browser viewport rather than the normal document flow.
z-index
popup.style.zIndex = "999999";
popup.style.zIndex = "999999";
A high z-index helps ensure that the popup appears above other interface elements.
display: none
Initially, the popup is hidden:
popup.style.display = "none";
It becomes visible only when the user hovers over the target element.
Embedding External Content with an iframe
The next step is to create an iframe:
const iframe = parentDoc.createElement("iframe");
The iframe can then load the external game or application:
iframe.src = "YOUR_EXTERNAL_URL";
The iframe is configured to occupy the entire popup:
iframe.style.width = "100%";
iframe.style.height = "100%";
iframe.style.border = "0";
Finally, it is added to the popup:
popup.appendChild(iframe);
And the popup itself is added to the page:
parentDoc.body.appendChild(popup);
At this point, the popup exists in the DOM but remains invisible.
Showing the Popup on Mouse Hover
The popup is displayed when the user enters the grid:
grid.addEventListener("mouseenter", function () {
const rect = grid.getBoundingClientRect();
popup.style.left = rect.left + "px";
popup.style.top = (rect.bottom + 10) + "px";
popup.style.display = "block";
});
The key part is:
grid.getBoundingClientRect();
This returns the position and dimensions of the target element.
The script uses those coordinates to position the popup underneath the grid:
popup.style.left = rect.left + "px";
popup.style.top = (rect.bottom + 10) + "px";
The + 10 adds a small 10-pixel gap between the grid and the popup.
Hiding the Popup
When the mouse leaves the grid, we don't immediately hide the popup.
Instead, the script waits 100 milliseconds:
grid.addEventListener("mouseleave", function () {
setTimeout(function () {
if (!popup.matches(":hover")) {
popup.style.display = "none";
}
}, 100);
});
This small delay is important.
Without it, moving the mouse from the grid toward the popup could cause the popup to disappear before the cursor reaches it.
The script checks:
popup.matches(":hover")
If the popup is not being hovered, it is hidden.
Closing the Popup When the Cursor Leaves It
There is also a second event handler:
popup.addEventListener("mouseleave", function () {
popup.style.display = "none";
});
This ensures that the popup disappears as soon as the user moves the cursor away from it.
Together, the two mouse events create a simple hover interaction:
Target Grid
│
mouse enters
↓
┌───────────┐
│ Popup │
│ │
│ iframe │
│ │
└───────────┘
│
mouse leaves
↓
Hidden
The Complete Concept
The complete function follows this sequence:
function disableAllGrids(dataControlName) {
const interval = setInterval(function () {
const parentDoc = window.parent.document;
const grid = parentDoc.querySelector(
'[data-control-name="' + dataControlName + '"]'
);
if (!grid) {
return;
}
clearInterval(interval);
const popup = parentDoc.createElement("div");
popup.style.position = "fixed";
popup.style.width = "70vw";
popup.style.height = "70vh";
popup.style.backgroundColor = "#fff";
popup.style.border = "1px solid #ccc";
popup.style.borderRadius = "8px";
popup.style.boxShadow = "0 5px 20px rgba(0,0,0,.3)";
popup.style.zIndex = "999999";
popup.style.display = "none";
popup.style.overflow = "hidden";
const iframe = parentDoc.createElement("iframe");
iframe.src = "YOUR_EXTERNAL_URL";
iframe.style.width = "100%";
iframe.style.height = "100%";
iframe.style.border = "0";
popup.appendChild(iframe);
parentDoc.body.appendChild(popup);
grid.addEventListener("mouseenter", function () {
const rect = grid.getBoundingClientRect();
popup.style.left = rect.left + "px";
popup.style.top = (rect.bottom + 10) + "px";
popup.style.display = "block";
});
grid.addEventListener("mouseleave", function () {
setTimeout(function () {
if (!popup.matches(":hover")) {
popup.style.display = "none";
}
}, 100);
});
popup.addEventListener("mouseleave", function () {
popup.style.display = "none";
});
}, 500);
}
disableAllGrids("casetypecode");
Why Use a Function Parameter?
The function accepts:
dataControlName
This makes the solution reusable.
Instead of hard-coding:
'[data-control-name="casetypecode"]'
the script dynamically creates the selector:
'[data-control-name="' + dataControlName + '"]'
Therefore, you can target different controls simply by changing the function argument:
disableAllGrids("casetypecode");
or:
disableAllGrids("anotherControl");
This is much more flexible than creating a separate function for every grid.
Possible Improvements
Although the basic implementation works, there are several ways to make it more robust.
Use CSS Instead of Inline Styles
For larger projects, it is often cleaner to create a CSS class rather than setting every property with JavaScript.
For example:
.game-popup {
position: fixed;
width: 70vw;
height: 70vh;
background: #fff;
border: 1px solid #ccc;
border-radius: 8px;
box-shadow: 0 5px 20px rgba(0, 0, 0, .3);
z-index: 999999;
overflow: hidden;
}
JavaScript can then simply use:
popup.className = "game-popup";
This makes the JavaScript easier to read and allows designers to modify the appearance without touching the code.
Add a Maximum Width and Height
Using:
width: 70vw;
height: 70vh;
works well for many screens, but the popup can become very large on high-resolution displays.
You could add limits such as:
max-width: 1200px;
max-height: 800px;
This can improve usability on larger monitors.
Consider Mobile Devices
Mouse hover does not exist in the same way on touch devices.
For mobile interfaces, a click or tap-based interaction is usually more appropriate.
For example, you could use:
grid.addEventListener("click", function () {
// Open popup
});
and provide a dedicated close button.
Prevent Duplicate Popups
If the function is called multiple times for the same control, multiple event listeners and popups could potentially be created.
A production implementation should therefore consider storing a reference to the created popup or marking the grid as already initialized.
For example:
if (grid.dataset.popupInitialized === "true") {
return;
}
grid.dataset.popupInitialized = "true";
This prevents the same element from being initialized more than once.
Browser Security Considerations
When embedding external content, browser security policies can affect whether the iframe is allowed to load.
The external website may use headers such as X-Frame-Options or Content Security Policy (frame-ancestors) to prevent embedding.
In addition, accessing:
window.parent.document
is subject to the browser's same-origin policy.
Therefore, this technique should always be tested in the actual environment where it will be deployed.
A dynamically generated hover popup is a simple but powerful way to add interactive content to an existing web interface.
The approach demonstrated here combines several useful JavaScript techniques:
DOM element detection
Dynamic element creation
iframe embedding
mouse event handling
viewport-based positioning
delayed hiding
reusable functions
interaction with a parent document