The Browser API

Now we move into the part of our book on JavaScript that is no longer just vanilla JavaScript. To work with JavaScript in the browser, we have to interact with the Browser API.

APIs are ways for us to interact with something using code. In this case we use the Browser API to let our JavaScript code interact with the browser code (the HTML and CSS).

We call the Browser API the "DOM," or Document Object Model.

When it comes to working with the DOM, or the Browser API, as I'll call it, you want to know three fundamental things:

  1. How to Get Access to HTML with Your JavaScript

  2. How to Manipulate HTML and CSS with Your JavaScript

  3. How to Create and Add Your Own HTML or CSS to a Page

  4. How to Listen for User Interactions on the Page

With these things you can do an awful lot. Here are just a few of the things you could do with that:

  • Build a modal that pops up over your content

  • Wipe parts of the page and add your own content

  • Update content on the page with fresh or new data

  • Track form usage and provide user feedback

  • Trigger CSS animations

  • Build charts, graphs and reports

  • Just about anything else you've seen JavaScript do

The list goes on and on, and while we will tackle some of these things in projects, for now we are going to keep it simple with our list of 4 Browser API Fundamentals:

  1. How to Get Access to HTML with Your JavaScript

  2. How to Manipulate HTML and CSS with Your JavaScript

  3. How to Create and Add Your Own HTML or CSS to a Page

  4. How to Listen for User Interactions on the Page

How to Get Access to HTML with Your JavaScript

Let's imagine that we have the following HTML Document:

<!DOCTYPE html>
<html>
<head>
<title>JavaScript and the Browser API</title>
<meta charset="UTF-8" />
</head>
<body>
<div id="content">
<h1>Header 1</h1>
<p>Some lorem to <strong>fill</strong></p>
<div class="featured">
<p><a href="https://javascriptexplained.com">Visit Us!</a></p>
</div>
<p>Closing thoughts</p>
</div>
<script src="js/index.js"></script>
</body>
</html>

We have a number of ways to select HTML with the Browser API, but the two simplest are querySelector() and querySelectorAll().

Remember how the Browser API is called the Document Object Model. It is literally a JavaScript object called document that the browser gives us. So, in order to use any DOM or Browser API functions, we have to do document.functionName(). Or, in this case: document.querySelector() document.querySelectorAll()

This is a little verbose, but it reminds us that we are not dealing with vanilla JavaScript anymore, but an object the browser gave us to mess with the web page.

The reason these are the simplest is because they use the same selectors as CSS. See if the selectors we use inside of these functions look similar to how you select HTML with your CSS.

src/index.js
// Gets the div with id of "content"
const content= document.querySelector(`#content`);
// Gets <h1>Header 1</h1>
const header = document.querySelector(`h1`);
// Gets <strong>fill</strong>
const bold = document.querySelector(`p strong`);
// Gets the div with the class of "featured"
const featured = document.querySelector(`.featured`);
// Gets all the <p> tags and what is inside them
const paragraphs = document.querySelectorAll(`p`);
// Gets all the <div> tags and what is inside them
const divs = document.querySelectorAll(`div`);

See? Looks kind of familiar! Except for all the document.querySelector()parts.

Hopefully the above code makes sense. These functions takes a CSS selector using # for ids and . for classes.

Both of these functions each have one important thing to remember.

  1. querySelector() will only return the first matching element and ignore and

  2. querySelectorAll() always returns a collection of none, one, or many items

Below are some examples that explain what I mean. Reference the following HTML:

<div id="primary">
<h1>Heading</h1>
<p>Lorem</p>
<p>Ipsum</p>
</div>
<div id="secondary">
<p>Fin</p>
</div>

If we use querySelector() it will get the first match it finds:

// Gets <p>Lorem</p>
// Ignores the rest of the <p> tags
const paragraph = document.querySelector(`p`);
// Gets <div id="primary"></div>
// Ignores <div id="secondary"></div>
const divs = document.querySelector(`div`);

And if we use querySelectorAll it will always return a collection that has zero, one or multiple items in it.

// Gets [ ] and empty collection
// Because no links are in the HTML above
const links = document.querySelector(`a`);
// Gets a collection of one [ <h1>Heading</h1> ]
// Because no other <h1>s are found
const header = document.querySelectorAll(`h1`);
// Gets a collection of multiple items
// [ <p>Lorem</p>, <p>Ipsum</p>, <p>Lorem</p> ]
const header = document.querySelectorAll(`p`);

