你应该知道的 DOM

概念

DOM 定义了访问 HTML 和 XML 文档的标准,同时它也是一个编程接口。DOM 早期针对于 XML 后来通过扩展后可用于 HTML,它把页面映射为一个多层节点结构,并提供方法、属性使得程序能够对该结构进行访问及修改,从而改变文档对应节点的样式和内容。javascript

简单来讲,浏览器在显示页面的时候须要解析文档,而脚本语言也须要获取这份文档,可是它们最终须要获取到的信息是不一样的,因此 DOM 至关于一个桥梁,沟通了脚本语言和浏览器。因此当咱们修改这个由 DOM 构建出的对象时,页面也会发生相应的变化。html

若是将一个web页面当作一个文档,因为不一样的浏览器有各自的 DOM 标准,因此浏览器在解析页面的时候会映射出不一样的 DOM 树模型,从而致使在操做、访问页面节点上存在差别。例如IE中全部的DOM对象都是以 COM 对象的形式实现的,因此和其余浏览器实现的DOM对象行为不一样。java

注:SVG、MathML、SMIL都是基于XML的语言,它们都发布了针对了本身的 DOM 标准node


产生缘由

W3C 组织为了防止 Netscape 公司和微软按本身意愿开发致使浏览器兼容困难状况发生。规划了 DOM 标准,Web 浏览器在 DOM 标准出现一段时间后才开始实现它,如今的浏览器的首要目的就是支持 DOM,有了标准就对开发者更加的友好了。web

同时须要注意的是 DOM 不只针对 JavaScript,其余语言也实现了DOM。编程


W3C DOM标准

  • 核心 DOM-适用于全部文档类型的标准模型
  • XML DOM-XML 文档的标准模型
  • HTML DOM-HTML 文档的标准模型

核心DOM和html DOM的区别

核心DOM

  • 对象:Document、Node、ElementNode、TextNode、AttributeNode、CommentNode、NodeList跨域

  • 接口:createElement、appendChild、setAttribute等数组

  • 接口分类:浏览器

    • 视图接口:跟踪不一样文档的接口(添加CSS前和后)
    • 事件接口:定义了事件和事件处理
    • 样式接口:定义了基于CSS的接口
    • 遍历和范围接口:定义了遍历和操做文档树的接口
    • ...
  • 提供了全部文档类型增删改查操做,经过对象提供的接口修改 DOM 树及其表明的文档。安全

html DOM

  • 对象:Image、Table、Form、 Input 等

  • 接口:createElement、appendChild、setAttribute等

  • 只提供了HTML增删改查操做,经过将 HTML 类型的封装,来简化和对象对应DOM的访问和操做。

    var img = new Image();
    img.src = 'xxxx';
    img.style.width = '100px';
    复制代码

总结:他们都为指定的文档类型提供了对象、属性、方法和事件。只不过核心DOM提供的是文档基本类型的扩展,HTML DOM提供的只是HTML类型的扩展。


节点层次

DOM树结构由一个个的节点组成,节点又分为不一样的类型,不一样的类型有其特有的行为和特征。每一个节点都有包含着本身的信息和接口等,因为节点和节点间有必定的关系,能够造成了层次关系,最终的体现就是以HTML元素即文档元素为根节点的树状结构。

image-20200714205746268

Node类型

JavaScript 中全部的节点类型都是继承于Node 类型,因此全部的节点都共享着相同的基本属性和基本方法。

节点关系
  • childNodes 是一个类数组,能够经过 slice 方法进行转换也可使用 ES6 中的扩展运算符进行转换。
  • 关系指针都只是可读的
  • hasChildNodes()能够判断该节点是否包含一个或以上的子节点,若是是那么返回 true。
  • 若是没有子节点 fristChild 和 lastChild 都为 null,若是只有一个节点那么这俩值指向同一个节点。

image-20200714170442235

节点操做
  • 因为关系指针都是可读的,因此 DOM 提供了操做节点的方法。操做节点的时候,节点的信息以及和其余节点的关系也会相应进行更新。

  • 插入节点

    • appendChild() 是最经常使用的节点插入方法,它将子节点插入到childNodes列表的最后。若是插入的节点已经在以前存在过,那么该节点将从以前的位置移除,在当前childNodes 列表最后进行添加。这是因为同一个节点不能出如今文档的多个位置上。
    • insertBefore(insert,refer) 能够将节点插入到任意位置,其参数为插入的节点以及参考的节点,最终插入的节点会在参考节点前面,若是参考节点为null,那么至关于appendChild()
  • 替换节点

    • replaceChild(insertNode,replaceNode)能够将被替换的节点替换成指定的节点(insertNode)。
  • 移除节点

    • removeChild() 移除节点,并将要被移除的节点做为返回值 。
  • 克隆节点

    • cloneNode( boolean),接受一个boolean值,若是为true那么就是深拷贝节点。深拷贝节点会把该节点和其下面节点及关系进行拷贝,浅拷贝只会拷贝当前节点。须要注意的是,cloneNode 不会拷贝节点的节点中 JavaScript 属性,例如事件处理程序等。可是在 IE 中该方法会拷贝事件处理程序,因此在拷贝以前最好先移除事件。
  • 优化文本节点

    • normalize() 若是对节点使用该方法,那么当该节点下全部子节点出现,文本节点为空或者有 2 个连续的子节点时会进行 删除或和并文本节点的操做。

