Start tracking your progress
Trailhead Home
Trailhead Home

Understand JavaScript in the Browser

Learning Objectives

After completing this unit, you'll be able to:

  • Describe the interaction between JavaScript and the browser.
  • Explain the role of web APIs.
  • Use basic Document Object Model APIs.
  • Describe ways Lightning components abstract the DOM.

How JavaScript Works in the Browser

You just learned a little about the JavaScript runtime engine and language. You also learned a few things that everyone writing JavaScript should know. In this module, we focus on the relationship between JavaScript and the browser, with a special focus on how your web page looks in JavaScript. 

The JavaScript Runtime Is Not the Browser, Nor Is the Browser JavaScript

So the JavaScript runtime engine runs in many different places, but it’s most often hosted in a browser. So what’s the difference between JavaScript and the browser? 

A browser’s primary job is to act as a client for a web server. It requests resources over the Internet, using one of several protocols (usually HTTP/HTTPS). Once a server passes it some of those resources, the browser needs to do something with them. At a minimum HTML and CSS are rendered into a page. When a resource contains some JavaScript, the browser reaches over to the JavaScript runtime engine to parse, evaluate, and execute that code. 

Likewise, while a script is executing it can also reach back to the browser to do things like modify the web page, interact with the local environment, or interact with other web resources.

JavaScript Engine with APIs

To make these interactions work, the browser surfaces APIs. In fact, a large part of what people think of as client-side JavaScript is actually these APIs. 

Web APIs

Standards

It shouldn’t be surprising that there are APIs to interact with the browser. Just like any other programming language or platform surfaces APIs to interact with the environment it runs on, so too does your browser. 

While over half of browser internet traffic comes from Google Chrome, roughly another 30 percent comes from five other browsers. This underscores the importance of the web standards for these APIs, so that JavaScript can be written once and run anywhere. 

Adding Web APIs to the Picture

Previously we created a picture of the JavaScript runtime. 

  • Requests execute on the stack using a single thread.
  • A request holds the thread until it is done executing all synchronous logic for that request.
  • New requests are queued until the thread is ready.
  • The event loop moves the next queued request into the stack when it becomes available.

We’re now adding web APIs to our picture of JavaScript. These extend the core JavaScript language. They surface interfaces that perform much of the work that is critical to the user experience of the modern web. For example, browser APIs can: 

  • Interact with the structure of the current page rendered in the browser (Document Object Model or DOM API)
  • Perform asynchronous requests to the server without leaving the current page (Fetch API)
  • Interact with audio, video, and graphics
  • Interact with device features surfaced to the browser (geolocation, device orientation, client-side data storage)

Most of the time it is these APIs that add new requests to the queue. 

Note

Note

We’re sidestepping an important piece of the JavaScript ecosystem: third-party APIs and JavaScript libraries that abstract or extend browser capabilities. These play an important role in how JavaScript is used in the wild. 

For our purposes in the context of Salesforce, we cover our JavaScript framework: the Lightning Component Framework. Specifically, when talking about JavaScript on the Lightning Platform, this module addresses the Lightning Web Component development model.

Take an example. You have a list of notes on a web page with a form. You fill out some fields on the form, click a button to save that data, and a new item is added to the list. For fun, let’s say also that you will cache this new record locally to speed up future requests involving that record. 

This simple use case would interact with the following browser APIs

  • Fetch API (to save the record)
  • DOM API (to add a new item to an HTML list)
  • Client-Data Storage (to cache the data locally)

Let’s dive into the DOM API, since it represents what the user sees within JavaScript. 

The Document Object Model

The DOM: Your Page on JavaScript

When a page is requested and then received by a browser, the browser parses the HTML and create a description, or a model, of that page. This model is used to draw the page in the browser’s viewport. It’s also surfaced to JavaScript through the DOM. 

Think of the DOM as a tree. It starts at the root of the browser’s display functionality: the window. From there, the page is encapsulated in window.document, with the page’s body in window.document.body. Then the tree fans out to every bit of content represented on the page. Most web pages have a very complex tree with many nodes finally ending in leaf nodes as the most granular pieces of the hierarchy. 

As an API, the DOM is vast, and lets you touch every part of this tree. It also has a number of methods to optimize interaction with the DOM. Take a look at this simple example on jsbin.com. It includes:

  • An input field
  • An Add Item button
  • An unordered list

Each time you click the button, the code reaches into the input field, reads its value, converts it to a text node, creates a new li element, inserts the text node into the li node, and finally sticks the new li-and-text node into the ul

Note

Note

For extra fun, and if you want to practice some of the principles discussed so far, see if you can modify the jsbin to do the following.

  • Check if the text input has data in it or not
  • If it doesn’t, do not add a new li to the list
  • Once the new li is added, clear the text input

If the suspense is killing you, check out the modified code.

As you can see, just this simple example requires the developer to perform a bunch of manual steps. And it doesn’t even store any data on a server, or interact with a server in any other way. For this reason, JavaScript libraries (reactjs, jQuery) and frameworks (Angular, vuejs) have become the standard for interactive pages. Such frameworks abstract away and simplify DOM interactions, and often automatically apply polyfills for missing features. The Lightning Component Framework is no different. 

Encapsulation with Shadow DOM

The DOM API is rich and flexible. Using relatively simple JavaScript it is easy to make changes to the looks, behaviors, and actions invoked by the UI. 

But there is a pitfall. The DOM model makes it difficult to encapsulate pieces of the UI and protect them from accidental (or purposeful and malicious) changes. 

