JavaScript Course 8: DOM and Browser

From WikiMLT

Ref­er­ences

De­f­i­n­i­tions

  • The DOM (Doc­u­ment Ob­ject Mod­el) API al­lows you to ma­nip­u­late HTML and CSS, cre­at­ing, re­mov­ing and chang­ing HTML, dy­nam­i­cal­ly ap­ply­ing new styles to your page, etc. Every time you see a pop­up win­dow ap­pear on a page, or some new con­tent dis­played (as we saw above in our sim­ple de­mo) for ex­am­ple, that's the DOM in ac­tion.
  • The Ge­olo­ca­tion API re­trieves ge­o­graph­i­cal in­for­ma­tion. This is how Google Maps is able to find your lo­ca­tion and plot it on a map.
  • The Can­vas and We­bGL (Web Graph­ics Li­brary) APIs al­low you to cre­ate an­i­mat­ed 2D and 3D graph­ics. Peo­ple are do­ing some amaz­ing things us­ing these web tech­nolo­gies – see Chrome Ex­per­i­ments and we­bglsam­ples.
  • Au­dio and Video APIs like HTML­Me­di­aEle­ment and We­bRTC (Web Re­al-Time Com­mu­ni­ca­tion) al­low you to do re­al­ly in­ter­est­ing things with mul­ti­me­dia, such as play au­dio and video right in a web page, or grab video from your web cam­era and dis­play it on some­one else's com­put­er…

In­ter­nal JavaScript

<script>
    document.addEventListener('DOMContentLoaded', () => {
      function createParagraph() {
        const para = document.createElement('p');
        para.textContent = 'You clicked the button!';
        document.body.appendChild(para);
      }
    
      const buttons = document.querySelectorAll('button');
    
      for (const button of buttons) {
        button.addEventListener('click', createParagraph);
      }
    });
</script>

Ex­ter­nal JavaScript

<script src="script.js" defer></script>
script.js
function createParagraph() {
  const para = document.createElement('p');
  para.textContent = 'You clicked the button!';
  document.body.appendChild(para);
}

const buttons = document.querySelectorAll('button');

for (const button of buttons) {
  button.addEventListener('click', createParagraph);
}

In­line JavaScript han­dlers

<button onclick="createParagraph()">Click me!</button>
<script>
    function createParagraph() {
      const para = document.createElement('p');
      para.textContent = 'You clicked the button!';
      document.body.appendChild(para);
    }
</script>

Please don't do this, how­ev­er. It is bad prac­tice to pol­lute your HTML with JavaScript, and it is in­ef­fi­cient — you'd have to in­clude the onclick="createParagraph()" at­tribute on every but­ton you want the JavaScript to ap­ply to.

Us­ing ad­dE­ventLis­ten­er in­stead

<button onclick="createParagraph()">Click me!</button>
<script src="script.js"></script>
script.js
const buttons = document.querySelectorAll('button');

for (const button of buttons) {
  button.addEventListener('click', createParagraph);
}

Script load­ing strate­gies

There are a num­ber of is­sues in­volved with get­ting scripts to load at the right time. Noth­ing is as sim­ple as it seems! A com­mon prob­lem is that all the HTML on a page is loaded in the or­der in which it ap­pears.

In the in­ter­nal ex­am­ple, you can see this struc­ture around the code:

<head>
    <script>
        document.addEventListener('DOMContentLoaded', () => {
          ...
        });
    </script>
</head>
<body>
    <!-- ... -->
</body>

In the ex­ter­nal ex­am­ple, we use a more mod­ern JavaScript fea­ture to solve the prob­lem, the de­fer at­tribute, which tells the brows­er to con­tin­ue down­load­ing the HTML con­tent once the <script> tag el­e­ment has been reached.

<script src="script.js" defer></script>

Note: In the ex­ter­nal case, we did not need to use the DOM­Con­tent­Loaded event be­cause the de­fer at­tribute solved the prob­lem for us. We didn't use the de­fer so­lu­tion for the in­ter­nal JavaScript ex­am­ple be­cause de­fer on­ly works for ex­ter­nal scripts.

async and de­fer

There are ac­tu­al­ly two mod­ern fea­tures we can use to by­pass the prob­lem of the block­ing script – async and de­fer (which we saw above). Let's look at the dif­fer­ence be­tween these two.

Scripts loaded us­ing the async at­tribute will down­load the script with­out block­ing the page while the script is be­ing fetched. How­ev­er, once the down­load is com­plete, the script will ex­e­cute, which blocks the page from ren­der­ing. You get no guar­an­tee that scripts will run in any spe­cif­ic or­der. It is best to use async when the scripts in the page run in­de­pen­dent­ly from each oth­er and de­pend on no oth­er script on the page.

Scripts loaded with the de­fer at­tribute will load in the or­der they ap­pear on the page. They won't run un­til the page con­tent has all loaded, which is use­ful if your scripts de­pend on the DOM be­ing in place (e.g. they mod­i­fy one or more el­e­ments on the page).

Here is a vi­su­al rep­re­sen­ta­tion of the dif­fer­ent script load­ing meth­ods and what that means for your page – Fig­ure 1.

Figure 1. <script ... async/defer>; This im­age is from the HTML spec, un­der CC BY 4.0 li­cense terms.

To sum­ma­rize:

  • async and de­fer both in­struct the brows­er to down­load the script(s) in a sep­a­rate thread, while the rest of the page (the DOM, etc.) is down­load­ing, so the page load­ing is not blocked dur­ing the fetch process.
  • scripts with an async at­tribute will ex­e­cute as soon as the down­load is com­plete. This blocks the page and does not guar­an­tee any spe­cif­ic ex­e­cu­tion or­der.
  • scripts with a de­fer at­tribute will load in the or­der they are in and will on­ly ex­e­cute once every­thing has fin­ished load­ing.
  • If your scripts should be run im­me­di­ate­ly and they don't have any de­pen­den­cies, then use async.
  • If your scripts need to wait for pars­ing and de­pend on oth­er scripts and/​​​or the DOM be­ing in place, load them us­ing de­fer and put their cor­re­spond­ing <script> el­e­ments in the or­der you want the brows­er to ex­e­cute them.