Iframes projected to slots. Iframes won't reload.
This fiddle demonstrates how when the target of a projection of an iframe is moved around, the iframe itself doesn't reload.
by David Iglesias
January 08, 2021
HTML
<div id='rotate'>
<button data-direction='down'>Iframe ⬆️</button>
<button data-direction='up'>Iframe ⬇️</button>
</div>
<div id='target'>
</div>
<p>
The iframe is now rendered in a stable position in the DOM, and instead it's displayed through a <tt>slot</tt> tag. The <tt>slot</tt> is inside the <tt>shadowDOM</tt> of the target element, and the iframe is a sibling of the shadow root. The slot can be moved around, and <strong>the iframe won't reload.</strong>
</p>
CSS
/* Nothing on this stylesheet matters, this is just eye candy. */
* { font-family: sans-serif; box-sizing: border-box; }
tt { font-family: monospace; background-color: #eee; }
.filler {
background: #fabada;
border: 1px solid black;
text-align: center;
}
iframe {
width: 100%;
height: 200px;
border: 1px solid black;
margin: 0px;
}
#rotate {
margin: 10px 0px;
text-align: center;
}
JavaScript
const targetDiv = document.querySelector('#target');
const target = targetDiv.attachShadow({mode: 'open'});
// Clone the CSS from the outside...
const css = document.querySelector('#compiled-css').cloneNode(true);
target.appendChild(css);
// Inject some filler divs...
for(let i=0; i < 3; i++) {
const div = document.createElement('div');
div.className = 'filler';
div.innerHTML = i;
target.appendChild(div);
}
// Inject a slot...
const slot = document.createElement('slot');
slot.name = 'iframe-target';
target.appendChild(slot);
// Inject an iframe...
const iframe = document.createElement('iframe');
iframe.src = 'https://www.flutter.dev/';
iframe.slot = 'iframe-target';
targetDiv.appendChild(iframe);
const rotate = document.querySelector('#rotate');
rotate.addEventListener('click', (event) => {
// Don't move the css tag around :)
const children = [...target.children].filter((el) => el !== css);
const firstChild = children[0];
const direction = event.target.dataset.direction;
if (direction === 'up') {
// Move Last to First
const lastChild = children[children.length-1];
target.insertBefore(lastChild, firstChild);
} else if (direction === 'down') {
// Move First to Last
target.appendChild(firstChild);
}
});