Select Page

Rendering Elements After The HEAD Tag In JavaScript

Cyberdime
Published: February 20, 2023

The other day, when I was exploring the progress bar in Hotwire, I noticed that the <div> implementing the progress bar was injected into the DOM (Document Object Model) after the <head> tag and before the <body> tag. I didn’t know that it was possible to render elements outside of the Body tag. And, in fact, statically rendered elements outside of the Body will be “moved” into the Body (by the browser) as the HTML is parsed. However, it turns out that you can render elements between the Head and Body tags at runtime using JavaScript.

Run this demo in my JavaScript Demos project on GitHub.

View this code in my JavaScript Demos project on GitHub.

I assume that the Hotwire framework is making this choice because it swaps out the entire <body> tag when a new page is loaded. As such, any elements contained within the <body> tag will be implicitly removed. Which means, if the progress bar is outside the Body tag, the progress bar can be persisted even as the Body tag is being replaced.

That’s a cool use-case; but, when I saw this, my immediate thoughts went to stacking context. Stacking context is the key to understanding z-index layering. If you’ve ever seen a developer using a CSS property like:

z-index: 999999999 ;

… it’s because they don’t understand how z-index works; and, they’re just throwing numbers at the wall to see what sticks. It’s a futile attempt to break free from a “stacking context” that is locking-down layering within a branch of the DOM.

The main rendered branch of the DOM is the <body> element. Which means, if we can create a “stacking context” on the Body tag itself, anything outside of the Body, will be in a different stacking context and can freely stack above or below the Body layer.

To demonstrate this, I’ve created a page with two <div> elements using z-index: 2. They are both defined in between the Head and Body tags; however, one of them is dynamically injecting itself after the <head> using JavaScript:

<!doctype html>
<html lang="en">
<head>
	<meta charset="utf-8" />
	<meta name="viewport" content="width=device-width, initial-scale=1" />
	<link rel="stylesheet" type="text/css" href="https://www.bennadel.com/blog/./demo.css" />
	<style type="text/css">
		/**
		* Creating a STACKING CONTEXT around the body.
		*/
		body {
			position: relative ;
			z-index: 0 ;
		}
		/**
		* Creating a CONTROL object for layering. Using a z-index of "999999", which is
		* HIGHER than the other two elements (below). However, the stacking context on the
		* BODY element changes how things are visually stacked.
		*/
		.box {
			position: fixed ;
			z-index: 999999 ; /* <--------- HIGH z-index value. */
		}
		.static {
			border: 2px solid blue ;
			position: fixed ;
			z-index: 2 ; /* <--------- LOWER (than BOX) z-index value. */
		}
		.dynamic {
			border: 2px solid hotpink ;
			position: fixed ;
			z-index: 2 ; /* <--------- LOWER (than BOX) z-index value. */
		}
	</style>
</head>
<!-- !!!! AFTER HEAD ELEMENT. !!!! -->
<!-- !!!! AFTER HEAD ELEMENT. !!!! -->
<div class="static">
	I am <strong>STATICALLY defined</strong> to be after the Head element.
</div>
<div class="dynamic">
	I am <strong>DYNAMICALLY defined</strong> to be after the Head element.
	<script type="text/javascript">
		document.head.after( document.querySelector( ".dynamic" ) );
		// Remove the Script tag from the DOM (for funzies!).
		document.currentScript.remove();
	</script>
</div>
<!-- !!!! BEFORE BODY ELEMENT. !!!! -->
<!-- !!!! BEFORE BODY ELEMENT. !!!! -->
<body>
	<h1>
		Inserting Elements After The HEAD Tag In JavaScript
	</h1>
	<div class="box">
		<br />
	</div>
</body>
</html>

As you can see, the “box” element is our control layer and has a high z-index. Each of my other elements use a z-index of 2. Normally, this would place both elements below the box from a stacking perspective. However, when we run this page, we get the following output:

The dynamically injected element is stacked above the box while the static element is stacked below the box.

As you can see, the browser moved the statically defined element into the Body tag as it parsed the HTML, which is why it is layered below the “box”. However, the dynamically defined element (which uses JavaScript to inject itself after the Head tag), is allowed to remain after the <head>. And, because it is rendered outside of the <body> tag, it is stacked above the “box” despite having a much lower z-index.

Check out the license.

Source: www.bennadel.com