ππΈοΈ
7/16/2025 9:41AM
New digital garden theme :)
Start menu here...
Thanks for visiting my website! Or as I like to call it, my web desktop. This is a space that I play around with web technologies, share my thoughts and my half-baked projects. Feel free to explore by clicking around.
Want to follow along? You are going to need some basic familiarity with JavaScript, html, css, you will need a code editor and have the files served from a webserver. Do to iframe restrictions, this won’t work locally.
If you recall from my introduction post, framed websites have three main issues:
So how can we bypass some of those issues? Let’s tackle the first one. We want to make sure that a “fragment” page isn’t accidentally indexed by search engines. That’s simply enough. you can include a robots no-follow directive <meta name="robots" content="noindex, nofollow" />
in the the head
section of the page. Like this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex, nofollow" />
</head>
<body>
</body>
</html>
We also want to make sure that the website fragment isn’t directly linked to in the website. Some of that comes down to the website creator being careful to not to link to it directly. But if JavaScript is allowed we can redirect the user back to the main website in case it does happen.
So what we want is to immediately invoke a function expression, IIFE, that checks if the fragment page is running in an iframe, the method we are going to use to embed it in another page, and if it is not, redirect the user. So how can we tell if it’s in an iframe? We can use the window object. We can check to see what the top window is, and if it’s the iframe we should bail i.e. window.self === window.top
and redirect the user. We can put this right in the head section of the document so that the redirect happens as fast as possible.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex, nofollow" />
<script>
;(function () {
if(window.self === window.top) {
window.location.replace("/");
}
})();
</script>
</head>
<body>
</body>
</html>
So now we’ve mostly taken care of the first point and as long as the website creator is careful to not link to frame sources directly we should be in a pretty good spot. But what if the user doesn’t have JavaScript enabled? We should give them a link to the main website page. Luckily there is an HTML5 tag for that <noscript>
that only shows when JavaScript is disabled.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex, nofollow" />
<script>
;(function () {
if(window.self === window.top) {
window.location.replace("/");
}
})();
</script>
</head>
<body>
<noscript>Lost? Go to <a href="/">home page</a></noscript>
</body>
</html>
Now to make things a little less busy in our fragment page, let’s put the script in it’s own file, I’m calling mine fragmentRedirect.js
. With a little bit of dummy data we have a website fragment. Let’s call it _pets.html
. The underscore is just a naming notation I am following to tell me at a glance that this is not a standalone html page, but a fragment of one meant to be embedded.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex, nofollow" />
<script src="fragmentRedirect.js" type="text/javascript"></script>
</head>
<body>
<noscript>Lost? Go to <a href="/">home page</a></noscript>
<p>Common pets:</p>
<ul>
<li>Cats</li>
<li>Dogs</li>
<li>Fish</li>
</ul>
</body>
</html>
What if we could take the body of the iframe and replace itself in the parent document? Then there would be no frame to get in the way of seo and accessibility. This is something we can do, but we’re going to need JavaScript to do it.
For security reasons this will only work if your iframe sources are on the same website domain. Why? CORS. So no using this trick to get third-party content π
I’m assuming you can set up a basic index.html
page to be the root of the website. This is my starting page with a catchy title. The <sup>
tag, is the superscript tag, which makes the asterisk small and aligned to the top of the line. One of the easiest ways to help with accessibility is to use the semantic HTML5 tags. With the added bonus that they make the HTML easier to read.
<!DOCTYPE html>
<html lang="en">
<head>
<title>A framed* website</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<main>
<header><h2>A framed<sup>*</sup> website</h2></header>
</main>
</body>
</html>
We should put in a bit of styling. I prefer small classless css libraries so I added in Simple.css. You can download it and then host it, my preference as style.css
, or link to the CDN. Let’s also embed the website fragment we made above. I have it in a file called _pets.html
. Notice the iframe has a title
, that’s for accessibility. It lets a screen reader know what it can expect to find in your iframe.
<!DOCTYPE html>
<html lang="en">
<head>
<title>A framed* website</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/style.css" />
</head>
<body>
<main>
<header><h2>A framed<sup>*</sup> website</h2></header>
<iframe
src="/_pets.html"
title="a list of common pets"></iframe>
</main>
</body>
</html>
Let’s take a look:
Not too bad. We can see our list of common pets on the page. You can see the content even if JavaScript is off, which is excellent. But it’s still in an iframe.
We’re going to need to add some JavaScript for this next part, making it disappear. And I don’t mean deleting it. I mean taking the content of the iframe, appending it into our parent page, and then deleting the iframe. Here’s a screen shot of what I mean:
Now the iframe tag has an onload attribute that you can use to execute some JavaScript when it is done loading. Which is exactly what we want. I avoided the addEventListener method because there is a chance that by the time that even listener gets added, the iframe content is already loaded. I got the idea to make the iframe disappear from this blog post HTML Includes That Work Today | Filament Group, Inc. and tweaked their method.
<!DOCTYPE html>
<html lang="en"><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>A framed* website</title>
<link rel="stylesheet" href="./style.css" />
<script>
function disappear(frame) {
let body = (frame.contentDocument.body || frame.contentDocument);
let children = [...body.children];
for(let child of children) {
frame.before(child);
}
frame.remove();
}
</script>
</head>
<body>
<main>
<header>
<h1>A framed<sup>*</sup> website</h1>
</header>
<iframe
src="/_pets.html"
title="a list of common pets"
onload="disappear(this)"></iframe>
</main>
</body>
</html>
The disappear
function first tries to find the body
element of the iframe once it’s loaded and from that the children elements. The children nodes are a live list that reflect the changes in the DOM. Which means, to be safe, we should copy this into an array first. Then we iterate over the children and into the DOM of the parent page using frame.before()
. The final step once everything is copied over is to remove itself from the DOM with frame.remove()
. Now there a quite a few improvements we can make to that function and some needed error checking, but for right now it gets the job done.
What about SEO and what about accessibility? The biggest issue with both is that they are not aware of dynamically loaded content. Though in 2023 the big search engines do crawl sites loaded with content via JavaScript. I couldn’t find any concrete sources, but my searching seemed to indicate that as long as the content updated dynamically happens quickly then there shouldn’t be a problem indexing it. But just in case, leave the content rich portions of your website out of the framed fragments.
Now onto accessibility. A lot of accessibility issues come from the markup of the HTML and leaving important attributes off of tags. People seldom fill out the alt
tags on images or if they do, then not in a meaningful way. Using semantic html tags also helps with accessibility There are also aria-
attributes that can be added to tags to further increase accessibility.
The biggest accessibility drawback to this disappearing iframe approach is that there is no notification, that say a screen reader detect, to the change when the iframe content is pulled in and the iframe deleted. So we’re going to need to tell it. There is an aria-live
tag that let’s screen readers and assistive technology know that a region of the page is dynamic and an aria-busy
attribute which helps indicate what is being actively modified. The aria-live
tag isn’t something you want to add in dynamically, so I put it on a section
tag around the iframe that I marked with the aria-busy
attribute. You may of noticed I set aria-live="off"
. This simply means the screen reader won’t interrupt the user with the update unless they are focused on that element.
The JavaScript for the frames I moved into a file called disappearingFrames.js
and I linked it in the head of the html document. Here is the final result of the index.html page:
<!DOCTYPE html>
<html lang="en">
<head>
<title>A framed* website</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/style.css" />
<script src="disappearingFrames.js" type="text/javascript"></script>
</head>
<body>
<main>
<header><h2>A framed<sup>*</sup> website</h2></header>
<section aria-live="off">
<iframe aria-busy
src="/_pets.html"
title="a list of common pets"
onload="disappear(this)"></iframe>
</section>
</main>
</body>
</html>
So now we have a small proof of concept that has a modern approach on using iframes in a framed website context. But to make sure this really works, we need to build something a bit more substantial. So next up, let’s put together a basic website with this approach.
I have a lot of interests and this place is a catch all. If you prefer to only follow a couple topics then check out my feeds page for category specific RSS feeds.