JavaScript in the Browser

5.2 DOM

# Element Properties

Here is a list of common properties that map to attributes in the HTML as well as some methods that you could use to manipulate the CSS classes assigned to an element.

let c = element.className; //get or set the value of the class attribute
//one string with all the class names

let list = element.classList; //get the list of css classnames assigned to the element (a DOMTokenList)
element.classList.add(className); //add a new css class to the list
element.classList.remove(className); //remove a single css class from the list
element.classList.replace(oldClassName, newClassName);
element.classList.contains();
element.classList.toggle(className); //remove if exists, add if it doesn't exist

let nm = element.tagName; //retrieve the HTML element's tag name eg: P, LI, A, DIV

element.href = 'http://www.example.com';
element.src = 'http://www.example.com/photo.jpg';
element.id = 'bob';
element.title = 'mouseover text';
element.alt = 'alternate description for image'; //accessibility matters
element.style = 'display:none;'; // create an inline style. Not recommended.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# Element Attributes

There are a few specific methods for working with HTML attributes.

let main = document.querySelector('main'); //first main element on page
let img = document.querySelector('img'); //first image element on page
let mi = main.querySelector('main img'); //first image element inside main

mi.setAttribute('src', 'http://www.example.com/photo.jpg'); //set the image's src attribute
let title = mi.getAttribute('title'); //retrieve the value of the title attribute
let hasAlt = mi.hasAttribute('alt'); //boolean indicating if the img has an alt attribute
mi.removeAttribute('href'); //remove the href attribute if it exists
1
2
3
4
5
6
7
8

# Dataset Properties

Having the ability to create your own custom attributes can be a very useful tool and allow you to store things like product reference ids in the HTML. To address this need, HTML5 standardized an approach to inventing custom attributes - called dataset properties.

You are allowed to create your own HTML attributes in the HTML or through JavaScript as long as their name starts with data-. Here is an example of what the HTML would look like.

<p data-price="22.33" data-id="a57dd55d7d8d" data-link="http://abc.io/product">
  Some product information
</p>
1
2
3

And here is the JavaScript that would create that same HTML.

let p = document.createElement('p');
p.setAttribute('data-price', '22.33');
p.setAttribute('data-id', 'a57dd55d7d8d');
p.setAttribute('data-link', 'http://abc.io/product');
document.body.append(p);
//document.body points to the <body> element
1
2
3
4
5
6

It should be noted that ALL dataset property values are strings. If you want to store a numeric or boolean value in a data-set property, that is fine, but you will have convert the value to number or boolean after extracting it.

let priceTxt = p.getAttribute('data-price'); //extract the attribute value
let price = parseFloat(priceTxt); //convert from string to number
1
2

# Document Fragments

A documentFragment is similar to an HTML element in that it can contain other HTML Elements and text nodes. The difference is that they only really exist in memory. They do not appear on the page.

We can use them to transport HTML from memory to the webpage.

Think of the HTML that you want to add to your page as a bunch of sand. The documentFragment is a bucket used to hold the HTML and carry it to the page. When you get to the page, the HTML is dumped out of the bucket on to the page.

let df = new DocumentFragment(); //version one of creating a document fragment
let dfAlt = document.createDocumentFragment(); //version two. Both work

let p = document.createElement('p');
let h2 = document.createElement('h2');
h2.className = 'mainTitle';
h2.textContent = 'The Heading';
p.textContent = 'Lorem Ipsum';
df.append(h2, p); //now the h2 and paragraph are inside the document fragment

document.body.append(df); //now the h2 and paragraph are on the page
//but the document fragment is still only in memory
//the h2 and paragraph were transfer from memory to the body element.
1
2
3
4
5
6
7
8
9
10
11
12
13

# Dynamic HTML Best Practices

When you are dynamically creating content for your webpages, you will frequently be looping through an Array of Objects to get the content. Each of the objects will have properties with the same property names. You will take the values of those properties and inject them into the HTML that you are creating.

DANGER

Reduce the Number of Inserts Every time you insert new Elements into the webpage you are forcing the browser to repaint the page. It has to rebuild the page.