So, remember, when getting elements from HTML with the Browser API:

  • querySelector() will get the first matching HTML element

  • querySelectorAll() will get a collection of none, one, or many items

  • Both take CSS Selectors

  • Add Array.from() on DOM collection

Other selectors exist, but we will primarily use these two.

Before we wrap up here though, we need to look at how to dig inside the HTML we get.

Here we have a heading with a link inside of it.

<article id="post">
<h2 class="post-title"><a href="/url/here">Heading</a></h2>
</article>

And here we have selected the heading with the Browser API and JavaScript. Then we go on to select the HTML inside of the heading with .innerhTML and the text inside the header with .innerText.

// Gets <h2 class="post-title"><a href="/url/here">Heading</a></h2>
const title = document.querySelector( `.post-title` )
// Logs "<a href="/url/here">Heading</a>"
console.log( title.innerHTML )
// Logs "Heading"
console.log( title.innerText )

We can also get the attributes of any HTML elements.

For instance, here we get the id, class and href of elements:

// Gets <article id="post"></article> and contents
const article = document.querySelector( `article` )
// Logs the article id of "post"
console.log( article.id )
// Get the class attribute
const h2 = document.querySelector( `h2` )
// Logs "post-title"
// Note: Use "className" not "class"!!!
console.log( h2.className )

Note, with forms we have to do something slightly different from the above normal markup elements.

<form action="">
<label for="username">Username</label>
<input type="text" id="username" name="username" value="zgordon" />
<input type="submit" />
</form>

To get the value of an input field like the username you would have to use .value instead of innerHTML or innerText.

// Gets <input type="text" id="username" name="username" value="zgordon" />
const username = document.querySelector( `#username` )
// Logs "zgordon"
console.log( username.value )

Of course we could look at an example of how to select every HTML element and attribute, but this is enough for us to move on to a more exciting part, changing and manipulating the content we select.

How to Manipulate HTML and CSS with Your JavaScript

Let's say that we have some HTML like this again:

<h2 class="post-title"><a href="/url/here">Heading</a></h2>

If we save elements to variables we can simply override the values of the HTML text, HTML, attributes, and just about everything else.

// Get the link
const link = document.querySelector( `.post-title a` )
// Replace the HTML inside the link with bold markup
link.innerHTML = `<strong>Heading</strong>`
// Get the h2 header
const h2 = document.querySelector( `h2` )
// Overried the class and add a new one
// Note: Use `className` and not `class`
const h2.className = `post-title hidden`
// Overried text inside element or children
const h2.innerText = `NEW HEADING`

Same goes for forms, although we use value again for form elements like input with value attributes.

// Gets <input type="text" id="username" name="username" value="zgordon" />
const username = document.querySelector( `#username` )
// Replaces username <input> value with "newuser"
username.value = "newuser"

Oh yeah, we can also simply remove elements completely too, but be careful with that of course.

// Gets the <h2>
const header = document.querySelector( `h2` )
// Removes it from the page!
header.remove()
  • Need a section on classList and modifying inline styles

How to Create and Add Your Own HTML or CSS to a Page

Third on our list of four important things to know about the Browser API is how to create our own HTML and add it to the page.

Most modern JavaScript sites start with an empty div for a page and build the entire UI from JavaScript.

There are more formal ways to create HTML with the Browser API, but the simplest is to create DOM Strings, or strings of text that get converted into HTML for us via the Browser API

// A string with HTML markup
const markup = `<div class="page">Hello!</div>`

We may also see markup created over multiple lines:

// A string with HTML markup
const markup = `<article class="post">
<h1>Post Title</h1>
</article>`

Better yet, we can place bits of related HTML markup into functions since we often need to remove and re-add elements to a page. Functions can help make that process more easily repeatable.

Plus, they can accept parameters to make them dynamic.

// A function that returns HTML for a post
function post( title ) {
return `<article class="post">
<h1>${title}</h1>
</article>`
}
// Gives us this article HTML
// <article class="post"><h1>Default Title</h1></article>
post( `Default Title` )

