DOM manipulation in mind a few tips

Use attributes through the elements characteristic property

// 迭代元素的每一个特性,将它们构造成 name = value 的字符串形式
function outputAttributes (element) {
  const pairs = []
  let attrName
  let attrValue

  for (let i = 0, len = element.attributes.length; i < len; i++) {
    attrName = element.attributes[i].nodeName
    attrValue = element.attributes[i].nodeValue
    pairs.push(`${attrName}=${attrValue}`)
  }
  return pairs.join(" ")
}

To operate using the class name attribute classList

<div class="bd user disabled">...</div>

The <div> element, a total of three class name. To remove from a class name, class name requires these three apart, delete the unwanted, then put the other class name makes up a new string. Consider the following example:

// div.className = 'bd user disabled'
let classNames = div.className.split(/\s+/)
let pos = -1
for (let i = 0, len = classNames.length; i < len; i++) {
  if (classNames[i] === 'user') {
    pos = i
    break
  }
}

classNames.splice(pos, 1) // 删除类名
div.className = classNames.join(' ') // 把剩下的类名拼接成字符串并重新设置

HTML5, a new way of operating the class name, you can make the operation easier and more secure, and that is to add classList attribute for all elements of this new type defines the following methods:

  • add (value): value given string is added to the list. If the value already exists, do not add up.
  • contains (value): Indicates whether a given value exists in the list, returns true if it exists, otherwise false.
  • remove (value): removed from the list given string.
  • toggle (value): If the given value in the list already exists, delete it; if not in the list given value, add it.

Thus, in front of so many lines of code with the following line of code can be replaced

div.classList.remove("user")

Element Traversal API use node operating element

In the past, to traverse all the child elements of an element, you need to write code like this:

let child = element.firstChild
while (child !== element.lastChild) {
  if (child.nodeType === 1) { // 检查是否为元素节点
    processChild(child)
  }
  child = child.nextSibling
}

Element Traversal API adds the following five attributes DOM element:

  • childElementCount: Returns the child elements (not including text and annotation nodes) number.
  • firstElementChild: points to the first child element; the firstChild element version.
  • lastElementChild: point to the last child; lastChild element version.
  • previousElementSibling: before pointing to a sibling; previousSibling element version.
  • nextElementSibling: After pointing to a sibling; nextSibling element version.

Element Traversal using the new API, the code is more concise:

let child = element.firstElementChild
while (child !== element.lastElementChild) {
  processChild(child) // 肯定是元素节点
  child = child.nextElementSibling // 遍历下一个元素节点
}

getElementsByTagName ( '*') will return what?
Recently saw a face questions: find the page that appears the most label, or the largest number of occurrences before two or three labels, you can try to lower their own implementation.

// 获取元素列表,以键值对的形式存储为一个对象
function getElements () {
  // 如果把特殊字符串 "*" 传递给 getElementsByTagName() 方法
  // 它将返回文档中所有元素的列表,元素排列的顺序就是它们在文档中的顺序。
  // 返回一个 HTMLCollection - 类数组对象
  const nodes = document.getElementsByTagName('*')
  const tagsMap = {}
  for (let i = 0, len = nodes.length; i < len; i++) {
    let tagName = nodes[i].tagName
    if (!tagsMap[tagName]) {
      tagsMap[tagName] = 1
    } else {
      tagsMap[tagName] ++
    }
  }
  return tagsMap
}

// n 为要选取的标签个数 - 即出现次数前 n 的标签名
// 将上面的方法获取的对象的键值对取出组成数组,按出现次数排序
function sortElements (obj, n) {
  const arr = []
  const res = []
  for (let key of Object.keys(obj)) {
    arr.push({ tagName: key, count: obj[key] })
  }

  // 冒泡
  for (let i = arr.length - 1; i > 0; i--) {
    for (let j = 0; j < i; j++) {
      if (arr[j].count < arr[j + 1].count) { // 升序
        swap(arr, j, j + 1)
      }
    }
  }

  for (let i = 0; i < n; i++) {
    res.push(arr[i].tagName)
  }
  return res
}

function swap (arr, index1, index2) {
  let temp = arr[index1]
  arr[index1] = arr[index2]
  arr[index2] = temp
}

let res = sortElements(getElements(), 2)
console.log(res)

Dynamically added script and style

// 动态添加脚本
function loadScript (url) {
  var script = document.createElement("script")
  script.type = "text/javascript"
  script.src = url
  document.body.appendChild(script)
} 

// 动态添加样式
function loadStyles (url) {
  var link = document.createElement("link")
  link.rel = "stylesheet"
  link.type = "text/css"
  link.href = url
  var head = document.getElementsByTagName("head")[0]
  head.appendChild(link)
} 

Method contains () determines whether a node to another node descendants

contains () method is used to determine whether a node is a descendant of another node, calling contains () method should be the ancestor node, the node is the beginning of the search, this method takes a parameter that needs to be detected nodes.

console.log(document.documentElement.contains(document.body)) // true

This example of the detection of the <body> element is not a descendant <html> element

Use Element.getBoundingClientRect () and the dataset