Imagine if you had a list of 150 song titles that you wanted to add to the page. Each title will be wrapped in a <li>. Seems simple enough, but if you loop through your array and call append() 150 times, then you are forcing the browser to rebuild the page 150 times.

Don't do this.

Call append once, after creating your content in memory.

Let's say that this is your data which you will use to create the new HTML.

let info = [
  { id: 'a6f7', txt: 'Told ya' },
  { id: 'b5f7', txt: 'Not real data' },
  { id: 'c2f7', txt: 'But it looks like it' },
  { id: 'a1f4', txt: 'And works like it' },
  { id: 'a6ee', txt: 'So we will use it' },
];
1
2
3
4
5
6
7

Now, use the Array map method to loop through the Objects and build an HTML String.

When the String is fully built, call one of the methods or properties to inject the content once.

//version one
// use the Element.setHTML() method to append a string that includes HTML
let one = document.querySelector('.one');
let html = info
  .map((item) => {
    let str = `<p data-ref="${item.id}">${item.txt}</p>`;
    return str;
  })
  .join('');
one.setHTML(html);

//version two
// use the innerHTML property to append a string that includes HTML
let two = document.querySelector('.two');
let html2 = info
  .map((item) => {
    let str = `<p data-ref="${item.id}">${item.txt}</p>`;
    return str;
  })
  .join('');
two.innerHTML = html2;

//version three
//use the DOMParser parseFromString method to convert a string to an HTML document
// and then append the document's body property value
let three = document.querySelector('.three');
let html3 = info
  .map((item) => {
    return `<p data-ref="${item.id}">${item.txt}</p>`;
  })
  .join('');
let parser = new DOMParser();
let doc = parser.parseFromString(html3, 'text/html');
three.append(doc.body);

//version four
// use createElement followed by append to create an array of HTML elements
// then use Element.append( ...theArray ) with the spread operator to append the array of Elements
let four = document.querySelector('.four');
let html4 = info.map((item) => {
  let p = document.createElement('p');
  p.append(item.txt);
  p.setAttribute('data-ref', item.id);
  return p;
});
four.append(...html4);
//  four.append( html4[0], html4[1], html4[2], html4[3] );
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

The same process can be done using the createElement method and wrapping everything inside a documentFragment. The documentFragment holds all the new HTML in memory and is used to transport all the new HTML to the page. Once the new HTML has been loaded in the page, the documentFragment removes itself and leaves behind the new HTML.

# DOM style, className, classList

If you want to dynamically style your web page content there are three properties that belong to every Element Node.

  • style
  • className
  • classList

The style property lets you set the value of any single CSS property on any Element. Inside the style property is a list of every CSS property name. However, the names are slightly different. Any CSS property name that includes a hyphen, like background-color, will be written as a single camel-case name - backgroundColor.

Every value is a string and needs quotation marks.

let elem = document.querySelector('p'); //get the first paragraph
elem.style.border = '1px solid red';
elem.style.backgroundColor = 'khaki';
1
2
3

The className property lets you set the contents of the HTML attribute class="". We can't use class as an HTML attribute from within JavaScript, because class is a reserved keyword.

let elem = document.querySelector('p'); //get the first paragraph
elem.className = 'big red content';
1
2

The classList property is a DOMTokenList, which is similar to an Array of all the values inside the HTML class="" attribute. The classList property has a series of methods that we can call to manage and update the list of values.

let elem = document.querySelector('p'); //get the first paragraph
elem.classList.add('active'); // add the class `active` to the HTML
elem.classList.remove('active'); // remove the class `active` to the HTML
elem.classList.toggle('active'); // add the class `active` to the HTML if it doesn't exist, remove if it does.
elem.classList.contains('active'); // check IF the class `active` is assigned to the element
elem.classList.replace('active', 'inactive'); // replace the class `active` with the class 'inactive'
1
2
3
4
5
6

The classList methods are the most efficient way to update your DOM element styling and are considered the best practice over style or className.

# What to do this week

TODO Things to do before next week.

  • Read all the content from Modules 5.1, 5.2, and 6.1.
  • Complete Assignment 1 by end of week.
  • Continue working on the Hybrid Exercises
Last Updated: 5/31/2023, 8:15:38 PM