Files
UiQuestions/docs/element-properties.mdx
T

204 lines
6.1 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: element-properties
title: Element attributes and properties
---
import Admonition from "../components/Admonition"
import Accordion from "../components/Accordion"
import InternalLink from "../components/InternalLink"
import MDN from "../components/MDNBadge"
### 1. tagName
`Element.tagName : string`
`.tagName` is a read-only property on any element, which returns the tag name in uppercase form. This is useful to identify type of a element.
Ex. For a `<div/>` element, tagName is `"DIV"`.
```js
ele = document.querySelector("img");
ele.tagName // "IMG"
```
:::note Text nodes don't have `tagName`
Text nodes are not element, so they don't have any tagName. To identify the type of node, use `.nodeType` property. For text nodes, `nodeType` is `3` - same as the constant `Node.TEXT_NODE`.
:::
<span>
<MDN title="tagName" i url="https://developer.mozilla.org/en-US/docs/Web/API/Element/tagName" />&nbsp;
<MDN title="nodeType" i url="https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType" />&nbsp;
</span>
## Children
:::note Types of Children
All elements will *generally* have 2 types of children - 1. element and 2. text node.
You can think of elements as other tags and text node as the text within a tag.
```jsx
<div>
This is // <- 1. text node
<span> // <- 2. element
some text // 2.1. <span> contains text node
</span>
</div>
```
If you recall, *Element* extends from *Node* - so all elements are nodes. That means, every child is technically a node.
This is important because some properties return **only child elements**, whereas others return **all child nodes** (both element and text node). For ex, `ele.childNodes` returns all child nodes, but `ele.children` returns only child elements.
**Always use the one which is more suitable for your case**. Remember, even if you haven't added any "text" between a tag, it can still have **invisible text nodes** due to spaces and newline characters.
```jsx
<div id="container">
<div></div>
</div>
container.childNodes // [text, div, text]
// extra text nodes contain newline and space
```
Read more about [other types of nodes](https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType) and how [whitespace works in html](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Whitespace).
:::
### 2. children
`Element.children: HTMLCollection`
This read-only property returns a live `HTMLCollection` containing all the **child elements** of the node. If there are no child elements, it returns empty HTMLCollection.
This property is available on `Document`, `Element`, and `DocumentFragment`, but not on text node.
`HTMLCollection` can be consumed easily by converting it to an array, using `Array.from()` or `[...ele.children]`. HTMLCollection is also a iterable and has a `.length` property.
Here, the HTMLCollection is **live** - meaning if new child elements are added, they will automatically appear in this collection.
```jsx
<div id="parent">
Some text
<span>some element</span>
</div>
const children = parent.children;
children // HTMLCollection [span]
// 1. For-loop using .length
for (let i = 0; i < children.length; i++) {
console.log(children[i].tagName);
}
// 2. Convert to array
[...children].forEach(ele => {
// do something
})
```
<MDN title="children" url="https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/children" />
### 3. childNodes
`Element.childNodes: NodeList`
This read-only property returns a live `NodeList` containing all the **child nodes** of the current element, including text nodes. If there are no child nodes, it returns a empty NodeList.
This property is available on all types of `Node` - like Element, text node, etc.
`NodeList` can be converted to an array easily, using `Array.from()` or `[...ele.children]`. NodeList is also a iterable and has a `.length` property.
Here, the NodeList is **live**.
```jsx
<div id="parent">
Some text
<span>some element</span>
and more text
</div>
const childNodes = parent.childNodes;
childNodes // NodeList [text, span, text]
[...childNodes].forEach(node => {
// do something
})
```
<MDN title="childNodes" url="https://developer.mozilla.org/en-US/docs/Web/API/Node/childNodes" />
### 4. firstChild + nextSibling
`Element.firstChild: Node`
This property returns the element's **first child** node in the tree. If the node has no children, then it returns `null`.
`Element.nextSibling: Node`
This property returns the node immediately **after the current one** in their parent's childNodes.
```jsx
<div id="A">
<div id="B"/>
<div id="C"/>
</div>
var eleA = document.getElementByID("A")
var eleB = eleA.firstChild // <div id="B">
var eleC = eleB.nextSibling // <div id="C"/>
eleC.nextSibling // null
```
There is also a similar `previousSibling` property, which returns the previous node. Also, there are element-only variant for all these properties like `firstElementChild`, `nextElementSibling`, etc.
<Accordion title="Why is this useful?">
:::note Easier traversal
This allows accessing the DOM tree in [Left-child right-sibling](https://en.wikipedia.org/wiki/Left-child_right-sibling_binary_tree) (LCRS) form, which is more memory efficient for usual traversal.
#### How do you implement **dom traversal**?
The typical approach is to use a queue, push all children to the queue, pop them one-by-one and visit them. This requires **extra memory** because of the queue data structure which holds all the *next items to visit*.
But with the LCRS form, you always have access to the next item **without using extra space**. If current element has child, visit `.firstChild`. Then visit next node using `.nextSibling`.
```js
// 1. Typical approach
var queue = [root]
while (queue.length > 0) {
const item = queue.shift();
console.log(item);
if (item.hasChildNodes()) {
queue = Array.from(item.childNodes).concat(queue)
}
}
// 2. LCRS approach
function traverse(item) {
if (item === null) {
return;
}
console.log(item);
traverse(item.firstChild)
traverse(item.nextSibling)
}
traverse(root.firstChild)
```
:::
</Accordion>
<!-- ## Content - html, text
## Traversal (and ientification)
## Attribute, class, id, data
## Scroll -->