Adding an HTML wrapper
Intent
My intent behind this approach is to enable writing the core content of a page in a very simple HTML file structure but then embedding the page in a more complex HTML page.
Content HTML skeleton
The only tags absolutely required in a valid HTML document are the DOCTYPE and title tags. (See the specification for details.) In addition to those, the skeleton of the simple content file includes a script tag to actually perform the wrapping and an article tag containing all of the content to be rendered.
Note that the script tag is executed synchronously before the page processing continues. If the tag had either the async attribute or the defer attribute or if it had type=module, the Javascript loading would be put on the defer queue and could result in an FOUC (a Flash Of Unstyled Content).
<!DOCTYPE html>
<title>My page title</title>
<script src="main.js"></script>
<article>
...
</article>
A simple wrapper example
The following is a very simple HTML wrapper example. The name of the file is not important except that the name must be known to the Javascript file described in the next section. So, we will assume that the filename is "wrapper.html". We will see that name later in the Javascript file.
<div id=bodycontent>
<header><h1>My Header Text</h1></header>
<div id=contentcontainer></div>
<footer></footer>
</div>
Wrapping code in main.js
In main.js, we first modify the head of the HTML document.
The viewport meta tag helps with mobile compatibility. The below setting is a reasonable placeholder pending refinement for a specific site. For details on this tag, see the MDN viewport meta tag documentation.
The stylesheet link brings in the CSS code and assumes a "main.css" location. Setting blocking=render helps to prevent an FOUC (that is, a Flash Of Unstyled Content).
Setting the body visibility to hidden in the style tag also helps to prevent an FOUC. Later, after completing the wrapping logic in the wrapArticle function, we make the body visible.
document.head.innerHTML += `
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="main.css" blocking=render>
<style>
body { visibility: hidden; }
</style>`
The actual wrapping of the content with the wrapper is performed by the wrapArticle function in main.js. It starts by creating a document fragment where we then park the <article> of the content HTML file (using appendChild). We then fetch the wrapper HTML file and assign that to be the innerHTML of the current document's body. With wrapper HTML in place, we use appendChild to move the <article> that we parked in the document fragment into the "contentcontainer" div of the document.
After the wrapping is complete we can now display the body (setting visibility="visible") without FOUC.
async function wrapArticle() {
const docfrag = document.createDocumentFragment()
docfrag.appendChild(document.querySelector('body>article'))
const response = await fetch('wrapper.html')
const html = await response.text()
document.body.innerHTML = html
document.getElementById('contentcontainer').appendChild(docfrag)
document.body.style.visibility='visible'
}
In order to invoke the wrapArticle function we set up an event listener to execute that function after the DOM from the content HTML page is initially loaded into the current document.
window.addEventListener('DOMContentLoaded', async ev => {
await wrapArticle()
})