We are experiencing memory leaks in a web application where we dynamically update elements with new HTML content using the srcdoc property. This issue becomes apparent after extended usage, especially when the content within the s changes frequently. The problem is worse with the highest diversity of html content. If the diversity of content is too high, it will consistently max out memory usage.
Our application uses two elements per container, switching between them to ensure a smooth transition. One is updated with new content using srcdoc, and once loaded, we switch its visibility with the currently active . The previously active is then cleared and hidden.
However, over time, this approach seems to cause a memory leak (e.g., Google Chrome, Edge, Opera). The memory usage keeps increasing, even after the unused is cleared. Firefox does not appear to suffer from the memory leak, however, video in the htmls seem to gradually suffer from slower playback.
Questions:
Here’s the logic for updating the srcdoc property and handling the onload event:
if (received_data.html) {
var inactiveIframe = document.getElementById(iframeIds.inactiveIframe);
// Update the srcdoc of the inactive iframe, which triggers content loading
inactiveIframe.srcdoc = received_data.html;
// Handle the iframe size if provided
if (received_data.width && received_data.height) {
inactiveIframe.style.width = received_data.width + "px";
inactiveIframe.style.height = received_data.height + "px";
inactiveIframe.parentNode.style.height = received_data.height + "px";
inactiveIframe.parentNode.parentNode.style.height = received_data.height + "px";
}
// Set up the onload event handler to switch iframes after the new content has loaded
inactiveIframe.onload = async function() {
await switchIframe(iframeIds);
// Optionally, remove the event listener after the switch
inactiveIframe.onload = null;
};
validateResources(received_data.html);
}
The switchIframe function is responsible for swapping the active and inactive iframes:
function switchIframe(iframeIds) {
// Swap the active and inactive iframe IDs
let temp = iframeIds.activeIframe;
iframeIds.activeIframe = iframeIds.inactiveIframe;
iframeIds.inactiveIframe = temp;
// Hide the previously active iframe
const inactiveIframeElement = document.getElementById(iframeIds.inactiveIframe);
inactiveIframeElement.style.zIndex = '999997';
inactiveIframeElement.style.opacity = '0';
// Clear the content of the inactive iframe
inactiveIframeElement.srcdoc = '';
let frameDoc = inactiveIframeElement.contentDocument || inactiveIframeElement.contentWindow.document;
if (frameDoc) {
frameDoc.documentElement.innerHTML = '';
}
// Remove event listeners
inactiveIframeElement.onload = null;
// Show the new active iframe
const activeIframeElement = document.getElementById(iframeIds.activeIframe);
activeIframeElement.style.zIndex = '999998';
activeIframeElement.style.opacity = '1';
}
Result: The memory usage maxes out over time.
We’ve tried different strategies to avoid memory leaks, but none were successful:
const inactiveIframeElement = document.getElementById(iframeIds.inactiveIframe);
inactiveIframeElement.srcdoc = '';
inactiveIframeElement.parentElement.innerHTML = `
<iframe id="${iframeIds.inactiveIframe}" class="iframe-class" scrolling="no" srcdoc=""></iframe>`;
Result: The memory usage maxes out over time.
let frameDoc = inactiveIframeElement.contentDocument || inactiveIframeElement.contentWindow.document;
if (frameDoc) {
frameDoc.documentElement.innerHTML = '';
}
Result: The memory usage maxes out over time.
We are experiencing memory leaks in a web application where we dynamically update elements with new HTML content using the srcdoc property. This issue becomes apparent after extended usage, especially when the content within the s changes frequently. The problem is worse with the highest diversity of html content. If the diversity of content is too high, it will consistently max out memory usage.
Our application uses two elements per container, switching between them to ensure a smooth transition. One is updated with new content using srcdoc, and once loaded, we switch its visibility with the currently active . The previously active is then cleared and hidden.
However, over time, this approach seems to cause a memory leak (e.g., Google Chrome, Edge, Opera). The memory usage keeps increasing, even after the unused is cleared. Firefox does not appear to suffer from the memory leak, however, video in the htmls seem to gradually suffer from slower playback.
Questions:
Here’s the logic for updating the srcdoc property and handling the onload event:
if (received_data.html) {
var inactiveIframe = document.getElementById(iframeIds.inactiveIframe);
// Update the srcdoc of the inactive iframe, which triggers content loading
inactiveIframe.srcdoc = received_data.html;
// Handle the iframe size if provided
if (received_data.width && received_data.height) {
inactiveIframe.style.width = received_data.width + "px";
inactiveIframe.style.height = received_data.height + "px";
inactiveIframe.parentNode.style.height = received_data.height + "px";
inactiveIframe.parentNode.parentNode.style.height = received_data.height + "px";
}
// Set up the onload event handler to switch iframes after the new content has loaded
inactiveIframe.onload = async function() {
await switchIframe(iframeIds);
// Optionally, remove the event listener after the switch
inactiveIframe.onload = null;
};
validateResources(received_data.html);
}
The switchIframe function is responsible for swapping the active and inactive iframes:
function switchIframe(iframeIds) {
// Swap the active and inactive iframe IDs
let temp = iframeIds.activeIframe;
iframeIds.activeIframe = iframeIds.inactiveIframe;
iframeIds.inactiveIframe = temp;
// Hide the previously active iframe
const inactiveIframeElement = document.getElementById(iframeIds.inactiveIframe);
inactiveIframeElement.style.zIndex = '999997';
inactiveIframeElement.style.opacity = '0';
// Clear the content of the inactive iframe
inactiveIframeElement.srcdoc = '';
let frameDoc = inactiveIframeElement.contentDocument || inactiveIframeElement.contentWindow.document;
if (frameDoc) {
frameDoc.documentElement.innerHTML = '';
}
// Remove event listeners
inactiveIframeElement.onload = null;
// Show the new active iframe
const activeIframeElement = document.getElementById(iframeIds.activeIframe);
activeIframeElement.style.zIndex = '999998';
activeIframeElement.style.opacity = '1';
}
Result: The memory usage maxes out over time.
We’ve tried different strategies to avoid memory leaks, but none were successful:
const inactiveIframeElement = document.getElementById(iframeIds.inactiveIframe);
inactiveIframeElement.srcdoc = '';
inactiveIframeElement.parentElement.innerHTML = `
<iframe id="${iframeIds.inactiveIframe}" class="iframe-class" scrolling="no" srcdoc=""></iframe>`;
Result: The memory usage maxes out over time.
let frameDoc = inactiveIframeElement.contentDocument || inactiveIframeElement.contentWindow.document;
if (frameDoc) {
frameDoc.documentElement.innerHTML = '';
}
Result: The memory usage maxes out over time.
Although setting inactiveIframeElement.srcdoc = '';
should be effective, here are a couple of other methods you could try:
Setting the src
property to an empty page:
inactiveIframeElement.src = 'about:blank';
Or Opening, then Closing the document. Calling open()
clears the contents and provisions the contents for writing. Calling close()
then closes it again.
inactiveIframeElement.contentDocument.open();
inactiveIframeElement.contentDocument.close();
However, the issue may not be with the iFrame, itself. I notice this line in your example:
validateResources(received_data.html);
This tells me you are continuing to use received_data.html
after the code in question. What happens in validateResources
? Is it possibly doing something that is not allowing this value to be disposed? Is there any other code that may be hanging on to the received_data
object or this html
property? Have you tried explicitly setting received_data.html = null
after it is used to see if it corrects the issue?
await
the call toswitchIframe()
? The function is notasync
and it does not return a Promise. – Pointy Commented Jan 21 at 15:00inactiveIframe.onload = function() { switchIframe(iframeIds); // Optionally, remove the event listener after the switch inactiveIframe.onload = null; };
But this still suffered from the memory leak anyway – Joshua Rhode Commented Jan 21 at 15:04window
ordocument
objects risks browsers not releasing the global environment record(s) for the bindings of values declared usingconst
,let
and (probably)class
. Without trying to prove it applies in this case, how does replacing the inactiveiframe
element with one freshly created withdocument.createElement
affect the outcome? – traktor Commented Jan 21 at 21:17