Author: valentinogagliardi

Translator: Front-end wisdom

Source: making


Everyone said there was no project on your resume, so I found one and gave it away【 Construction tutorial 】.

Document Object Model (DOM)

JS has a lot to tease us about, but it’s not that bad. As a scripting language that runs in a browser, it is very useful for processing Web pages. In this article, we’ll see what methods we have to interact with and modify HTML documents and their elements. But first let’s demystify the Document Object Model.

The document Object Model is a fundamental concept that underlies everything we do in browsers. But what exactly is that? When we visit a Web page, the browser tells us how to interpret each HTML element. This creates a virtual representation of the HTML document and stores it in memory. The HTML page is transformed into a tree structure, with each HTML element becoming a leaf connected to the parent branch. Consider this simple HTML page:

<! DOCTYPE html> <html lang="en"> <head> <title>A super simple title! </title> </head> <body> <h1>A super simple web page! </h1> </body> </htmlCopy the code

When the browser scans the HTML above, it creates a document object model, which is a mirror image of the HTML structure. At the top of this structure is a document, also known as the root element, that contains another element: HTML. The HTML element contains a head, which in turn has a title. Then the body with the h1. Each HTML element is represented by a specific type, also known as an interface, and may contain text or other nested elements

document (HTMLDocument)
  |
  | --> html (HTMLHtmlElement)
          |  
          | --> head (HtmlHeadElement)
          |       |
          |       | --> title (HtmlTitleElement)
          |                | --> text: "A super simple title!"
          |
          | --> body (HtmlBodyElement)
          |       |
          |       | --> h1 (HTMLHeadingElement)
          |              | --> text: "A super simple web page!"
Copy the code

Every HTML Element is derived from Element, but a large number of them are further specialized. We can examine the prototype to find out what “category” the element belongs to. For example, the H1 element is HTMLHeadingElement

Document.quertselector ('h1').__proto__ // output: HTMLHeadingElementCopy the code

HTMLHeadingElement is the descendant of HTMLElement

document.querySelector('h1').__proto__.__proto__

// Output: HTMLElement
Copy the code

Element is a very versatile base class from which all objects under the Document object inherit. This interface describes methods and attributes that are common to all elements of the same kind. Interfaces that inherit from Element and add additional functionality describe specific behavior. For example, the HTMLElement interface is the base interface for all HTML elements, while the SVGElement interface is the base for all SVG elements. Most of the functionality is further defined in the hierarchy interface of the class.

At this point (especially for beginners), there can be some confusion between Document and Window. Window refers to the browser, and document refers to the current HTML page. The Window is a global object that can be accessed directly from any JS code running in the browser. It is not a “native” object of JS, but is exposed by the browser itself. Window has a number of properties and methods, as shown below:

window.alert('Hello world'); // Shows an alert
window.setTimeout(callback, 3000); // Delays execution
window.fetch(someUrl); // makes XHR requests
window.open(); // Opens a new tab
window.location; // Browser location
window.history; // Browser history
window.navigator; // The actual device
window.document; // The current page
Copy the code

Since these attributes are global, we can omit window as well:

alert('Hello world'); // Shows an alert
setTimeout(callback, 3000); // Delays execution
fetch(someUrl); // makes XHR requests
open(); // Opens a new tab
location; // Browser location
history; // Browser history
navigator; // The actual device
document; // The current page
Copy the code

You should already be familiar with some of these methods, such as setTimeout() or window.navigator, which retrieve the language used by the current browser:

if (window.navigator) {
  var lang = window.navigator.language;
  if (lang === "en-US") {
    // show something
  }

  if (lang === "it-IT") {
    // show something else
  }
}
Copy the code

To learn more about methods on Windows, check out the MDN documentation. In the next section, let’s take a closer look at DOM.

Node, element, and DOM operations

The Document interface has a number of useful methods, such as querySelector(), which is used to select any HTML element within the current HTML page:

document.querySelector('h1');
Copy the code

Window represents the browser of the current window. The following instructions are the same as above:

window.document.querySelector('h1');
Copy the code

However, the following syntax is more common, and we’ll use it a lot in the next section:

document.methodName();
Copy the code

In addition to querySelector() for selecting HTML elements, there are many more useful methods

// Return a single element document.getElementById(' Testimonials '); / / return a HTMLCollection document. GetElementsByTagName (" p "); / / returns a nodelist document. QuerySelectorAll (" p ");Copy the code

Not only can we select HTML elements, but we can also interact with and modify their internal state. For example, if you want to read or change the internal contents of a given element:

// Read or write
document.querySelector('h1').innerHtml; // Read
document.querySelector('h1').innerHtml = ''; // Write! Ouch!
Copy the code

Each HTML element in the DOM is also a ** “node” **, and we can actually check the node type like this:

document.querySelector('h1').nodeType;
Copy the code

The above result returns 1, representing the identifier of the node that is of type Element. We can also check the node name:

document.querySelector('h1').nodeName;

"H1"
Copy the code

Here, the node name is returned in uppercase. Typically we deal with four types of nodes in the DOM

  • Document: Root node (nodeType 9)

  • Nodes of type Element: actual HTML tags (nodeType 1), such as

    and

  • Nodes for type attributes: attributes for each HTML element (attributes)

  • Text node: the actual Text content of the element (nodeType 3)

Since elements are nodes, nodes can have properties (also called attributes) that we can examine and manipulate:

// Return true or false document.querySelector('a').hasattribute ('href'); // Return the text content of the attribute, or null document.querySelector('a').getAttribute('href'); // Set the given attribute document.querySelector('a').setattribute ('href', 'someLink');Copy the code

We said earlier that DOM is a tree-like structure. This feature is also reflected in HTML elements. Each element may have parent and child elements, which we can see by examining certain attributes of the element:

// Return an HTMLCollection document.children; // Return a list of nodes document.childNodes; // Returns a node document.querySelector('a').parentNode; // Return the HTML element document.querySelector('a').parentelement;Copy the code

You learned how to select and query HTML elements. What about creating elements? To create a new node of type Element, the native DOM API provides the createElement method:

var heading = document.createElement('h1');
Copy the code

Create a text node with createTextNode:

var text = document.createTextNode('Hello world');
Copy the code

You can combine the two nodes by attaching text to a new HTML element. Finally, you can also attach the heading element to the root document:

var heading = document.createElement('h1');
var text = document.createTextNode('Hello world');
heading.appendChild(text);
document.body.appendChild(heading);
Copy the code

You can also remove nodes from the DOM using the remove() method. Call a method on the element and the node will disappear from the page:

document.querySelector('h1').remove();
Copy the code

That’s all you need to know to start manipulating the DOM with JS in your browser. In the next section, we’ll use DOM flexibly, but first we need to take a detour, because we also need to talk about ** “DOM events” **.

DOM and events

DOM elements are smart. Not only can they contain text and other HTML elements, they can also “emit” and respond to “events.” Browse any web site and open the browser console. Select an element using the following command:

document.querySelector('p')
Copy the code

Look at this property

document.querySelector('p').onclick
Copy the code

What type is it:

typeof document.querySelector('p').onclick // "object"
Copy the code

“object”! Why is it called “onclick”? A little bit intuitively we can imagine that it’s some sort of magical property on the element that responds to clicks, right? Exactly.

If you’re interested, look at the prototype chain for any HTML element. You’ll see that each Element is also an Element, which in turn is a node, which in turn is an EventTarget. You can verify this using Instanceof.

document.querySelector('p') instanceof EventTarget // true
Copy the code

I’d love to call EventTarget the father of all HTML elements, but there’s no real inheritance in JS, it’s more like any HTML element can see the property of another connected object. Therefore, any HTML element has the same property as EventTarget: the ability to publish events.

But what exactly is the event? Take an HTML button. If you click on it, that’s an event. With this.onclick object, we can register events that will run whenever the element is clicked. The function passed to the event is called ** “event listener” or “event handle” **.

Events and Listening

There are three ways to register event listeners in the DOM. The first form is archaic and should be avoided because it couples logical operations with labels

<! <button onclick="console.log('clicked')"> </button>Copy the code

