<h2class="episodetitle">Up and Running with JS</h2>
</div>
<article>
<h2class="sectiontitle">JavaScript in an HTML Document</h2>
<p>Like CSS, JavaScript can be inline or it can be stored in an external file. This is demonstrated in exercise file 02_01 which contains the HTML file shown below.</p>
<p>The file contains some inline CSS near the top and inside the <head> and wrapped up in the <style> tag. Similarly, neat the bottom there is some JavaScript and this is wrapped up in a <script> tag. You can put the <script> tag anywhere in the document but it is meta data - that is, it's not part of the document being viewed - so it really should go into the <head> section as well. However, you will often see it down at the bottom of the page, especially in older code.</p>
<p>The reason for this is quite simple. When the page is loaded and the browser encounters this JavaScript, it will stop rendering the HTML until it has downloaded and executed the JavaScript and this is known as render-blocking. However, as you can see in this example, the JavaScript looks for any element in the HTML of type code and what it is doing is formatting this as an HTML tag. For exanple, it converts script to <script> and so on.</p>
<p>When we view the web page, we can see that three elements have been formatted in this way but if we move the JavaScript up to the <head> section, none of these elements are formatted as HTML. This is because the JavaScript is being loaded and executed and this pauses the HTML rendering, and as it rund before the HTML can be rendered, the browser has not rendered the code elements so there are no elements to be formatted.</p>
<p>By putting the JavaScript at the end of the file, it doesn't run until the HTML has been fully rendered and therefore we do see the code elements being correctly formatted.</p>
<p>Be aware that this is not the right way to add JavaScript to the HTML file because modern JavaScript offers a better option. But until that option became available, you really had to put the JavaScript after the HTML and that's why you will see this in older code and you may see this being recommended in some tutorials or code examples, and possibly in some documentation.</p>
<p>For now, the thing to remember is that inline JavaScript goes inside the <script> and it will work wherever you put in the HTML, but if it needs to access elements rendered in the HTML, it needs to go after those elements so that they are rendered and available to be accessed by the JavaScript.</p>
<h2class="sectiontitle">JavaScript as an External File</h2>
<p>Like CSS, adding the JavaScript inline is probably not very common and really only makes sense if you are adding a tiny bit of code that doesn't need to be accessed from anywhere other than the page you put it in. For more complex code or for code that you want to be accessible to a number of HTML pages, it makes more sense to put it in a separate file and call that file from any HTML page that needs it. Again, JavaScript is not any different from CSS in this sense and you may often find, if you are using a lot of JavaScript in a website, that it makes more sense to put the JS code in several files with some sort of organisation. You might have a file that contains the JS code for a photo gallery, another for code that handles form input and so on.</p>
<p>Rather than containing the JavaScript, it has a source attribute telling the browser to go and get the JavaScript from the file named in the source attribute. Again, when the browser encounters this tag, it will pause rendering until the JavaScript has been downloaded and executed so we still have the risk of render blocking. In fact, if we view this example in a web browser, we will see an error.</p>
<p>The HTML page itself is just empty. To see the error, we need to open the browser developer tools and look in the console. This is in Firefox, we actually get a more decriptive error message in Chrome.</p>
<preclass="inset">
Uncaught TypeError: Cannot read property 'appendChild' of null
at script.js:51</pre>
<p>THis is providing the name of the function where the error was found and if we look at this function in the JavaScript, we see this line.</p>
<p>This is taking the HTML generated in the JavaScript and trying to add it to the body of the HTML file. However, since we put it in the <head> section, it runs before the <body> is rendered and since there is no body, when the JavaScript executes, we see this error. Of course, we already know how to fix this, we can do that simply by putting the <script> tag after the <body>. Actually, in this example, putting it inside the <body> tag seems to work just as well, but putting after is probably safer, especially when you bear in mind that in a real-world example, the HTML document is probably going to be much more complex.</p>
<p>So, we have seen the problem where the rendering of the HTML is paused while the JavaScript is loaded and executed and the problems this causes when it tries to access HTML elements that haven't been rendered yet. In addition, wesaw that this was resolved by putting the JavaScript, or the <script> tag at least, at the end of the HTML document. This works, but not very well since, for example, there might be some code in the JavaScript that we need to run before the HTML is fully rendered.</p>
<p>To summarise what happens based on what we have already seen when a page is loaded into the browser.</p>
<preclass="inset">
• HTML parsing begins.
• The browser encounters a script tag and starts to download the JavaScript, at the same time pausing the HTML rendering - this is known as render-blocking or content-blocking.
• When the JavaScript has finished downloading, it executes.
• HTML parsing continues. If the <script> tag is in the <head> section, it is at this point that rendering of the HTML in the <body> section begins.</pre>
<p>A more modern solution to this problem is to alter the pattern shown above and we can do that with a couple of additional commands, async and defer.</p>
<p>With async, the JavaScript is treated as it was previously, when the <script> tag is encountered, the browser will download the JavaScript and thene execute it. However, it will not pause the HTML rendering which continues in the background. The advantage, here, is that the JavaScript is loaded and run as quickly as possible. However, you can't rely on the browser having rendered all of the HTML elements when the JavaScript is exceuted so you may only have some of the HTML being accessible to the JavaScript when it runs. Note that rendering of the HTML is paused whilst the JavaScript runs, so you may still have some render-blocking.</p>
<p>With defer, you have a similar situation to that you have with async, but this time the JavaScript doesn't run until the page is fully rendered. So this is the best option, if you need your JavaScript to be able to access the HTML.</p>
<p>If we go back to the previous example, we got this to work by putting the line</p>
<p>and this works perfectly well. As a matter of interest, the HTML rendering is going to be very quick here because there is not really much in the file, especially when compared to the JavaScript file so async would actually work fine here as well, but you should bear in mind that async won't be completely reilable here, especially when your HTML documents become much more complex but even in this simple case, there is no way to guarantee that the HTML will always complete before the JavaScript runs and this may give you incomplete results, so you should use defer if you need to guarantee that the HTML rendering completes before the JS is run.</p>
<p>You can still use the old way of putting the <script> tag at the end of the document, but you should only use that if there is a good reason for doing so and it is difficult to think of a scenario where this would be better than using the defer keyword. Your page will load faster with defer and I can't think of any advantage using the older method gives you.</p>
<h2class="sectiontitle">JavaScript Modules</h2>
<p>Modules in JavaScript are relatively new and experimental, but in many ways they work in a similar way to modules in other languages and for many of the same reasons. Simply put, they allow you to simplify your code by abstracting some of the complexity out into a module and we can see this in exercise 02_04. This is similar to the previous examples we have seen in this episode and the HTML is almost identical to exercise 02_03 except that we have two <source> tags because we now have two JavaScript files and they have an additional type property with a value of module.</p>
<p>These JavaScript files contain the same JavaScript we saw in the previous exercise but it has been brokwn down into two files. The first is backpack.js.</p>
<preclass="inset">
const updateBackpack = (update) => {
let main = document.querySelector("main");
main.innerHTML = markup(backpack);
console.info(update);
};
const backpack = {
name: "Everyday Backpack",
volume: 30,
color: "grey",
pocketNum: 15,
strapLength: {
left: 26,
right: 26,
},
lidOpen: false,
toggleLid: function (lidStatus) {
this.lidOpen = lidStatus;
updateBackpack(`Lid status changed.`);
},
newStrapLength: function (lengthLeft, lengthRight) {
this.strapLength.left = lengthLeft;
this.strapLength.right = lengthRight;
updateBackpack(`Strap lengths updated.`);
},
};
export default backpack;</pre>
<p>The second is scrip.js.</p>
<preclass="inset">
/**
* Create a Backpack object, populate some HTML to display its properties.
<p>Basically, all of the code including the functions and constant declarations associate with a backpack object have been taken out and placed in a file by themselves, that's backpack.js. This file exports a backpack object and the script.js file imports it. So, the backpack.js file has created and intialised an object and by importing it, it has made it accessible to other files and the script.js file makes use of that fact.</p>
<p>There are a couple of peculiarities here which are (to my knowledge) specific to JavaScript. Firstly, you may wonder why we need to tell the HTML file about the backpack.js file since it doesn't directly interface with it - or in fact, why do we need to mark these files as modules. The answer to both of these questions is that the HTML document needs to load both files before they can be executed. The HTML file also needs to know that they are modules because they may depend on each other (in reality, the script.js file depends on the backpack.js file) so both must be loaded before they can be executed.</p>
<p>You may also wonder why we didn't use the defer keyword and the simple answer there is that modules are deferred by default so there is no need to tell the browser to do that, it will do it anyway.As a reuslt, we can be sure that the browser will have all the necessary modules available to it when it runs the JavaScript.</p>
<p>One of the implications of this is the suggestion that the module type is just a roundabout way of deferring execution of the JavaScript so we could just use the defer keyword and not the type property. This is not the case and if you try to do that, the code will not execute and you will see error messages sich as</p>
<preclass="inset">
Uncaught SyntaxError: export declarations may only appear at top level of a module
Uncaught SyntaxError: import declarations may only appear at top level of a module</pre>
<p>The import and export declarations tell the browser that these are modules so they have to be declared as such. You can add the defer keyword and you can also use async although I'm not sure if that would override the default behaviour of the module, it may defer execution anyway, but you can certainly specify it in your HTML.</p>
<p>Modules are probably not used much in vanilla JavaScript, but its useful to know about them because they come from the world of frameworks such as React and are very common there, so you may need to know about them when you start to move into more advanced study. But also, they can be used in vanilla JS, it is an option if you want to streamline your code.</p>
<p>However, one final point that it is important to be aware of. The browser is now accessing a backpack object via the script.js file. What I mean by that is that the object is exported from backpack.js and imported into script.js and then script.js presents it to the browser. More accurately, it presents the output of a backpack object, which is the HTML listing the backpack attributes.</p>
<p>If we run this page (which looks no different to the HTML output from the previous example) and open the console to view the backpack object as we did before, we see an error message telling us that backpack is not defined. We know that it is defined, of course, but the browser doesn't because it can only see the HTML passed to it by script.js. For this reason, it might not make sense to use modules, at least in some cases, but we will (I hope) see more on these later in the course. </p>