Element 类型

Element类型用于表现 XML 、HTML元素,提供了对元素标签名 (TagName)子节点特性的访问。也是继承于Node类型。同时在HTML中标签名始终以所有大写的形式显示,在XML中标签名和源代码中相同。

if(element.tagName.toLowerCase() == 'div'){
	// 这样能够适用 HTML 和 XML 形式。
}
复制代码
获取标签名

获取标签名可使用 ele.tagName 或者是 ele.nodeName ,两者返回的值都是相同的。

标准特性

HTMLElement类型继承于Element,全部的HTML元素都是由该类型或者其子类型构建。下面的标准特性就是Element特有的特性。他们均可以经过元素访问特定属性进行访问或修改。

div.id = test;
div.id //test;
复制代码
  • id 文档中的惟一标识符

  • title 有关元素的附加说明信息,通常会经过提示条显示出来

  • lang 元素内容的语言代码,不多使用

  • dir 语言的方向,默认左到右 ‘ltr’ ,能够修改成右到左 ‘rtl’ 。不多使用

  • className 和元素的 class 特性对应,能够为元素指定 CSS 类。

  • childNodes和children的区别

    • childNodes返回元素、文本甚至是注释

    • children只返回元素,不返回文本。

      <div id="outer">
          text1
          <div>1</div>
          <div>2</div>
          <div>3</div>
          text2
      </div>
      复制代码
      var dom = document.getElementById('outer');
      console.log(dom.children)// HTMLCollection(3)
      //0: div
      //1: div
      //2: div
      console.log(dom.childNodes)// NodeList(7)
      //0: text
      //1: div
      //2: text
      //3: div
      //4: text
      //5: div
      //6: text
      复制代码
特性和属性的区别
  • 特性(attribute )

    特性是 HTML 元素节点自带的,浏览器在解析 HTML 文档时,会将标准的特性转换成属性,从而使得脚本程序也能够经过属性来访问到这些特性(id、title、class等)。

    <div id = 'test'><div>
    复制代码
    div.id //因为标签id 为test,因此DOM 对象就会有 body.id="test"。
    div.id = 'test1';
    div.id //test1;修改属性会致使特性修改。
    复制代码
    • 自定义特性

      除了标准的特性,还存在自定义特性,须要注意的是自定义特性须要加上 data- 的前缀。这是因为若是咱们直接使用了非标准的特性(不加data-),之后标准更新后可能会致使冲突!

    <div id = 't' data-test="666"></div>
    复制代码
    let elem = getElementById('t');
    elem.getAttribute('test') // 666,此时添加到DOM的特性中能够访问到
    elem.dataset.test //test,能够在dataset中获取到自定义特性
    elem.test //undefined,自定义特性不会添加到属性中 
    复制代码
    • 方法

      • elem.hasAttribute(name) — 检查特性是否存在。

      • elem.getAttribute(name) — 获取这个特性值。

      • elem.setAttribute(name, value) — 设置这个特性值。

      • elem.removeAttribute(name) — 移除这个特性。

  • 属性(property )

    image-20200714214333474

    • 每一个对象均可以有属性,DOM对象内置了不少属性能够任意的修改,固然暂时不考虑使用Object.defineProperties修改属性的权限。因此DOM对象也能够自由操做属性(此时只是对象)。
  • 特殊特征

    • style

      • 特性值访问的时候包含的是CSS文本
      • 经过属性访问则会返回一个对象
    • 事件处理程序(onclick)

      • 经过特性访问获取到的是字符串
      • 经过属性访问获取到的是函数

    image-20200714221455172

属性 特性
类型 任何值,标准的属性具备规范中描述的类型 字符串
名字 名字(name)是大小写敏感的 名字(name)是大小写不敏感的

在大多数状况下,最好使用 DOM 属性。仅当 DOM 属性没法知足开发需求,而且咱们真的须要特性时,才使用特性,例如:

  • 咱们须要一个非标准的特性。可是若是它以 data- 开头,那么咱们应该使用 dataset
  • 咱们想要读取 HTML 中“所写的”值。对应的 DOM 属性可能不一样,例如 href 属性一直是一个 完整的 URL,可是咱们想要的是“原始的”值。