The second option depends on the object named after the event. For example, we can listen for the click event by registering a function on the object.onclick:

document.querySelector("button").onclick = handleClick; function handleClick() { console.log("Clicked!" ); }Copy the code

This syntax is more concise and is a good alternative to inline handlers. There is another modern form based on addEventListener:

document.querySelector("button").addEventListener("click", handleClick); function handleClick() { console.log("Clicked!" ); }Copy the code

Personally, I prefer this form, but if you want maximum browser compatibility, use the.on mode. Now that we have an HTML element and an event listener, let’s take a closer look at DOM events.

Event objects, event defaults, and event bubbling

Each function passed as an event handler receives an object named “event” by default

var button = document.querySelector("button");
button.addEventListener("click", handleClick);

function handleClick() {
  console.log(event);
}
Copy the code

It can be used directly in the function body, but in my code I prefer to declare it explicitly as a parameter:

function handleClick(event) {
  console.log(event);
}
Copy the code

Event objects are a “must have” because we can control the behavior of events by calling methods on them. Events actually have specific characteristics, notably “default” and “bubbling” **. Consider an HTML link. Create a new HTML file called click-event.html with the following tags:

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Click event</title> </head> <body> <div> <a href="/404.html">click me! </a> </div> </body> <script src="click-event.js"></script> </html>Copy the code

Run the file in your browser and try clicking the link. It will jump to a 404 screen. The default behavior of a click event on a link is to go to the actual page specified in the href attribute. But what if I told you there was a way to block the defaults? Type preventDefault(), which is available for the event object. Create a new file named click-event.js with the following code:

var button = document.querySelector("a");
button.addEventListener("click", handleClick);

function handleClick(event) {
  event.preventDefault();
}
Copy the code

Refresh the page in your browser and try clicking the link now: it won’t jump. Because we prevent the browser’s “event default” link from being the only HTML element that isn’t the default action, the form has the same properties.

Another interesting feature occurs when AN HTML element is nested within another element. Consider the following HTML

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Nested events</title> </head> <body> <div id="outer"> I am the outer div <div id="inner"> I am the inner div </div> </div> </body> <script src="nested-events.js"></script> </html>Copy the code

And the following JS code:

// nested-events.js

var outer = document.getElementById('inner');
var inner = document.getElementById('outer');

function handleClick(event){
    console.log(event);
}

inner.addEventListener('click', handleClick);
outer.addEventListener('click', handleClick);
Copy the code

There are two event listeners, one for the external div and one for the internal div. Click on the inner div exactly and you’ll see:

Two event objects are printed. This is the bubbling of events at work. It looks like a bug in the browser behavior and can be disabled using the stopPropagation() method, which is also called on the event object

// function handleClick(event) { event.stopPropagation(); console.log(event); } / / /Copy the code

Although the implementation looks poor, bubbling can be a surprise in cases where registering too many event listeners is really bad for performance. Consider the following example:

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Event bubbling</title> </head> <body> <ul> <li>one</li> <li>two</li> <li>three</li> <li>four</li> <li>five</li> </ul> </body> <script src="event-bubbling.js"></script> </html>Copy the code

How many event listeners do I need to register in the list if I want to listen to click events in the list concurrently? The answer is: one. All you need is a listener registered with ul to intercept all clicks on any LI:

// event-bubbling.js

var ul = document.getElementsByTagName("ul")[0];

function handleClick(event) {
  console.log(event);
}

ul.addEventListener("click", handleClick);
Copy the code

As you can see, event bubbling is a practical way to improve performance. In practice, registering event listeners is an expensive operation for browsers, and can result in a performance penalty in the case of large element lists.

Generate tables with JS

Now let’s start coding. Given an array of objects, you want to dynamically generate an HTML table. HTML tables are represented by

elements. Each table can also have a header, represented by the

element. The header can have one or more rows

, each with a cell represented by a

element. As follows:
<table> <thead> <tr> <th>name</th> <th>height</th> <th>place</th> </tr> </thead> <! -- more stuff here! --> </table>Copy the code