For this reason, the Shadow DOM standard was developed. Shadow DOM creates a boundary around a particular part of UI functionality. The boundary prevents a parent from changing the elements or CSS of a child. It also forces any events propagated across the boundary to rescope their targets, preventing the parent from reaching across the shadow DOM boundary.

The Lightning Component Framework: Aura Components and Lightning Web Components

As of the Spring ’19 release (API version 45.0), you can build Lightning components using two programming models: the Lightning Web Components model and the original Aura Components model. Lightning web components are custom HTML elements built using HTML and modern JavaScript. Lightning web components and Aura components can coexist and interoperate on a page. This content covers only Lightning Web Components.

Lightning Web Components and the DOM

Note

Note

Each unit from this point forward will have a similar section with some examples to illustrate the tie-in to Lightning Web Components. If you want to apply what you’re learning, now is a great time to begin the Lightning Web Components Basics module on Trailhead.

Automatic DOM Updates

The fundamental value of Lightning components is in building a custom UI driven by Salesforce data. This ties in to DOM manipulation because the DOM itself is driven by Salesforce data. 

This component demonstrates this principle in its simplest form.

Simple component with data driven UI, displaying text that reads “This text came from a JS prop.Here is the code of this component.

// JavaScript Module: demo.js
import { LightningElement } from 'lwc';
export default class Demo extends LightningElement {
    text = 'This text came from a JS prop';
}
<!-- HTML Template: demo.html -->
<template>
    <lightning-card title="Basic DOM Example" icon-name="utility:hierarchy">
        <div class="slds-card__body slds-card__body-inner">
            <p>
                <lightning-formatted-text value={text}></lightning-formatted-text>
            </p>
        </div>
    </lightning-card>
</template>

First some context: Every Lightning web component requires a JavaScript module. If the component contains any elements to be presented to the user, an HTML template is required, too. Most components contain a JavaScript and an HTML file in what’s called a bundle. 

Lightning Web Components make it effortless to render data into UI. For example, the code above uses the JavaScript property text to store a hard coded string. In the <lightning-formatted-text> tag, the value attribute's text property, is bound to the JavaScript text property, rendering the data in the UI. 

But what if you want the UI to change when the user interacts with it? We can extend this example to do just that.

Here's the code.

// JavaScript Module demo2.js
import { LightningElement, track } from 'lwc';
export default class Demo2 extends LightningElement {
    @track text = '';
    handleChange(event){
        this.text = event.target.value;
     }
}
<!-- HTML Template demo2.html -->
<template>
    <lightning-card title="Basic DOM Example" icon-name="utility:hierarchy">
        <div class="slds-card__body slds-card__body-inner">
            <p>
                <lightning-formatted-text value={text}></lightning-formatted-text>
            </p>
            <p>
                <lightning-input onchange={handleChange} label="Input Text" value={text}></lightning-input>
            </p>
        </div>
    </lightning-card>
</template>

If you look at the text property again, you see we’ve added some new behavior. By using @track, we take advantage of a relatively new JavaScript feature called a decorator. This decorator attaches functionality to the property that ensures that when its value changes, the component’s DOM is rerendered.  

In the template we added a <lightning-input>. Although binding the text properties is key to displaying the data, notice the onchange attribute. The handleChange function that’s been added to the JavaScript module now is triggered by change events. Each modification to the value by the user updates the value of text. When text is updated, so is the DOM.  

While this is a contrived example, it illustrates how Lightning Web Components surfaces features that make implicit DOM changes easy to implement as a developer.

Explicit DOM Manipulation

Manual DOM manipulation shouldn't be your first strategy when building a Lightning web component, but more sophisticated components may require you to do so. This is a more advanced topic, but it’s worth scratching the surface of how this works before signing off on the DOM. 

Conditional Rendering

When writing Lightning web components, you can explicitly define sections of the UI that are rendered only when certain conditions are met. You can do this by adding an if:true or if:false directive to a nested template tag. 

//conditionalButton.js
import { LightningElement, track } from 'lwc';
export default class ConditionalButton extends LightningElement {
    @track show = true;
    handleClick(){
        this.show = !this.show;
    }
}
//conditionalButton.html
<template>
    <lightning-card title="Conditional Rendering with Button" icon-name="utility:hierarchy">
        <div class="slds-card__body slds-card__body-inner">
            <p>
                <template if:true={show}>
                    Peek-a-boo!
                </template>
                <template if:false={show}>
                    I'm hiding!
                </template>
            </p>
            <p>
                <lightning-button onclick={handleClick} variant="neutral" label="Switch"></lightning-button>
            </p>
        </div>
    </lightning-card>
</template>

In this code sample, the text Peek-a-boo! appears when the show property has a value of true. When show is set to false, the text I’m hiding! appears. Clicking the button toggles the value of the show property.

Other Ways to Manually Manipulate the DOM

In some circumstances, it’s OK to manipulate the DOM element display using the display CSS style. Although this method works as usual in Lightning Web Components, it’s beyond the scope of this particular module. 

Resources

Trailhead Module: Lightning Web Components Basic

Lightning Web Components Developer Guide: HTML Templates

Lightning Web Components Developer Guide: HTML Template Directives

Lightning Web Components Developer Guide: Render DOM Elements Conditionally

Lightning Web Components Developer Guide: DOM Access Containment

Lightning Web Components Developer Guide: Shadow DOM

UI Frameworks:

Third party web APIs

retargeting