This is a small booklet the previous example, the use of native JS implement lazy loading images, you need to understand both points of knowledge

1. Element.getBoundingClientRect()A method returns the size of the element and position relative to the viewport. Specific interpretation and usage reference MDN

By Element.getBoundingClientRect().topthe
window.innerHeight(height of the current window) comparison can determine whether the image appears in the viewable area.

Note that this top value is relative to the top of the top of the current window rather than a top began.

2. You can get to the data- * attribute to add an element node Element.dataset, we can save the url when the image is loaded through this property.

<!DOCTYPE html>
<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>
    <style>
        * {
            padding: 0;
            margin: 0;
        } 
        .box-image {
          display: flex;
          flex-direction: column;
          align-items: center;
        }
        img {
            display: inline-block;
            height: 300px;
            margin-bottom: 20px;
        }
    </style>
</head>

<body>
    <div class="box-image">
        <img src="" class="image-item" lazyload="true" data-original="http://cmk1018.cn/wp-content/uploads/2019/04/6.jpg" alt="">
        <img src="" class="image-item" lazyload="true" data-original="http://cmk1018.cn/wp-content/uploads/2019/04/8.jpg" alt="">
        <img src="" class="image-item" lazyload="true" data-original="http://cmk1018.cn/wp-content/uploads/2019/04/aotu-10.jpg" alt="">
        <img src="" class="image-item" lazyload="true" data-original="http://cmk1018.cn/wp-content/uploads/2019/04/aotu-15.jpg" alt="">
        <img src="" class="image-item" lazyload="true" data-original="http://cmk1018.cn/wp-content/uploads/2019/05/%E7%BD%AA%E6%81%B6%E7%8E%8B%E5%86%A04.jpg" alt="">
        <img src="" class="image-item" lazyload="true" data-original="http://cmk1018.cn/wp-content/uploads/2019/05/%E7%BD%AA%E6%81%B6%E7%8E%8B%E5%86%A06.jpg" alt="">
        <img src="" class="image-item" lazyload="true" data-original="http://cmk1018.cn/wp-content/uploads/2019/04/9.jpg" alt="">
        <img src="" class="image-item" lazyload="true" data-original="http://cmk1018.cn/wp-content/uploads/2019/04/aotu-16.jpg" alt="">
    </div>
    <script>
        var viewHeight = document.documentElement.clientHeight;
        // 节流:加一个 300ms 的间隔执行
        function throttle(fn, wait) {
          let canRun = true
          return function (...args) {
            if (!canRun) return
            canRun = false 
            setTimeout(() => {
              fn.apply(this, args)
              canRun = true
            }, wait)
          }
        }
        function lazyload() {
          let imgs = document.querySelectorAll('img[data-original][lazyload]') // 获取文档中所有拥有 data-original lazyload 属性的<img>节点
          imgs.forEach(item => {
            if (item.dataset.original == '') {// HTMLElement.dataset 访问在 DOM 中的元素上设置的所有自定义数据属性(data-*)集。
              return
            }
            // 返回一个 DOMRect 对象,包含了一组用于描述边框的只读属性——left、top、right 和 bottom,
            // 单位为像素。除了 width 和 height 外的属性都是相对于视口的左上角位置而言的。
            let rect = item.getBoundingClientRect()
            // 其 top 值是相对于当前视窗的顶部而言的而不是绝对的顶部,所以 top 值 < window.innerHeight 的话图片就出现在底部了就需要加载
            if (rect.bottom >= 0 && rect.top < viewHeight) {
              let img = new Image()
              img.src = item.dataset.original
              // 图片加载完成触发 load 事件
              img.onload = function () {
                item.src = img.src
              }
              // 移除属性的话就不会重复加载了
              item.removeAttribute('data-original')
              item.removeAttribute('lazyload')
            }
          })
        }
        // 先调用一次加载最初显示在视窗中的图片
        lazyload();
        let throttle_lazyload = throttle(lazyload, 300)
        document.addEventListener('scroll', throttle_lazyload)
    </script>
</body>
</html>

How to render tens of thousands of pages of data and does not get stuck?

It is also on the brochure, examines the use of document fragments (createDocumentFragment) batches insertion node

<!DOCTYPE html>
<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>
  <body>
    <ul>
      控件
    </ul>
    <script>
      const total = 100000 // 10万条数据
      const once = 20      // 每轮插入的数据条目
      const loopCount = total / once // 渲染总次数
      let countOfRender = 0
      let ul = document.querySelector('ul')
      function add() {
        // 使用文档碎片优化性能
        const fragment = document.createDocumentFragment()
        for (let i = 0; i < once; i++) {
          const li = document.createElement('li')
          li.innerText = Math.floor(Math.random() * total)
          fragment.appendChild(li)
        }
        ul.appendChild(fragment)
        countOfRender+=1
        loop()
      }
      function loop() {
        if (countOfRender < loopCount) {
          window.requestAnimationFrame(add) // 使用 requestAnimationFrame 每隔 16ms(浏览器自己选择最佳时间)刷新一次
        }
      }
    </script>
  </body>
</html>

Guess you like

Origin www.cnblogs.com/cmk1018/p/11347635.html