More than that, in most cases, each table has a body, defined by , which in turn contains a set of rows . Each row can have cells containing actual data. Form cells are defined by < TD >. Complete as follows:

<table>
    <thead>
    <tr>
        <th>name</th>
        <th>height</th>
        <th>place</th>
    </tr>
    </thead>
    <tbody>
    <tr>
        <td>Monte Falco</td>
        <td>1658</td>
        <td>Parco Foreste Casentinesi</td>
    </tr>
    <tr>
        <td>Monte Falterona</td>
        <td>1654</td>
        <td>Parco Foreste Casentinesi</td>
    </tr>
    </tbody>
</table>
Copy the code

The task now is to generate a table from an array of JS objects. First, create a new file called build-table.html with the following contents:

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Build a table</title> </head> <body> <table> <! -- here goes our data! --> </table> </body> <script src="build-table.js"></script> </html>Copy the code

Create another file called build-table.js in the same folder and start with the following array:

"use strict";

var mountains = [
  { name: "Monte Falco", height: 1658, place: "Parco Foreste Casentinesi" },
  { name: "Monte Falterona", height: 1654, place: "Parco Foreste Casentinesi" },
  { name: "Poggio Scali", height: 1520, place: "Parco Foreste Casentinesi" },
  { name: "Pratomagno", height: 1592, place: "Parco Foreste Casentinesi" },
  { name: "Monte Amiata", height: 1738, place: "Siena" }
];
Copy the code

Consider this table. First, we need a :

document.createElement('thead')
Copy the code

There’s nothing wrong with that, but a closer look at the MDN table documentation reveals an interesting detail.

is an HTMLTableElement that also contains interesting methods. . One of the most useful is HTMLTableElement createTHead (), it can help we need to create the < thead >.

First, write a function generateTableHead that generates the thead tag

function generateTableHead(table) {
  var thead = table.createTHead();
}
Copy the code

This function takes a selector and creates a on the given table:

function generateTableHead(table) {
  var thead = table.createTHead();
}

var table = document.querySelector("table");

generateTableHead(table);
Copy the code

Open build-table. HTML in your browser: nothing. However, if you open the browser console, you can see a new attached to the table.

Next, populate the header content. Start by creating a row in it. There is another method can help: HTMLTableElement. The insertRow (). With this, we can extend the method:

function generateTableHead (table) {
  var thead = table,createThead();
  var row = thead.insertRow();
}
Copy the code

At this point, we can generate our row. By looking at the source array, we can see that any object in it has the information we need:

var mountains = [
  { name: "Monte Falco", height: 1658, place: "Parco Foreste Casentinesi" },
  { name: "Monte Falterona", height: 1654, place: "Parco Foreste Casentinesi" },
  { name: "Poggio Scali", height: 1520, place: "Parco Foreste Casentinesi" },
  { name: "Pratomagno", height: 1592, place: "Parco Foreste Casentinesi" },
  { name: "Monte Amiata", height: 1738, place: "Siena" }
];
Copy the code

This means we can pass another argument to our function: an array to iterate over to generate the header cells:

function generateTableHead(table, data) { var thead = table.createTHead(); var row = thead.insertRow(); for (var i = 0; i < data.length; i++) { var th = document.createElement("th"); var text = document.createTextNode(data[i]); th.appendChild(text); row.appendChild(th); }}Copy the code

Unfortunately, there is no native way to create cells, so resort to document.createElement(“th”). Also note that document.createTextNode(data[I]) is used to create text nodes and appendChild() is used to add new elements to each tag.

When elements are created and manipulated in this way, we call it “imperative” DOM manipulation. Modern front-end libraries solve this problem by supporting a “declarative” approach. Instead of ordering the browser step by step, we can declare which HTML elements we want, and the library takes care of the rest.

Going back to our code, we can use the first function as follows

var table = document.querySelector("table");
var data = Object.keys(mountains[0]);
generateTableHead(table, data);
Copy the code

Now we can go further and generate the data for the actual table. The next function will implement a similar logic to generateTableHead, but this time we need two nested for loops. In the innermost loop, another native method is used to create a series of TDS. Method is HTMLTableRowElement insertCell (). Add another function called generateTable to the file you created earlier

