How to create a personal website, but it’s 1999
Have you ever wondered, what web developer life looked like over 20 years ago, when world wide web was still kinda new thing, you had to listen to strange beeps and boops before you entered it, JavaScript and CSS were more of a curiosity than something commonly used in creating simple websites, and the most popular browser was Internet Explorer? Well, probably you haven’t… but if you have couple of minutes to spare and you want to learn another useless thing — this article will not disappoint you!
Backstory
At the beginning of 2022 I fell into some nostalgic mood and really wanted to play one game from my childhood (The Prince and The Coward — it’s a polish point and click game and, yeah, I’m from Poland). I had original game CD, but I had no machine that could run it. I thought that it would be more fun to play it on old-school Windows PC, so I decided to go through auction/secondhand item websites/marketplaces yo buy some old, used hardware (and software — Windows 98) to build one for myself… anyway how I built it and what problems I had — let’s leave that for another story.
After completing the game, the nostalgic spirit faded away, leaving me with stuff that occupied space and gathered dust. I quickly came up with the idea to try with some web development on this PC, but there was always something better to do. So this idea spent some time aside until I finally made up my mind to write this article.
1999
I was 10 years old back then and had at least 3 years experience with the Microsoft Windows operating systems. I’m quite sure that my journey with web development started in early 2000s and I used Macromedia Dreamweaver (Adobe acquired Macromedia in 2005) for my first “projects” with no CSS, table-based layouts, and JS snippets copied from the internet to have some trendy effects like falling snow animation.
The internet itself was a mystery to me in 1999. I had probably heard about it and knew what it was all about, but I made my first contact in the 21st century.
What was happening in the world? European Union introduced new currency — Euro. Bill Clinton was still president of the USA. The World Meteorological Organization announced that the ’90s were the hottest (talking about average temperature) decade ever. SpongeBob SquarePants debuted on Nickelodeon. Eminem released The Slim Shady LP. Movies like The Matrix and Star Wars: Episode 1 — The Phantom Menace (I really loved this one back then, re-watched it recently and I think I still like it) had their world premieres.
Everyone was talking about the millennium bug, as the year 2000 was supposed to bring a global computer apocalypse due to date formatting issues. So, if you hadn’t done it already, 1999 was your last chance to create a personal website.
The Project
What we’re going to do? A simple personal website with three tabs — home (say hello to our visitors), about (short bio) and contact (some contact info). Layout shouldn’t be very complicated — header (with a title and tab navigation) at the top, footer at the bottom, and tab content area in between.
I would like to use as much CSS as possible, and the switching between tabs should be implemented using JavaScript. There will be no subpages — just one HTML index file, one JS file, one CSS file, and few (two) images. Here is the structure of my project tree:
project-root/
├── assets/
│ ├── scripts.js
│ ├── styles.css
| ├── bg.gif (used as background of the page)
| └── mk.jpg (my face for about section)
|
└── index.html
The page should work and look good on all (two) major browsers available at that time — MS Internet Explorer 5 and Netscape Navigator 4.51.
Tools
I could go classic and choose MS Notepad to write all code for my website. But let’s make it a bit more professional and get some real code editor. My first thought was Notepad++, but since it was released in 2003, it doesn’t exist in 1999. I had in my mind polish code editor called Pajączek (Spidey), available since 1996, but I think it was only in polish and not free. After a few minutes of searching on Google and Wikipedia, I found Arachnophilia — released in 1996, it’s free, it’s in english, and its name is related to spiders. This will do.
My Windows 98 came with preinstalled Internet Explorer (v5.0), but I had to get somewhere other apps — Netscape Navigator and Arachnophilia. Naturally, both had to be versions that were available 23 years ago. Looking for old software is not easy but usually oldversion.com is a good place to start. Unfortunately when I was trying to get there it was down for couple of days, and I was pretty sure it’s gone for good. However, as I type these words, it seems to be back online.
Nevertheless, I had to search elsewhere. It took me some time, cause phrase like “netscape 4.5 download” typed in google won’t give you satisfying results on the first page (it won’t get you any satisfying results at all), so I had to dig around. Eventually, the page archive.org gave me what I needed — apcmag.cd disk image from May 1999 which included both apps: Netscape Navigator v4.51 and Arachnophilia v3.9.
My initial ambitious plan involved using a local server and running my website on localhost (I even found an old article about installing Apache and PHP on Windows 98). However, for a project of this scale, it felt a bit overwhelming, so in the end I dropped that idea. Perhaps one day I will go with more advanced retro web-thing, that requires backend logic, but for now, let’s focus only on the frontend.
Arachnophilia
Let’s shortly talk about code editor of my choose. it may have been considered quite good back then, but now it feels a bit cheap. Still it’s more than regular MS Notepad can offer — it has some basic HTML syntax highlighting and preview feature (click on a button and Arachnophilia saves the current HTML code in a temporary file and opens it in IE for preview; it claims to reload the page after every save, but unfortunately, that feature didn’t work for me).
It doesn’t support JS and CSS files, but you can create them as txt files with .js or .css extension. Then just write code there.
When you choose to create a new HTML file, a prompt window will appear, allowing you to set the page title, text and link colors. Based on your input, Arachnophilia will generate some initial HTML code for you, with the title set in the head section and the text and link styles added as attributes to the body tag (I’m not going to keep it that way).
The initial HTML code has capitalized tag names, which I quite like as it gives off an old-school vibe, so I’ll try to follow it. Of course, there is no auto-indentation.
Logic comes first
Before implementing the entire page, I want to explore what I can do with JavaScript.. My goal is to have JS-controlled tabs — when a navigation link is clicked, the associated tab should be displayed while others are hidden.
First I focused on Internet Explorer. There are no dev tools and no JS console — if something throws an error IE shows warning icon in the bottom bar, duble-click on it gives us more detailed info — error message and where it occurred.
Not what I’m used to, but still quite helpful. When I had some problem I usually was able to find solution when googling it (results pointing to old threads on forgotten forums) or I used caniuse.com to figure out what methods are available (it lists only IE v6 and higher, but from my experience, when something was “green” for 6–8, then it worked on IE v5 as well).
Here are couple of things, that I figured out during PoC JavaScript development for Internet Explorer 5:
getElementsByClassName
is not supported, butgetElementsByName
works fine (so I used it to select all navigation links)…- … but not with DIVs (so I couldn’t use it to select tab contents),
addEventListener
doesn’t exist, but IE has own method for that —attachEvent
— it takes at least two arguments — eventName (but with on at the beginning, as example: onclick instead of click) and callback (which doesn’t receive the event object as an argument),anchorElement.getAttribute('href')
returns the entire URL path instead of just the value added in the href attribute (so if the attribute has assigned the value #foobar, you will get locally something like C:\\…\#foobar)
That’s my HTML to test my tabs concept:
<A href="#tab0" tab="tab0" name="link">Link 1</a>
<A href="#tab1" tab="tab1" name="link">Link 2</a>
<A href="#tab2" tab="tab2" name="link">Link 3</a>
<DIV id="tab0">Content 1</div>
<DIV id="tab1" style="display: none;">Content 2</div>
<DIV id="tab2" style="display: none;">Content 3</div>
I kept href attributes to make it more semantic, but needed custom attribute tab
to make it easier to get tab ID with JS.
And here’s my JavaScript code to control switching between tabs:
var tabLinks = document.getElementsByName('link');
var currentOpenTabElement = document.getElementById('tab0');
for (var i = 0; i < tabLinks.length; i++) {
tabLinks[i].attachEvent('onclick', createOnClickHandler(i))
}
function createOnClickHandler(tabLinkIndex) {
var tabLink = tabLinks[tabLinkIndex];
var tabId = tabLink.getAttribute('tab');
return function() {
openTab(tabId);
}
}
function openTab(tabId) {
var tab = document.getElementById(tabId);
currentOpenTabElement.style.display = 'none';
tab.style.display = ''
currentOpenTabElement = tab;
}
And I need to admit — I’m surprised. Code looks quite good and it does what I wanted (switches between tabs). It’s 1999, it’s Internet Explorer and it works. I’m really surprised.
Netscape Navigator — first impact
If my code works on Internet Explorer, what could possibly go wrong on Netscape browser? — I asked myself. In the ’90s it was even the most popular browser, and while it eventually lost to IE, I was sure I have nothing to worry about. So I opened my page in Netscape Navigator and…
…tabs control doesn’t work.
My first bet was that attachEvent
is just IE thing, so it won’t work in Netscape. But how can I know? Where Netscape prints out JS error messages? Is there any JS console? No. Is there some info in the bottom bar like in IE? No. Is there any indication, that something went wrong when executing JS code? Well… it doesn’t work so it’s kind of feedback, but no, there isn’t.
So how to debug JavaScript in Netscape Navigator 4.51? Well, you need to make some error handling by your own — add an window.onerror
handler that will call alert
method with error message (I didn’t came up with this by my own, found it here).
Ok, so getElementsByName
didn’t exist in Netscape world at the time. What about getElementsByClassName
? No. Maybe getElementById
at least? No. Is Netscape Navigator all about no’s? No, but for sure it gives you a lot of them.
You won’t find any info about support on caniuse.com, you’re limited to what’s left on the internet. And, fortunately, there is one resource that can be useful here — Client-Side JavaScript reference (version 1.3) by Netscape Communications Corporation, published in 1999. That gave me some idea of what I could use, and, oh boy, the Netscape version of JavaScript feels like a completely different story. There are collections like document.ids or document.classes but we can’t do much with them — usually, we can only set some basic styles, but only once when the browser loads the page.
My idea was to use the onClick
attribute (as it’s a more cross-browser solution) to attach click handlers responsible for setting display style property for tab elements. But I couldn’t make it work. Once again, I had to turn to Uncle Google and hope for some luck. Found few old articles, but most of them pointed me nowhere. Finally I got this one that saved my day. Netscape introduced own layer
HTML tag, that was intended to position and animate (through scripting) elements in a page.
The only problem with the <layer>
tag is that it behaves like an element with CSS absolute positioning (relative to the parent layer or window). Luckily, there is also the <ilayer>
tag, which is just an inline layer. That's more useful, but there is still one minor issue - the visibility
attribute for layers works like the CSS visibility
property, which means you can hide the element but it still takes up space. As a result, the first tab will be shown where I want it to be when visible, but the other ones will appear below, which would look bad. To fix this, I need to use a negative top
property value to move them into the correct position. Phew...
So, how does the cross-browser version look? First, I had to add some inline script in the document’s <head>
to determine if the browser is Netscape:
<SCRIPT>
var probablyNetscape = !!document.layers && !!document.classes && !!document.ids;
</SCRIPT>
Why didn’t I use the navigator
property to check which browser is being used to visit the page? navigator.appName
returns Netscape
, fine, but same as Chrome in 2023 and navigator.userAgent
gives me back Mozilla v4.51
— that’s a bit more specific, but not entirely reliable. Checking if document
has properties like layers
, which are totally Netscape things, provides me with more confidence.
Anyway — I’ll use the info that it’s probablyNetscape
in my scripts.js file and directly in the HTML. Speaking of HTML, here it is:
<A href="#tab0" onClick="tabLinkClickHandler('tab0')">Link 1</A>
<A href="#tab1" onClick="tabLinkClickHandler('tab1')">Link 2</A>
<A href="#tab2" onClick="tabLinkClickHandler('tab2')">Link 3</A>
<DIV>
<ILAYER name="tab0" style="display: block;">
<DIV id="tab0">Content 1</DIV>
</ILAYER>
<ILAYER name="tab1" visibility="hide" style="display: block;">
<DIV id="tab1">Content 2</DIV>
</ILAYER>
<SCRIPT>if (!probablyNetscape) {document.getElementById('tab1').style.display = 'none';}</SCRIPT>
<ILAYER name="tab2" visibility="hide" style="display: block;">
<DIV id="tab2">Content 3</DIV>
</ILAYER>
<SCRIPT>if (!probablyNetscape) {document.getElementById('tab2').style.display = 'none';}</SCRIPT>
</DIV>
As you can see, I used some inline script to add the display: none
style to tabs that should be initially hidden if the browser is not Netscape. I did this because the possibility to modify the display
property with JS in Netscape Navigator doesn't work as you would expect (so I couldn't remove it), and I wanted to hide those tabs as soon as possible (before scripts.js
loads).
The <ILAYER>
properties, such as visibility
and top
, will only be understood by Netscape. Other browsers will ignore them (well, IE 5 ignores them, so I hope that future browsers will do the same).
The links for tab navigation have onClick
attributes with click handlers assigned, which are defined in my scripts.js
file:
var activeTab = 'tab0';
if (probablyNetscape) {
window.onerror = function(message, file, line) {
alert('JavaScript error!\nFile: ' + file + '\nLine: ' + line + '\nMessage: ' + message);
}
// Set position for layers in Netscape Navigator
document.layers.tab1.top = -38;
document.layers.tab2.top = -76;
}
function tabLinkClickHandler(tab) {
if (tab === activeTab) return;
if (probablyNetscape) {
document.layers[activeTab].visibility = 'hide'
document.layers[tab].visibility = 'show'
} else {
document.getElementById(activeTab).style.display = 'none';
document.getElementById(tab).style.display = 'block';
}
activeTab = tab;
}
And I know — if someone clicks a link before script file loads, it will cause an error, so at this point I could just put all my script code into index.html, but… I just want it to be like that.
Now, I have a working cross-browser proof of concept. That experience was — thanks to Netscape Navigator — a bit frustrating. But now it’s time for relaxing part — layout and css styling!
Good look with that
I learned my lesson — it doesn’t make sense to develop something specifically for IE5 and then try to adjust it for Netscape Navigator, as I would end up with completely different code. From the beginning I need to look for solutions that will work on both browsers.
I had some idea of HTML structure and CSS styles I would like to use, so to make it cross browser, I started doing things step by step. Now let me tell you a story about craziness of styling HTML layouts in 1999.
Centring elements with margin left and right set to auto doesn’t work. There are two possible solutions — <center>
tag or <div>
with attribute align=”center”.
That element should wrap page content container (with width set to 480px). In both cases IE centers also content of descendants as well, so we can set attribute align
to left
on page container, that fix things IE. However Netscape moves the entire container back to left, so to make it right (I mean center, but with content on left), we need third container with align attribute set to left
. Here’s some code, to make it easier to understand:
<DIV align="center">
<!-- this one has width set to 480px in styles.css -->
<DIV class="page-container">
<DIV align="left">...</DIV>
</DIV>
</DIV>
CSS flexbox (not to mention grid system) wasn’t around all the time. If you’re in web development more than ~10 years then most probably you remember that to make a grid layout developers used float
property (even Bootstrap did). I believe it wasn’t intended to be used for that (originally), but developers gave it quite important place in history of styling website layouts.
Anyway, I used floats to align my tab controls (links), I wanted to use here list tags — ul
and li
— but looks like Netscape doesn’t like combination of float and list elements… Moreover, using float style directly on anchor tag will remove all default anchor styles (I’m talking about Netscape, on IE everything is fine). I’ve end up with DIVs-based list.
Background color on a DIV set with CSS is some serious s*** for Netscape Navigator v4.51. Without CSS border
property set to none
(or whatever, it just needs border style to fix itself), only text inside DIV will have desired background color (like some text highlight style).
Setting background-image
gave me another headache. IE requires image (bg.gif
) path to be relative to styles.css file (so url('bg.gif')
is ok), but Netscape expects it to be relative to index.html file (url('assets/bg.gif')
). The easiest solution here is to set background directly in body style attribute (with path relative to index.html, of course).
When I was working on the navigation, I wanted to change styles of link when associated tab is opened. Due to Netscape’s limitations I had to play with (i)layer tag and I got to the point where my HTML caused Netscape critical error.
...
<DIV class="tab-control">
<ILAYER name="start-link" class="tab-control-layer">
<DIV class="tab-control-inner">
<A href="#start" id="start-link">Start</A>
</DIV>
</ILAYER>
</DIV>
...
Well, maybe it also had something to do with CSS, but I haven’t checked that.
Next problem — margins. Let’s make it more clear: margins on Netscape Navigator v4.51. You can set margin: 0
everywhere, but Netscape doesn’t give a… you know what. It just knows better. Margins must be. Of course there is a solution — use negative values. However that would affect other browsers (IE) too, and other browsers understand that ZERO is ZERO. NO MARGINS THEN. WHAT’S WRONG WITH YOU, NETSCAPE?
At this point I knew I would never make Netscape version of my site as good as I want. And I need to use special styles for it. How to differentiate it? My first thought was to add additional class to body with JavaScript in case of non-Netscape browser (because I can’t set class names with Netscape version of JS), but it turned out that Netscape Navigator 4 is so broken, that it’s quite easy to set styles that will be ignored by it. Using html
as descendant selector will do the job, cause Netscape doesn’t recognise it as valid one.
Working with CSS for my page realised me how many things are broken in Netscape Navigator 4, as example: you can use every selector only once (others will be ignored) and combined selectors are not welcome (no go for something like .class.with-other-class
).
I wasn’t able to remove spaces between tab links that you can see on image above.
Of course Internet Explorer 5 is not perfect either, as it has some unexpected behaviours, but Netscape Navigator 4 is just pure craziness.
Speaking about IE — I wanted to implement simple animation of catchphrase (Welcome to 1999!) below page headline that slides from left to right continuously. I used setInterval
for that purpose, and it turned out that passing anonymous function as callback will crash IE after few seconds.
// IE5 KILLER
setInterval(function() {...}, 100);
Defining function first and then passing it to setInterval
makes things right.
// THAT'S FINE!
function animateCatchphrase() {...}
setInterval(animateCatchphrase, 100);
My bet is that IE has some problem with memory management in case of anonymous function. Netscape Navigator has no problem with that. Good job Netscape, 10 points to Gryffindor.
Finally
If I would want to describe all problems I had when working on my 1999 page project, I would have to write a book about it. I think I gave you already most interesting (frustrating) part of it. But believe me — there is more, like paragraphs styling — in this case I quickly gave up and used DIVs instead.
Anyway, after few hours spent on it my page was ready and working on both Internet Explorer 5 and Netscape Navigator 4.51.
Total size of all files required to run the page is ~42KB (html, css, js and two images — all uncompressed). I couldn’t find any reliable information about average speed of the Internet connection in 1999, so let’s calculate downloading time for telephone-based modem that in late ’90s was able to reach 56 kbit/s speed — with that kind of device (operating on full speed) it would take 6 seconds to get all data. In 1999 it was probably acceptable.
I’m not going to paste the whole code here (long and boring), you’ll find link to Github repository somewhere below. Now, let’s take a look on some screenshots of my page I took on Windows 98.
Internet Explorer 5
I must say that I’m really proud of how it ended up on Internet Explorer. It looks and works as I wanted.
Netscape Navigator 4.51
As you can see Netscape Navigator version has some flaws… the content section and tabs appear broken and despite spending a lot of time trying to make them look as close as possible to what I achieved on IE, I ultimately failed… Perhaps I would been able to do so if I would done everything on LAYER
s, but that sounds like over-engineering.
Back to the future
Let’s back to 2023 and open my page in some modern browser, like Google Chrome.
It looks… small, but apart from that everything looks and works good. There is one minor problem with size of the content box (it’s too wide, you can easily tell that if you look on the right-bottom corner of grey box). The page wrapper has a width of 480px, and the content box inside the page wrapper also has a width of 480px but also has 10px padding on each side. Back in 1999, this worked fine (although I believe it was a bug in both IE and Netscape). However, now the content box has a total width of 500px (sum of the width and padding on both sides) — which is a more expected behaviour. We can fix this with the following three lines of CSS code:
* {
box-sizing: border-box;
}
Retro browsers won’t understand it, so nothing should be broken there. No further changes are required (well, I need to add short info about privacy policy, because of the GDPR law we have in EU).
If you would like to check this page on your own, it’s published at 1999.mihau.co, and you can find the code in the Github repository.
Last words
This small, simple website took me much more time than I had initially assumed, and it’s not because I’m bad at task estimation (well, sometimes I am, but it wasn’t my fault this time). The possibilities of Internet Explorer 5, compared to modern browsers, are, of course, much, much smaller, but it felt like a world I knew. Netscape Navigator 4.51 was a completely different universe. It had very limited DOM manipulation and event handling capabilities, and CSS felt completely broken (you can find all the bugs described here).
But, stop. I’ve already complained about it a lot. I’m polish, so complaining is in my DNA, but enough is enough. Let’s try to understand some things here.
First of all, it was 1999. I believe that even in the early 2000s, table layouts and attributes for styling were still more popular than CSS. And the content itself was more important than the look. JavaScript? Relatively new and lacked standardization. Netscape came up with that, Microsoft implemented own version, with different APIs and more possibilities. And anyway Flash became more popular back then.
Secondly — 4.51 wasn’t a major release, Internet Explorer 5 was. I strongly believe that Netscape improved and fixed a lot of things in Navigator v5 (released in 2000). Well, I haven’t checked that, but maybe I’ll try one day.
Anyway, despite all the frustration caused by the broken Netscape browser, it was still a nice travel in time. Now I can go back to the future and live long and happy with all these JS frameworks and browsers that are updated more often than I take out the trash.
Thanks for your time! I hope you enjoyed the article. Don’t hesitate to give a clap or leave a comment!
You can follow me on medium, twitter or mastodon. And if you really liked the article and would like to spare me some change — you can do it via buymeacoffee.com or buycoffee.to.
More from me:
The Game of Life in one tweet — a short story about condensing code to fit within 280 characters.
Do not waste your money on developer certifications — why you should think twice before you spend your money to get another dev certificate.