Selectors API
querySelector()
The querySelector() method takes the CSS selector argument and returns the first descendant element that matches the pattern, or null if there is no match. Here are some examples:
// Get the element
let body = document.querySelector("body");
// get the element with ID "myDiv"
let myDiv = document.querySelector("#myDiv");
// Get the first element of class named "selected"
let selected = document.querySelector(".selected");
// Get the image of class "button"
let img = document.body.querySelector("img.button");
Copy the code
When you use the querySelector() method on Document, you start the search from the Document element; When the querySelector() method is used on Element, only descendants of the current Element are queried.
querySelectorAll()
The querySelectorAll() method, like querySelector(), takes an argument to use in the query, but it returns all matching nodes instead of just one. This method returns a static instance of NodeList.
Again, the NodeList instance returned by querySelectorAll() is full of attributes and methods, but it is a static “snapshot” rather than a “live” query. Such an underlying implementation avoids the performance problems that might be caused by using NodeList objects.
usage
// Get all elements in the
element with ID "myDiv"
let ems = document.getElementById("myDiv").querySelectorAll("em");
// Get all elements whose class names contain "selected"
let selecteds = document.querySelectorAll(".selected");
// Get all elements that are children of the element
let strongs = document.querySelectorAll("p strong");
Copy the code
The returned NodeList object can retrieve individual elements through a for-of loop, the item() method, or the bracketed syntax.
matches()
The matches() method (called matchesSelector() in the draft specification) takes a CSS selector argument that returns true if the elements match and false otherwise.
Usage Scenarios:
This method makes it easy to detect whether an element will be returned by querySelector() or querySelectorAll().
Matches () is supported by all major browsers. Edge, Chrome, Firefox, Safari, and Opera are fully supported. IE9 to 11 and some mobile browsers support prefixed methods.
Element traversal
Previous versions of IE9 did not treat Spaces between elements as blank nodes, as other browsers do. This results in a difference in attributes such as childNodes and firstChild. To bridge this gap without affecting the DOM specification, the W3C has defined a new set of attributes through the new Element Traversal specification.
The Element Traversal API adds five attributes to the DOM Element:
- ChildElementCount, which returns the number of child elements (excluding text nodes and comments);
- FirstElementChild, pointing to the child of the firstElement type (Element version firstChild);
- LastElementChild, which refers to the lastElementChild (Element version lastChild);
- PreviousElementSibling refers to the sibling of the previousElement type (Element version previousSibling);
- NextElementSibling, points to the sibling of the latter Element type (Element version nextSibling).
In supported browsers, all DOM elements have these attributes to facilitate traversal of DOM elements. This way developers don’t have to worry about blank text nodes.
For example, in the past, to traverse all children of a particular element in a cross-browser manner, the code would look something like this:
let parentElement = document.getElementById('parent');
let currentChildNode = parentElement.firstChild;
// There are no children, firstChild returns null, skip the loop
while (currentChildNode) {
if (currentChildNode.nodeType === 1) {
// If there are element nodes, do the corresponding processing
processChild(currentChildNode);
}
if (currentChildNode === parentElement.lastChild) {
break;
}
currentChildNode = currentChildNode.nextSibling;
}
Copy the code
Using the Element Traversal property, this code can be simplified as follows:
let parentElement = document.getElementById('parent');
let currentChildElement = parentElement.firstElementChild;
// There are no child elements, firstElementChild returns null, skipping the loop
while (currentChildElement) {
// This is the element node
processChild(currentChildElement);
if (currentChildElement === parentElement.lastElementChild) {
break;
}
currentChildElement = currentChildElement.nextElementSibling;
}
Copy the code
The Element Traversal attribute is supported in IE9 and above, and in all modern browsers.
HTML5
The CSS class extensions
getElementsByClassName()
GetElementsByClassName () is one of HTML5’s most popular new methods, exposed on the Document object and all HTML elements. This approach is derived from JavaScript libraries that implement this functionality based on the original DOM features, providing a high performance native implementation.
The getElementsByClassName() method takes an argument, a string containing one or more class names, and returns the NodeList containing the elements of the corresponding class in the class name. If multiple class names are provided, the order does not matter.
Here are a few examples:
// Get all class names containing "username" and "current" elements
// The order of the two class names does not matter
let allCurrentUsernames = document.getElementsByClassName("username current");
// Get all elements in the element subtree with ID "myDiv" that contain the "selected" class
let selected = document.getElementById("myDiv").getElementsByClassName("selected");
Copy the code
The getElementsByClassName() method is supported in IE9 and above, and in all modern browsers.
ClassList properties
ClassName is a string, so this value needs to be reset after each operation to take effect, even if only part of the string is changed.
Take the following HTML code for example:
<div class="bd user disabled">... </div>Copy the code
this
// Delete the "user" class
let targetClass = "user";
// Split the class name into arrays
let classNames = div.className.split(/\s+/);
// Find the index to drop the class name
let idx = classNames.indexOf(targetClass);
// If yes, delete it
if (idx > -1) {
classNames.splice(i,1);
}
// Reset the class name
div.className = classNames.join("");
Copy the code
This is from
HTML5 provides a simpler and safer way to do this by adding classList attributes to all elements.
ClassList is an instance of a new collection type, DOMTokenList. Like other DOM collection types, DOMTokenList has a length attribute to indicate how many items it contains, and individual elements can also be retrieved through item() or brackets.
In addition, DOMTokenList adds the following methods.
- add (value), adds the specified string value value to the class list. If the value already exists, do nothing.
- contains (value), returns a Boolean value indicating whether the given value exists.
- remove (value), removes the specified string value value from the class list.
- toggle (value), if the specified value already exists in the class list, delete it; If it does not exist, add it.
Thus, many lines of code in the previous example can be reduced to the following line:
div.classList.remove("user");
Copy the code
This line of code can do this without affecting the other class names. Other methods also greatly simplify the complexity of operation class names, as shown in the following example:
// Delete the "disabled" class
div.classList.remove("disabled");
// Add the "current" class
div.classList.add("current");
// Switch the "user" class
div.classList.toggle("user");
// Check the class name
if (div.classList.contains("bd") && !div.classList.contains("disabled")) {// Perform the operation
)
// The name of the iteration class
for (let class of div.classList){
doStuff(class);
}
Copy the code
After adding the classList attribute, the className attribute is not used unless the element’s class attribute is completely removed or overridden. IE10 and later (partially) and other major browsers (fully) implement the classList attribute.
The focus of management
HTML5 adds features to aid DOM focus management. The first is document.ActiveElement, which always contains the DOM element that currently has focus. When a page loads, an element can be brought into focus automatically by user input (pressing Tab or using the focus() method in your code). Such as:
let button = document.getElementById("myButton");
button.focus();
console.log(document.activeElement === button); // true
Copy the code
By default, Document.ActiveElement is set to Document.body just after the page loads. The value of Document.ActiveElement is null before the page is fully loaded.
The second is the document.hasfocus () method, which returns a Boolean indicating whether the document hasFocus:
let button = document.getElementById("myButton");
button.focus();
console.log(document.hasFocus()); // true
Copy the code
The first method can be used to query the document to determine which element has focus, and the second method can be used to query whether the document has focus, which is important for making Web applications accessible. An important aspect of a frictionless Web application is focus management, and being able to determine which element currently has focus is a big step forward from previous guesses.
HTMLDocumentextension
ReadyState attribute
ReadyState was one of the first attributes added to the Document object in IE4, and other browsers have since followed suit. Eventually, HTML5 wrote this property into the standard. The document.readyState property has two possible values:
- loading: indicates that a document is being loaded.
- complete: the document is loaded successfully.
In practical development, it is best to use document.readState as an indicator to see if the document has finished loading. Before this property became widely supported, you typically relied on the onload event handler to set a flag indicating that the document was finished loading. The basic usage of this property is as follows:
if (document.readyState == "complete") {// Perform the operation
}
Copy the code
CompatMode properties
Effect: Detect page rendering mode
In standard mode, the value of document.compatMode is “CSS1Compat”; in promiscuous mode, the value of document.compatMode is “BackCompat” :
if (document.compatMode == "CSS1Compat") {console.log("Standards mode");
} else {
console.log("Quirks mode");
}
Copy the code
HTML5 is finally standardizing implementations of the compatMode attribute as well.
The head properties
As a complement to document.body (the element that points to the document), HTML5 adds the document.head attribute, which points to the element of the document. We can get elements directly like this:
let head = document.head;
Copy the code
Character set attributes
HTML5 has added several new attributes related to the document character set. The characterSet attribute represents the actual characterSet used by the document, and can also be used to specify a new characterSet. The default value for this attribute is “UTF-16”, but can be changed by the element or response header, as well as the new characterSeet attribute. Here’s an example:
console.log(document.characterSet); // "UTF-16"
document.characterSet = "UTF-8";
Copy the code
Custom data attributes
HTML5 allows you to assign non-standard attributes to elements, but with the prefix data- to tell the browser that these attributes contain neither rendering-related nor semantic information about the element. There is no restriction on the naming of custom attributes except prefixes, data-, or anything else. Here’s an example:
<div id="myDiv" data-appId="12345" data-myname="Nicholas"></div>
Copy the code
After a custom data attribute is defined, it can be accessed through the dataset attribute of the element. The dataset attribute is an instance of DOMStringMap that contains aset of key/value pair mappings. Each data-name attribute of an element in a dataset can be accessed by the string following data- as a key (for example, the attributes data-myname and data-myname can be accessed by myname, Note that data-my-name and data-my-name are accessed by myName. Here is an example of using custom data attributes:
// The method used in this example is for demonstration purposes only
let div = document.getElementById("myDiv");
// Get the value of the custom data attribute
let appId = div.dataset.appId;
let myName = div.dataset.myname;
// Sets the value of the custom data attribute
div.dataset.appId = 23456;
div.dataset.myname = "Michael";
// Do you have "myname"?
if (div.dataset.myname){
console.log(`Hello, ${div.dataset.myname}`);
}
Copy the code
Custom data attributes are great for scenarios where you need to attach some data to elements, such as link tracing and identifying different parts of a page in an aggregated application. In addition, single-page application frameworks make heavy use of custom data attributes.
Insert tags
InnerHTML attribute
When you read the innerHTML attribute, the HTML string for all the descendants of the element is returned, including the element, comment, and text node. When the innerHTML is written, all the nodes in the element are replaced with a new DOM subtree based on the supplied string value. For example, the following HTML code:
<div id="content">
<p>This is a <strong>paragraph</strong> with a list following it.</p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
Copy the code
For this
<p>This is a <strong>paragraph</strong> with a list following it.</p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
Copy the code
The actual text returned varies from browser to browser. IE and Opera convert all element labels to uppercase, while Safari, Chrome, and Firefox return the document’s source format, including Spaces and indentation. So don’t expect the innerHTML from different browsers to return exactly the same value.
In write mode, the value assigned to the innerHTML attribute is parsed into a DOM subtree, replacing all nodes before the element. Because the assigned value defaults to HTML, all tags in it are converted to elements in the same way that the browser handles HTML. If the assignment does not contain any HTML tags, a text node is generated directly, as shown below:
div.innerHTML = "Hello world!";
Copy the code
Because the browser parses the value, setting innerHTML to a string that contains the HTML makes a big difference. Consider the following example:
div.innerHTML = "Hello & welcome, \"reader\"! ";
Copy the code
The result of this operation is equivalent to:
<div id="content">Hello & welcome, <b>"reader"!</b></div>
Copy the code
Note that setting innerHTML causes the browser to parse the HTML string into the corresponding DOM tree. This means that setting the innerHTML property immediately after reading it will give you a different string. This is because the string returned is the result of serializing the DOM subtree corresponding to the original string.
InnerHTML in old IE
In all modern browsers, it’s inserted with innerHTML
In IE8 and previous versions, you only need to insert like this
/ / it won't work
div.innerHTML = ";
Copy the code
// All of the following works
div.innerHTML = "_;
div.innerHTML = "
;
div.innerHTML = ";
Copy the code
To understand, IE play with yourself!
OuterHTML properties
This is basically the same as innerHTML, except that the original container element is replaced;
With insertAdjacentText insertAdjacentHTML () ()
The last two new methods for inserting tags are insertAdjacentHTML() and insertAdjacentText(). These two methods originated in Internet Explorer
They both accept two parameters: the location of the tag to be inserted and the HTML or text to be inserted.
- “beforebegin”, inserts before the current element as the previous sibling node;
- “afterbegin”, inserted inside the current element as a new child or placed before the first child;
- “beforeend”, inserts inside the current element as a new child or after the last child;
- “afterend”, inserted after the current element as the next sibling node.
The second parameter is parsed either as an HTML string (the same as innerHTML and outerHTML) or as plain text (the same as innerText and outerText). If it is HTML, an error is thrown if parsing fails.
The basic usage is shown below
// insert as the previous sibling node
element.insertAdjacentHTML("beforebegin"."Hello world!
");
element.insertAdjacentText("beforebegin"."Hello world!");
// inserted as the first child
element.insertAdjacentHTML("afterbegin"."Hello world!
");
element.insertAdjacentText("afterbegin"."Hello world!");
// insert as the last child node
element.insertAdjacentHTML("beforeend"."Hello world!
");
element.insertAdjacentText("beforeend"."Hello world!");
// insert as the next sibling node
element.insertAdjacentHTML("afterend"."Hello world!
"); element.
insertAdjacentText("afterend"."Hello world!");
Copy the code
Memory and performance issues
Replacing child nodes with the method described in this section can cause memory problems in browsers, especially Internet Explorer. For example, if the subtree element is removed from which there were previously associated event handlers or other JavaScript objects (as attributes of the element), the binding relationship between them will remain in memory. If this substitution occurs frequently, the memory footprint of the page will continue to climb. Before using innerHTML, outerHTML, and insertAdjacentHTML(), it’s a good idea to manually remove the event handlers and JavaScript objects associated with the element to be replaced.
There are certainly advantages to using these attributes, especially innerHTML. In general, it’s much easier to insert a lot of new HTML using innerHTML than using multiple DOM operations to create nodes and then insert them. This is because the HTML parser parses the value set to innerHTML (or outerHTML). Parsers are low-level code (usually C++ code) in the browser and are much faster than JavaScript.
However, building and deconstructing HTML parsers is not without costs, so it’s best to limit the number of times you can use innerHTML and outerHTML.
For example, the following code uses innerHTML to create list items:
for (let value of values){
ul.innerHTML += '<li>${value}</li>'; // Don't do that!
}
Copy the code
This code is inefficient because the innerHTML is set once per iteration. Not only that, but each loop also reads the innerHTML first, meaning that the loop accesses the innerHTML twice. To do this, it’s best to loop through building a single string and then assign the resulting string to innerHTML once, as in:
let itemsHtml = "";
for (let value of values){
itemsHtml += '<li>${value}</li>';
}
ul.innerHTML = itemsHtml;
Copy the code
This is much more efficient because there is only one assignment to innerHTML. when
Cross-site scripting
Even though innerHTML doesn’t execute its own creation
You are not advised to use innerHTML if you are using user-supplied information in a page. Preventing XSS attacks is more of a headache than the convenience you get with innerHTML. It is best to escape them.
scrollIntoView()
One problem not covered in the DOM specification is how to scroll an area of the page. To fill in the gaps, different browsers implement different ways to control scrolling. Of all these proprietary methods, HTML5 chooses to standardize scrollIntoView().
The scrollIntoView() method exists on all HTML elements and can scroll the browser window or container element to include the element into the viewport. This method takes the following parameters:
AlignToTop is a Boolean value.
- True: Align the top of the element with the top of the viewport after the window scrolls.
- False: Align the bottom of the element with the bottom of the viewport after the window scrolls.
ScrollIntoViewOptions is an option object.
- Behavior: Defines transition animations with desirable values “smooth” and “auto”, default “auto”.
- Block: Defines vertical alignment. Desirable values are “start”, “center”, “end”, and “nearest”. Default is “start”.
- Inline: Defines horizontal alignment with desirable values of “start”, “center”, “end” and “nearest”, default “nearest”.
Not passing an argument is equivalent to alignToTop being true.
Often use the menu bar scrolling operation, chat messages back to the bottom and other scenarios;
Here’s an example:
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="Width = device - width, initial - scale = 1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<style>
ul {
height: 200px;
overflow-y: scroll;
position: relative;
border:1px solid pink;
}
li {
height: 30px;
line-height: 30px;
}
</style>
<body>
<ul>
<li>111</li>
<li>112</li>
<li>113</li>
<li>114</li>
<li>151</li>
<li>114541</li>
<li>11451</li>
<li>141411</li>
<li>11453411</li>
<li>112811</li>
<li>1124231</li>
<li>11446491</li>
<li>1233</li>
<li>111231231</li>
<li>11145645</li>
</ul>
</body>
<script>
(function () {
var li = document.querySelectorAll("li");
var ul = document.querySelector("ul");
var height = parseInt(getComputedStyle(document.querySelector("ul")).height);
li.forEach(item= > {
item.onclick = function () {
this.style = "color:red"
But if dad and Grandpa both have scroll bars, they will move
this.scrollIntoView({behavior: 'smooth'.block: 'center'});
/* 1. ScrollTop < 0 is fixed; 2. Li's offsetTop is based on dom and will not change. So if scollTop is equal to offsetTop, then the offsetTop will be at the top
// let top = this.offsetTop;
// console.log('top', top);
// ul.scrollTop = top - height / 2;}})}) ()</script>
</html>
Copy the code
Proprietary extensions
childrenattribute
Why it’s here: Differences in the way that previous versions of IE9 handled blank text nodes compared to other browsers resulted in the children property.
The Children property is an HTMLCollection that contains only the Element’s Element type children. If the element’s child node types are all element types, children and childNodes should contain the same nodes.
The contains () method
DOM programming often involves determining whether an element is a descendant of another element. Internet Explorer first introduced the Contains () method, which lets developers retrieve this information without traversing the DOM. The contains() method should be called on the ancestor element to search for, taking the target node to be determined.
Contains () returns true if the target node is a descendant of the node being searched, false otherwise.
Here’s an example:
console.log(document.documentElement.contains(document.body)); // true
Copy the code
In addition, relationships between nodes can be determined using the compareDocumentPosition() method of DOM Level 3. This method returns a bitmask representing the relationship between the two nodes.
To mimic the contains() method, you use a mask of 16 (0x10). The result of the compareDocumentPosition() method can be used to determine whether the reference node contains the passed node by bitting and,
let result = document.documentElement.compareDocumentPosition(document.body);
console.log(!! (result &0x10));
Copy the code
The value of result after the above code executes is 20 (or 0x14, where 0x4 means “followed”, plus 0x10 is “included”). Applying bitwise to result and 0x10 returns a non-zero value, and the two exclamation points convert this value to the corresponding Boolean value.
Internet Explorer 9 and later, as well as all modern browsers, support the contains() and compareDocumentPosition() methods.
Insert tags
HTML5 includes innerHTML and outerHTML, created by Internet Explorer, but there are two other attributes that don’t make the cut. The two remaining properties are innerText and outerText.
The innerText attribute
The innerText attribute corresponds to all the text content contained in the element, regardless of the level in the subtree. When used to read values, innerText concatenates the values of all the text nodes in the subtree in depth-first order.
Take a look at the following HTML code:
<div id="content">
<p>This is a <strong>paragraph</strong> with a list following it.</p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
Copy the code
In this case
This is a paragraph with a list following it.
Item 1
Item 2
Item 3
Copy the code
Note that different browsers treat whitespace differently, so the formatted string may or may not contain indentation in the original HTML code.
Setting the innerText removes all descendant nodes before the element, completely changing the DOM subtree. In addition, setting innerText encodes HTML syntax characters (less-than, greater-than, quotes, and ampersand) that appear in the string. Here’s an example:
div.innerText = "Hello & welcome, \"reader\"! ";
Copy the code
The result is as follows:
<div id="content">Hello & welcome, < b> " reader" ! </b> </div>
Copy the code
Because setting innerText can only generate one text node in the container element, HTML encoding is required to ensure that it is a text node. The innerText property can be used to remove HTML tags.
An interesting topic, with all the HTML tags removed, could be written like this:
div.innerText = div.innerText;
Copy the code
After executing the above code, the contents of the container element will only contain the original text content.
OuterText properties
OuterText is similar to innerText, except that the scope includes the node that calls it.
OuterText is a non-standard property and has no prospect of being standardized. Therefore, relying on this attribute for important operations is not recommended. OuterText is supported by all major browsers except Firefox.
rolling
As mentioned earlier, scrolling is an area not covered by the DOM standard prior to HTML5. While HTML5 standardizes scrollIntoView(), there are still other proprietary methods in different browsers. For example, scrollIntoViewIfNeeded() can be called on all elements as an extension of type HTMLElement.
Consider that scrollIntoView() is the only method supported by all browsers, so it’s ok to use it alone.