function generateTable(table, data) { for (var i = 0; i < data.length; i++) { var row = table.insertRow(); for (var key in data[i]) { var cell = row.insertCell(); var text = document.createTextNode(data[i][key]); cell.appendChild(text); }}}Copy the code

Call the above function, passing the HTML table and array of objects as arguments:

generateTable(table, mountains);
Copy the code

Let’s dig into the logic of generateTable. The data parameter is an array that corresponds to mountains. The outermost for loop iterates through the array and creates a row for each element:

function generateTable(table, data) {
  for (var i = 0; i < data.length; i++) {
    var row = table.insertRow();
    // omitted for brevity
  }
}
Copy the code

The innermost loop iterates through each key of any given object and creates a text node containing the key value for each object

function generateTable(table, data) { for (var i = 0; i < data.length; i++) { var row = table.insertRow(); for (var key in data[i]) { // inner loop var cell = row.insertCell(); var text = document.createTextNode(data[i][key]); cell.appendChild(text); }}}Copy the code

Final code:

var mountains = [ { name: "Monte Falco", height: 1658, place: "Parco Foreste Casentinesi" }, { name: "Monte Falterona", height: 1654, place: "Parco Foreste Casentinesi" }, { name: "Poggio Scali", height: 1520, place: "Parco Foreste Casentinesi" }, { name: "Pratomagno", height: 1592, place: "Parco Foreste Casentinesi" }, { name: "Monte Amiata", height: 1738, place: "Siena" } ]; function generateTableHead(table, data) { var thead = table.createTHead(); var row = thead.insertRow(); for (var i = 0; i < data.length; i++) { var th = document.createElement("th"); var text = document.createTextNode(data[i]); th.appendChild(text); row.appendChild(th); } } function generateTable(table, data) { for (var i = 0; i < data.length; i++) { var row = table.insertRow(); for (var key in data[i]) { var cell = row.insertCell(); var text = document.createTextNode(data[i][key]); cell.appendChild(text); }}}Copy the code

Where:

var table = document.querySelector("table");
var data = Object.keys(mountains[0]);
generateTable(table, mountains);
generateTableHead(table, data);
Copy the code

Execution Result:

Of course, our method can also be advanced, the next chapter will be introduced.

conclusion

The DOM is a virtual copy of a Web page that the Web browser keeps in memory. DOM manipulation refers to the creation, modification, and deletion of HTML elements from the DOM. In the past, we used to rely on jQuery for simpler tasks, but now the native API has matured enough to make jQuery obsolete. JQuery, on the other hand, isn’t going away anytime soon, but every JS developer must know how to manipulate the DOM using the native API.

There are a number of reasons for doing this, additional libraries increase load times and the size of JS applications. Not to mention DOM manipulation comes up a lot in interviews.

Each available HTML element in the DOM has an interface that exposes a number of attributes and methods. When in doubt about which method to use, refer to the MDN documentation. The most common ways to manipulate the DOM are document.createElement() for creating new HTML elements and document.createTextNode() for creating text nodes in the DOM. Last but not least is.appendChild (), which is used to attach a new HTML element or text node to an existing element.

HTML elements can also emit events, also known as DOM events. Notable events are “click,” “submit,” “Drag,” “drop,” and so on. DOM events have some special behaviors, such as “default” and bubbling.

JS developers can take advantage of these properties, especially for event bubbling, which are useful for speeding up event processing in the DOM. While a good understanding of native apis is a good thing, modern front-end libraries offer undeniable benefits. It is possible to build large JS applications with Angular, React, and Vue.

The bugs that may exist after code deployment cannot be known in real time. In order to solve these bugs, I spent a lot of time on log debugging. Incidentally, I recommend a good BUG monitoring tool for youFundebug.

**github.com/valentinoga…

communication

This article is updated every week, you can search wechat “big move the world” for the first time to read and urge more (one or two earlier than the blog hey), this article GitHub github.com/qq449245884… It has been included and sorted out a lot of my documents. Welcome Star and perfect. You can refer to the examination points for review in the interview.