To add this to a page, we have a great Browser API function called insertAdjacentHTML() that let's us select an existing element on the page and add our element before it, inside it, or after it.

Here are the four insertAdjacentHTML( *option* ) options:

  • beforebegin - Insert your HTML before the selected element

  • afterbegin - Insert your HTML inside the selected element before anything else

  • beforeend - Insert your HTML inside the selected element at the very end

  • afterend - Insert your HTML after the selected element

In most cases we will add our HTML using beforeend as in the example below where we take our article and add it to HTML already on the page.

Starting HTML:

index.html
<div class="app"></div>

Then we 1) create our HTML, 2) select the div above and 3) add our markup

// Create a function for our title UI
function title( text ) {
return `<h1>${text}</h1>`
}
// Select our app div
const container = document.querySelector( `.app` )
// Add title inside app
container.inserAdjacentHTML( `beforeend`, title(`Hello!`) )

This would give us the resulting markup on the page:

index.html
<div class="app">
<h1>Hello!</h1>
</div>

Here is a larger example for more markup, but it follows the same pattern:

  1. Create a markup string

  2. Select an element from the page

  3. Add the markup to the element

As usual, we just start with the following markup:

index.html
<div class="app"></div>

Then we build a few functions for different parts of our site. This time though we will build them with arrow functions, to simplify the returns.

// Create functions for UI
const header = siteName => `<header><h1>${title}</h1></header>`
const content = page => (`<section class="content">
<h2>${page.title}</h2>
<div>${page.content}</div>
</section>`)
const sidebar = widgets => `<aside>${widgets}</h1></aside>`
const footer = siteName => `<footer><p>Copyright ${siteName}</p></header>`

Finally, we can setup some data for the site and add the markup inside our starting <div class="app></div>.

// Get the container div
const container = document.querySelector( `.app` )
// Setup site data
const siteName = `My Site`
const page = { title: `Hello`, content: `<p>Lorem</p>` }
const widget = `<h3><a href="/">About Me</a></h3>`
// Add data to the page
container.insertAdjacentHTML( `beforeend`, header(siteName) )
container.insertAdjacentHTML( `beforeend`, content(page) )
container.insertAdjacentHTML( `beforeend`, sidebar(widget) )
container.insertAdjacentHTML( `beforeend`, footer(siteName) )

This would result in the final HTML on the page:

index.html
<div class="app">
<header>
<h1>My Site</h1>
</header>
<section class="page">
<h2>Hello</h2>
<div><p>Lorem</p></div>
</section>
<footer>
<p>Copyright My Site</p>
</footer>
</div>

The nice thing about this approach is saving all your markup for different parts of your site or application in separate functions. This will make them easy to manage long term as well as easier to copy and paste and use between projects.

Now we need to look at the final, and most exciting part of the Browser API: Events!

How to Listen for User Interactions on the Page

For me, listening for and responding to user interactions is the coolest part of the Browser API.

We can setup what are called "Event Listeners" on any element on the page (or even the page itseslf) and listen for dozens of different types of events.

Here are just a few of the events we can listen for

  • Scrolls

  • Hovers

  • Mouse movements

  • Window (re)size

  • URL changes

  • Page and item loading

  • Anything clicked on

  • Touch and drag events

  • Key presses

  • Form interactions

  • Media interactions

  • And more..

In addition to listening to any user events involving these sorts of things, we can also control a lot of these things with our JavaScript via the Browser API.

Adding Event Listeners

All of this starts with selecting the element(s) we want to change listen to changes for:

<nav class="main" >
<ul>
<li><a href="/">Home</a></li>
<li><a href="/services">Services</a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</nav>

We are going to listen for anytime someone clicks on the links in the main navigation above and then call a function named sayHi that will alert a hello!