元素关系

相似于Node节点之间的关系,只不过当前关系只包含元素没有text节点、注释节点等其余节点。

image-20200715143656603

Document类型

在浏览器中document是HTMLDocument对象的一个实例,同时HTMLDocument对象是继承自 Document 对象。 实际上docuemnt 表示的是 HTML页面,同时它也是 window 的一个属性,因此能够经过全局对象来访问。经过 document 能够获取到浏览器文档中全部节点的数据(包括方法)、以及能够操做文档。

文档信息

如下几个信息都是 Document 对象没有的,都和网页请求有关,这些全部的信息都存在 HTTP 头部 ,只不过经过这些属性能够在Javascript中访问到它们。

  • document.URL:URL格式为 <协议>://<域名>:<端口>/<路径>

  • document.domain:能够获取到当前页面的域名。域名的设置只能愈来愈松散,即限制条件愈来愈低。

    可使用document.domain实现跨域,可是前提是必须是同一个基础域名,且协议和端口都得一致。Javascript 出于对安全的考虑,因此不一样域的页面不能互相操做。

    此时若是存在基础域名和端口都相等的URL,url1 为a.test.com ,url2 为 b.test.com, 此时若是url1中的 home.html页面须要访问到url2 中的about.html页面且操做about.html页面,此时是没法直接操做的。若是须要操做就须要在url1 和 url2 中分别设置document.domain 为 test.com。 此时两个页面同域,因此能够互相访问和修改。

  • document.referrer: 保存着连接到当前页面的 URL,若是没有来源页面可能为空。

  • document.tiltle:能够获取或者修改当前页面的标题,页面的标题会在修改后直接更新。

查找元素
  • getElementById 会返回第一个 id 为指定值的元素且区分大小写,可是须要注意若是是在 IE8 及更低版本时,不区分大小写。在 IE7 中调用该方法时会把第一个 name属性为该值或者第一个 id 为该值的元素进行返回.

    <input name="test" type="text" value="Just Test"></input>
    <div id="test"></div>
    
    //document.getElementById('test') 返回input
    复制代码
  • getElementsByTagName() 能够获取到指定节点为该标签的全部子节点。子节点的集合即 HTMLCollection 对象。若是须要查看到文档中全部的元素,能够经过getElementsByTagName(”*“); HTMLCollection 对象有一个nameItem方法,能够返回符合当前 name 为指定值的元素。

  • getElementsByClassName 方法返回文档中全部指定类名的元素集合,做为 NodeList 对象。同时能够同时查找多个类

    var nodeList = document.getElementsByClassName("className1 className2");
    复制代码
  • getElementsByName() 方法可返回带有指定名称的对象的集合

  • querySelector();返回与该模式匹配的第一个元素,可兼容到CSS2

  • querySelectorAll();返回与该模式匹配的全部元素,底层相似于对一组元素的快照,而不是不断的动态查询(损耗性能 )

  • document.anchors:包含文档中全部带那么特性的元素。

  • document.forms:包含文档中全部的元素,与document.getElementByTagName("form")的到的结果相同。

  • document.images:包含文档中全部的元素,与document.getElementByTagName("img")的到的结果相同。

  • document.links:包含文档中全部带href特性的元素。

理解DOM操做的关键,就是理解DOM对性能的影响。DOM操做每每是 JavaScript 程序中开销最大的部分。因为NodeList是”动态的“,因此每次访问它的时候都会进行一次新的查询,最好避免的方法就是少访问和减小 DOM 操做。

​ -----高程


经常使用扩展

classList

在操做类名的时候能够经过元素的classList属性对类名进行添加,删除或者替换,其实是直接操做className的语法糖,表明着当前元素的集合,其兼容性如图所示。

image-20200715111352642

  • 方法
    • add(value) 添加类名
    • contains(value) 判断是否包含类名
    • remove(value) 移除类名
    • toggle(value) 转换类名,若是该元素没有这个类名就添加,若是有就删除这个类名。

焦点管理

  • 得到焦点的办法

    • 页面加载
    • 用户输入
    • focus方法
    var btn = document.getElementsByTagName('button')[0];
    btn.focus()
    复制代码
  • 获取当前焦点元素

    • activeElement() 在文档加载期间,其返回值为null,页面加载成功后为body
  • 判断当前文档内节点是否有焦点

    • hasFocus() 代表当前文档或者当前文档内的节点是否得到了焦点。该方法能够用来判断当前文档中的活动元素是否得到了焦点。

参考文档:

一、特性和属性(Attributes and properties)

二、《JavaScript 高级程序设计》