前端还能不会DOM操作吗?

336 阅读4分钟

「这是我参与2022首次更文挑战的第5天,活动详情查看:2022首次更文挑战

获取DOM节点

常用的方法:

document.getElementById()
document.getElementsByClassName()
document.getElementsByName()
document.getElementsByTagName()
document.getElementsByTagNameNS()

方便但是一般不用的方法:

document.querySelector()
document.querySelectorAll()

因为下面这两个方法有两个缺点:

  • 性能问题
  • 他两个获取的只是一个DOM快照,不具备实时性,而我们平时开发DOM操作的实时性非常重要。

DOM节点和元素的关系

节点是包含元素的,元素只是指自身,节点指整个DOM结构。

常用的节点类型如下:

常用节点类型类性值nodeType
元素节点1
属性节点2
文本节点3
注释节点8
document9
DocumentFragment11

常用的DOM属性

获取DOM的属性

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

console.log(oli.firstChild); // 获取第一个子节点
console.log(oli.lastChild); // 获取最后一个子节点
console.log(oli.childNodes); // 获取所有类型的子元素 
console.log(oli.childElementCount); // 获取子元素节点的个数
console.log(oli.children); // 获取所有子元素节点
console.log(oli.children[0].nextSibling); // 获取下一个兄弟节点
console.log(oli.children[0].nextElementSibling); // 获取下一个元素节点的兄弟节点
console.log(oli.children[1].previousSibling); // 获取上一个兄弟节点
console.log(oli.children[1].previousElementSibling); // 获取上一个元素节点的兄弟节点

一些带有兼容问题的属性:

parentElement // 获取父元素   IE9及以下不支持
children   // 获取所有子元素节点   IE7及以下不支持
childELementCount  //  获取子元素节点的个数    IE9及以下不支持
firstElementChild  lastElementChild  //  IE9及以下不支持
nextElementSibling  previousElementSibling  //  IE9及以下不支持

获取DOM上的信息

odiv.attrbutes // 元素的属性集合  NameNodeMap

<a href="" data-url="plmxs" data-sort="pay">普罗米修斯</a>
this.dataset.sort // 用data-key做属性名,可以通过ele.dataset[key]获取

odiv.hasChildNodes() // 判断节点是否含有某一个元素 只要又一个空格都算是文本节点
节点类型/属性keynodeNamenodeValuenodeType
文本节点#text内容3
注释节点#comment内容8
元素节点大写的标签名null1
document#documentnull9
属性节点属性key属性值2

用childNodes实现children的功能,做兼容

function elemChildren(node) {
  var temp = {
      length: 0,
      push: Array.prototype.push,
      splice: Array.prototype.splice,
    },
    children = node.childNodes;
  for (let i = 0; i < children.length; i++) {
    const childItem = children[i];
    if (childItem.nodeType === 1) {
      // (1)
      temp[temp[length]] = childItem;
      temp[length] += 1;
      // (2)
      // temp.push(childItem);
    }
  }
  return temp;
}
console.log(elemChildren(odiv))

DOM集合

通过getElementsByClassName等方法获取到的是类数组集合,继承自HTMLCollection。

类数组:本身是对象,为了保存自身的方法并且具备有序的特性,衍生出的一种数据结构;有length属性可以通过索引获取,但是不可被迭代(没有迭代器)。转数组的常用方法:Array.from(target) Array.prototype.slice.call(target) ...target

DOM的原型链

<div class="box">
  我是文本
  <!-- 我是注释 -->
</div>
<script>
  const oBox = document.getElementsByClassName("box");

  console.log(oBox); // HTMLCollection -> Object

  console.log(oBox[0].__proto__); // HTMLDivElement(不同的标签类型HTML+标签名+Element) -> HTMLElement -> Element -> Node -> EventTarget -> Object
  
  console.log(oBox[0].childNodes[1].__proto__)  // Comment -> CharacterData -> Node -> EventTarget -> Object
  
  console.log(oBox[0].childNodes[0].__proto__)  // Text -> CharacterData -> Node -> EventTarget -> Object

  console.log(oBox[0].attributes) // NamedNodeMap -> Object

  console.log(oBox[0].attributes[0].__proto__)  // Attr -> Node -> EventTarget -> Object

  console.log(document.__proto__)  // HTMLDocument -> Document -> Node -> EventTarget -> Object
</script>

document和element

console.log(Document.prototype);
console.log(Element.prototype);
// 获取dom的api
// Document下的api
// getElementById
// getElementsByClassName
// getElementsByName
// getElementsByTagName
// getElementsByTagNameNS
// querySelector
// querySelectorAll

// Element的api
// getElementsByClassName
// getElementsByTagName
// getElementsByTagNameNS
// querySelector
// querySelectorAll;

总结:元素节点是不能用 getElementById  getElementsByClassName getElementsByName

一些便捷的方法

// 获取body  head   title 有快捷方法
console.log(document.body); // body节点
console.log(document.head); // head节点
console.log(document.title); // title节点的内容
console.log(document.documentElement); // html节点

创建DOM的方法

// 创建dom
const obox = document.getElementsByClassName('box')[0];
const odiv = document.createElement('div');
odiv.innerText = '我是属性节点';
// 创建文本节点
const otext = document.createTextNode('我是文本节点');
// otext.textContent = '我是文本节点';
// 创建注释节点
const ocomment = document.createComment('我是注释节点')

obox.appendChild(odiv);
obox.appendChild(otext);
obox.appendChild(ocomment);
// document.body.appendChild也能用,所以**appendChild是在Node下**

操作DOM

 obox1.innerText = "我是添加的div";
// 把obox1放在oh1前面
obox.insertBefore(obox1, oh1);

// 移除子dom(移除之后依然可以打印出oh1,因为reomveChild只是从dom结构中删除,本身还存在浏览器的内存中)
console.log(obox.removeChild(oh1));

// 返回undefined,用自己remove方法删除自己
console.log(obox1.remove());

// 替换dom   replaceChild(new, origin)
obox.replaceChild(oh1, oh2);

// innerText  会过滤掉所有的标签,省下纯文本
console.log(obox.innerText);

// 设置属性setAttrbute
obox.setAttribute("id", "obox");
// 获取属性值
console.log(obox.getAttribute("class"));

所有滚动知识点

滚动条位置

window.pageXOffset/pageYOffset  // 整个页面滚动条位置
window.body.scrollLeft/scrollTop // IE9及以下  某个dom内部滚动条位置
document.documentElement.scrollLeft/scrollTop  // 
window.scrollY/scrollX  // 基本不用 

操作滚动条

window.scroll(20, 20)   window.scrollTo(20, 20)  // 这两个效果一样,滚动到指定位置 
window.scrollBy(20, 20) // 滚动指定的距离 

盒子的几何面积

浏览器的标准模式(CSS1Compat)和怪异模式(BackCompat)

BackCompat 对应quirks mode

CSS1Compat 对应strict mode

在Standars mode中:

元素真正的宽度 = margin-left + border-left-width + padding-left + width + padding-right + border-right-width + margin-right;

在Quirks mode中:

width则是元素的实际宽度,内容宽度 = width - (margin-left + margin-right + padding-left + padding-right + border-left-width + border-right-width);

document.compatMode // 判断模式

元素位置

 // 如果父元素有定位,则根据父元素位置,若无定位则一直向上找直到body
 console.log(box2.offsetTop);