// Get a DOM collection and convert to a JS array for mapping
const links = Array.from( document.querySelectorAll( `nav.main a` )
// Add an event listener to each link
// To call sayHi() when clicked
links.map( link => link.addEventListener( `click`, sayHi )
// Function called when link is clicked
function sayHi() { alert( `Hi!` ) }

This will attach an "Event Listener" to each link in the main nav that will call our sayHi function

Now, if you were to run this code and click on a link it would alert Hi! and then continue on to the link you clicked on.

Many times in JavaScript we want to prevent the default behavior of events so we can create our own custom interactions. For example, let's say in the example above we did not want the user to go to that page, but we wanted to fetch that data ourselves and display it on the page withou having to reload the entire page.

Examples of Different Types of Events

  • List out various event listener examples with made up function names

Preventing Default Event Behavior (And Adding Our Own)

To prevent the default behavior we can do the following with our event functions:

function sayHi( e ) {
e.preventDefault()
alert( `Hi!` )
}

Notice how we simply added an e -- we could also use event or call it whatever we want -- but this gives us a function called preventDefault() which will magically stop whatever default behavior was about to occur.

In this case, e.preventDefault() stops the user from going to the link they clicked on. In a form it could prevent the form from submitting. We are then able to create our own custom interaction.

function sayHi( e ) {
e.preventDefault()
displayModal( `Hi` )
}

In this example above we could prevent a link from being clicked and instead call a modal to pop up with a message (we will learn how to do this in a later chapter). This shows though how you can begin to create your own custom user interactions.

The Event Object

When a Browser Event occurs and we have hooked into it with an addEventListener() in our JavaScript, we also get access to the event object.

We saw this before when we added e as a parameter to our function:

// Adding an event listener to a link
document.querySelector(`a.hacked`).addEventLister( `click`, sayHi )
// The Browser API will give us the Event Object
function sayHi( e ) {
// Logs out the Event Object
console.log( e )
// This is using the Event Object
e.preventDefault()
}

That simple Event Object would actually log out something like of this, for a simple click event:

MouseEvent {
altKey: false,
bubbles: true,
button: 0,
buttons: 0,
cancelBubble: false,
cancelable: true,
clientX: 517,
clientY: 335,
composed: true,
ctrlKey: false,
currentTarget: null,
defaultPrevented: false,
detail: 1,
eventPhase: 0,
fromElement: null,
isTrusted: true,
layerX: 517,
layerY: 335,
metaKey: false,
movementX: 0,
movementY: 0,
offsetX: 22,
offsetY: 6,
pageX: 517,
pageY: 335,
path: [(a.hacked, div.content, body, html.md, document, Window)],
relatedTarget: null,
returnValue: true,
screenX: 517,
screenY: 438,
shiftKey: false,
sourceCapabilities: `InputDeviceCapabilities`,
srcElement: a.hacked,
target: a.hacked,
timeStamp: 161482.935,
toElement: a.hacked,
type: `click`,
view: `Window`,
which: 1,
x: 517,
y: 335
}

You can see we get quite a lot of information from the event object. This can be used to get a lot of different types of information about what just happened. You can then use this to code custom behaviors and interactions.

The event object will change slightly depending on what type of event occurs (ie, a mouse event, a media event, a window scroll event). It can be a good idea when setting up event listeners to log out the event object to see what you have.

Getting the Element In Your Event Listener Function

When you have a function hooked up to an event listener, you will often want to get that element and information about it.

For example, when a form submits, you will want to get the values of the fields inside. Take a look at this email signup form below:

<form class="signup">
<p>
<label for="email">Email</label>
<input type="email" id="email" name="email" />
</p>
<p><input type="submit" value="Signup" /></p>
</form>

When we setup an event listener on the form submit

const form = document.querySelecto( `.signup` )
form.addEventListener( `submit`, logEmail )
function logEmail( e ) {
// Keyword "this" logs out the <form> and contents
console.log( this )
// Gets the email field inside the form
const email = this.querySelector( `#email` )
}

With Event Listeners, the Browser API will automatically assign the keyword this to whatever you attached the event listener to. This makes it very convenient to get information about what the user just interacted with.

In this example below we can log out the link text when the user clicks on the link

const links = Array.from( document.querySelectorAll( `a` )
links.map( link => link.addEventListener( `click`, sayHi )
function sayHi( e ) {
e.preventDefault()
// Logs out the text of the link clicked on
console.log( this.innerText )
}

There is a lot we can do with the information we have covered so far.

Next Steps

The information in this chapter is crucial for using JavaScript on the web. In particular, writing "event listener" or "event handler" functions is going to be a big part of what you do with JavaScript.

Before you proceed, please practice the related exercises until you feel comfortable with the Browser API fundamentals. Then we will get into some fun pulling in content using the Fetch API!