回顾文档对象模型 (DOM)(一)

一、概念

将 web 页面与到脚本或编程语言连接起来。通常是指 JavaScript,但将 HTML、SVG 或 XML 文档建模为对象并不是 JavaScript 语言的一部分。DOM模型用一个逻辑树来表示一个文档,树的每个分支的终点都是一个节点(node),每个节点都包含着对象(objects)。DOM的方法(methods)让你可以用特定方式操作这个树,用这些方法你可以改变文档的结构、样式或者内容。节点可以关联上事件处理器,一旦某一事件被触发了,那些事件处理器就会被执行。

1、内容树

任何HTML文档 (或者说任何SGML文档或者 XML 文档) 是一个树状结构。 比如,以下的文档和树状结构是相似的

<html>
<head>
  <title>My Document</title>
</head>
<body>
  <h1>Header</h1>
  <p>Paragraph</p>
</body>
</html>

在这里插入图片描述

当Mozilla解析文档的时候,它首先构建一个内容树然后用它来显示这个文档。

用于描述树状结构的术语通常出现在DOM Level 1的核心中。 我上面画的每一个方块都是这个树的一个节点。 节点上面的线条表示父子关系: 上面的父节点, 而位于下方的是子节点. 位于一个父节点下面的两个子节点是相邻的。 类似地,我们可以指代祖先和后代。

2、使用选择器定位DOM元素

Selectors API提供了通过与一组选择器匹配来快速轻松地从DOM检索 Element节点的方法。这比以前的技术要快得多,其中有必要使用JavaScript代码中的循环来定位您需要查找的特定项目。

  1. NodeSelector 接口
    此规范向实现 Document, DocumentFragment, 或 Element 接口的任何对象添加了两种新方法:
    querySelector
    返回节点子树内与之相匹配的第一个 Element 节点。如果没有匹配的节点,则返回null。
var baseElement = document.querySelector("p");
document.getElementById("output").innerHTML =
         (baseElement.querySelector("div span").innerHTML);

querySelectorAll
返回一个NodeList 包含节点子树内所有与之相匹配的Element节点,如果没有相匹配的,则返回一个空节点列表。

var el = document.querySelector('#test');
var matches = el.querySelectorAll('div.highlighted > p'); 

3、事件及DOM

主要描述了事件(Event)接口本身以及DOM节点中的事件注册接口、事件监听接口

注册事件监听器
这里有3种方法来为一个DOM元素注册事件回调。
(1)EventTarget.addEventListener

// 假设 myButton 是一个按钮
myButton.addEventListener('click', greet, false);
function greet(event) {
    // 打印并查看event对象
    // 打印arguments,以防忽略了其他参数
    console.log('greet: ' + arguments);
    alert('Hello world');
} 

(2)HTML 属性

<button onclick="alert('Hello world!')">

应该尽量避免这种书写方式,这将使HTML变大并减少可读性。考虑到内容/结构及行为不能很好的分开,这将造成bug很难被找到。

(3)DOM 元素属性

// 假设 myButton 是一个按钮
myButton.onclick = function(event){alert('Hello world');};

这种方式的问题是每个事件及每个元素只能被设置一个回调。

二、接口

1、Attr

该类型使用对象来表示一个DOM元素的属性。在大多数DOM方法中,你可能会直接通过字符串的方式获取属性值(例如Element.getAttribute()),但是一些函数(例如Element.getAttributeNode())或通过迭代器访问时则返回Attr类型。

在这里插入图片描述
(1)name 该属性的名称
(2)namespaceURI 表示该属性的命名空间URIDOMString,如果该元素不在命名空间中,则返回null。
(3)localName 表示该属性的命名空间限定的本地名称DOMString。

2、Document

接口表示任何在浏览器中载入的网页,并作为网页内容的入口,也就是DOM 树。DOM 树包含了像 、

这样的元素,以及大量其他元素。它向网页文档本身提供了全局操作功能,能解决如何获取页面的 URL ,如何在文档中创建一个新的元素这样的问题。

在这里插入图片描述

1. 属性
(1)body:返回当前文档中的元素或者元素.

var aNewBodyElement = document.createElement("body");

aNewBodyElement.id = "newBodyElement";
document.body = aNewBodyElement;
alert(document.body.id); // "newBodyElement"

(2)Document.characterSet 只读属性返回当前文档的字符编码。该字符编码是用于渲染此文档的字符集,可能与该页面指定的编码不同。

<button onclick="alert(document.characterSet);">查看字符集</button>
//返回当前文档的字符集,比如"ISO-8859-1" 或者 "UTF-8"

(3)ParentNode.childElementCount 只读属性返回一个无符号长整型数字,表示给定元素的子元素数。

var foo = document.getElementById("foo"); 
if (foo.childElementCount > 0) { 
    // do something
}

(4)ParentNode.children 返回 一个Node的子elements ,是一个动态更新的 HTMLCollection。

// parg是一个指向<p>元素的对象引用
if (parg.childElementCount)
// 检查这个<p>元素是否有子元素
// 译者注:childElementCount有兼容性问题
 {
   var children = parg.children;
   for (var i = 0; i < children.length; i++) 
   {
   // 通过children[i]来获取每个子元素
   // 注意:List是一个live的HTMLCollection对象,在这里添加或删除parg的子元素节点,都会立即改变List的值.
   };
 };

(5)Document.dir 代表了文档的文字朝向,是从左到右(默认)还是从右到左。‘rtl’(right to left)代表从右到左,‘ltr’(left to right)代表从左到右。

console.log(document.dir);// "" (译者添加)
document.dir = "ltr"//(默认);
document.dir = "rtl";
dirStr = document.dir; 
document.dir = dirStr;

(6)Document.documentElement 是一个会返回文档对象(document)的根元素的只读属性(如HTML文档的 元素)。

const rootElement = document.documentElement;
const firstTier = rootElement.childNodes;

// firstTier 是由根元素的所有子节点组成的一个 NodeList
for (let i = 0; i < firstTier.length; i++) {
   // 使用根节点的每个子节点
   // 如 firstTier[i]
}

(7)document.head 返回当前文档中的 元素。如果有多个 元素,则返回第一个。

// HTML部分源码为: <head id="my-document-head">
var aHead = document.head;

alert(aHead.id); // "my-document-head";

alert( document.head === document.querySelector("head") ); // true

(8)document.images 接口的只读属性images返回当前文档中所有 image 元素的集合.

var ilist = document.images;
for(var i = 0; i < ilist.length; i++) {
    if(ilist[i].src == "banner.gif") {
         // 发现了banner图片
    }
}

(9)document.links 属性返回一个文档中所有具有 href 属性值的 元素与 元素的集合。

var links = document.links;
for(var i = 0; i < links.length; i++) {
  var linkHref = document.createTextNode(links[i].href);
  var lineBreak = document.createElement("br");
  document.body.appendChild(linkHref);
  document.body.appendChild(lineBreak);
}

(10)Document.readyState 属性描述了document 的加载状态。当该属性值发生变化时,会在 document 对象上触发 readystatechange 事件。

一个文档的 readyState 可以是以下之一:
loading(正在加载)
document 仍在加载。
interactive(可交互)
文档已被解析,"正在加载"状态结束,但是诸如图像,样式表和框架之类的子资源仍在加载。
complete(完成)
文档和所有子资源已完成加载。表示 load 状态的事件即将被触发。

switch (document.readyState) {
  case "loading":
    // 表示文档还在加载中,即处于“正在加载”状态。
    break;
  case "interactive":
    // 文档已经结束了“正在加载”状态,DOM元素可以被访问。
    // 但是像图像,样式表和框架等资源依然还在加载。
    var span = document.createElement("span");
    span.textContent = "A <span> element.";
    document.body.appendChild(span);
    break;
  case "complete":
    // 页面所有内容都已被完全加载.
    let CSS_rule = document.styleSheets[0].cssRules[0].cssText;
    console.log(`The first CSS rule is: ${CSS_rule }`);
    break;
}

document.addEventListener('readystatechange', (event) => {
    log.textContent = log.textContent + `readystate: ${document.readyState}\n`;
});

2、方法
(1)ParentNode.append 方法在 ParentNode的最后一个子节点之后插入一组 Node 对象或 DOMString 对象。被插入的 DOMString 对象等价为 Text 节点。

var parent = document.createElement("div");
var p = document.createElement("p");
parent.append(p);

console.log(parent.childNodes); // NodeList [ <p> ]

(2)Document.createAttribute() 方法创建并返回一个新的属性节点。这个对象创建一个实现了 Attr 接口的节点。这个方式下DOM不限制节点能够添加的属性种类。

var node = document.getElementById("div1");
var a = document.createAttribute("my_attrib");
a.value = "newVal";
node.setAttributeNode(a);
console.log(node.getAttribute("my_attrib")); // "newVal"

(3)document.createDocumentFragment(); 创建一个新的空白的文档片段( DocumentFragment)。因为文档片段存在于内存中,并不在DOM树中,所以将子元素插入到文档片段时不会引起页面回流(对元素位置和几何上的计算)。因此,使用文档片段通常会带来更好的性能。

var element  = document.getElementById('ul'); // assuming ul exists
var fragment = document.createDocumentFragment();
var browsers = ['Firefox', 'Chrome', 'Opera', 
    'Safari', 'Internet Explorer'];

browsers.forEach(function(browser) {
    var li = document.createElement('li');
    li.textContent = browser;
    fragment.appendChild(li);
});

element.appendChild(fragment);

(4)Document.createElement() 方法用于创建一个由标签名称 tagName 指定的 HTML 元素。如果用户代理无法识别 tagName,则会生成一个未知 HTML 元素 HTMLUnknownElement。

document.body.onload = addElement;

function addElement () { 
  // 创建一个新的 div 元素
  let newDiv = document.createElement("div"); 
  // 给它一些内容
  let newContent = document.createTextNode("Hi there and greetings!"); 
  // 添加文本节点 到这个新的 div 元素
  newDiv.appendChild(newContent); 

  // 将这个新的元素和它的文本添加到 DOM 中
  let currentDiv = document.getElementById("div1"); 
  document.body.insertBefore(newDiv, currentDiv); 
}

(5)document.createTextNode 创建一个新的文本节点。这个方法可以用来转义 HTML 字符。

 const p1 = document.getElementById("p1"),
  buttons = document.body.querySelectorAll(":scope > button");
  function addTextNode(text) {
    p1.appendChild( document.createTextNode(text) );
  }
  buttons.forEach(button =>
    button.addEventListener("click", () =>
      addTextNode(button.value)
    )
  );

(6)document.getElementById 返回一个匹配特定 ID的元素. 由于元素的 ID 在大部分情况下要求是独一无二的,这个方法自然而然地成为了一个高效查找特定元素的方法。

 var elem = document.getElementById('para');
  elem.style.color = newColor;

(7) document.getElementsByClassName(names); 返回一个包含了所有指定类名的子元素的类数组对象。当在document对象上调用时,会搜索整个DOM文档,包含根节点。你也可以在任意元素上调用getElementsByClassName() 方法,它将返回的是以当前元素为根节点,所有指定类名的子元素。

var testElements = document.getElementsByClassName('test');
var testDivs = Array.prototype.filter.call(testElements, function(testElement){
    return testElement.nodeName === 'DIV';
});

(8)Document.hasFocus() 方法返回一个 Boolean,表明当前文档或者当前文档内的节点是否获得了焦点。该方法可以用来判断当前文档中的活动元素是否获得了焦点。

 var info = document.getElementById("message");
 if (document.hasFocus()) {
    info.innerHTML = "该页面获得了焦点.";
   }
   else {
    info.innerHTML = "该页面没有获得焦点.";
  }

(9)ParentNode.prepend 方法可以在父节点的第一个子节点之前插入一系列Node对象或者DOMString对象。DOMString会被当作Text节点对待(也就是说插入的不是HTML代码)。

var parent = document.createElement("div");
var p = document.createElement("p");
var span = document.createElement("span");
parent.append(p);
parent.prepend(span);

console.log(parent.childNodes); // NodeList [ <span>, <p> ]

3、事件
(1)DOMContentLoaded 事件 当纯HTML被完全加载以及解析时,DOMContentLoaded 事件会被触发,而不必等待样式表,图片或者子框架完成加载。

document.addEventListener('DOMContentLoaded', (event) => {
    console.log('DOM fully loaded and parsed'); // 译者注:"DOM完全加载以及解析"
});

(2)readystatechange 当文档的 readyState 属性发生改变时,会触发 readystatechange 事件。

document.addEventListener('readystatechange', (event) => {
    log.textContent = log.textContent + `readystate: ${document.readyState}\n`;
});

(3)selectionchange 事件在文档上的当前文本选择被改变时触发

document.addEventListener("selectionchange", () => {
  console.log(document.getSelection());
});

(4)selectstart 事件在用户开始一个新的选择时候触发。如果事件被取消,选择将不被触发。

document.addEventListener("selectstart", function() {
  console.log('Selection started'); 
}, false);

(5)copy 事件在用户复制文本时触发

document.addEventListener('copy', (event) => {
    console.log('copy action initiated')
});

(6)cut 剪切事件

document.addEventListener('cut', (event) => {
    console.log('cut action initiated')
});

3、DocumentFragment

文档片段接口,一个没有父对象的最小文档对象。它被作为一个轻量版的 Document 使用,就像标准的document一样,存储由节点(nodes)组成的文档结构。与document相比,最大的区别是DocumentFragment 不是真实 DOM 树的一部分,它的变化不会触发 DOM 树的重新渲染,且不会导致性能等问题。

const list = document.querySelector('#list');
const fruits = ['Apple', 'Orange', 'Banana', 'Melon'];

const fragment = document.createDocumentFragment();

fruits.forEach(fruit => {
  const li = document.createElement('li');
  li.innerHTML = fruit;
  fragment.appendChild(li);
});

list.appendChild(fragment);

4、DOMTokenList

DOMTokenList 接口表示一组空格分隔的标记(tokens)。如由 Element.classList、HTMLLinkElement.relList、HTMLAnchorElement.relList 或 HTMLAreaElement.relList 返回的一组值。

(1)Element.classList
是一个只读属性,返回一个元素的类属性的实时 DOMTokenList 集合。相比将 element.className 作为以空格分隔的字符串来使用,classList 是一种更方便的访问元素的类列表的方法。

const div = document.createElement('div');
div.className = 'foo';

// 初始状态:<div class="foo"></div>
console.log(div.outerHTML);

// 使用 classList API 移除、添加类值
div.classList.remove("foo");
div.classList.add("anotherclass");

// <div class="anotherclass"></div>
console.log(div.outerHTML);

// 如果 visible 类值已存在,则移除它,否则添加它
div.classList.toggle("visible");

// add/remove visible, depending on test conditional, i less than 10
div.classList.toggle("visible", i < 10 );

console.log(div.classList.contains("foo"));

// 添加或移除多个类值
div.classList.add("foo", "bar", "baz");
div.classList.remove("foo", "bar", "baz");

// 使用展开语法添加或移除多个类值
const cls = ["foo", "bar"];
div.classList.add(...cls); 
div.classList.remove(...cls);

// 将类值 "foo" 替换成 "bar"
div.classList.replace("foo", "bar");

(2)HTMLLinkElement.relList
是一个只读属性,返回一个元素的类属性的实时 DOMTokenList 集合。相比将 element.link作为以空格分隔的字符串来使用,relList是一种更方便的访问元素的类列表的方法。

var links = document.getElementsByTagName("link");
var length = links.length;
for (var i = 0; i < length; i++) {
  var list = links[i].relList;
  var listLength = list.length;
  console.log("New link found.");
  for (var j = 0; j < listLength; j++) {
    console.log(list[j]);
  }
}

(3)HTMLAnchorElement.relList
是一个只读属性,返回一个元素的类属性的实时 DOMTokenList 集合。相比将 element.a作为以空格分隔的字符串来使用,relList是一种更方便的访问元素的类列表的方法。

var anchors = document.getElementsByTagName("a");
var length = anchors.length;
for (var i = 0; i < length; i++) {
  var list = anchors[i].relList;
  var listLength = list.length;
  console.log("New anchor node found with", listLength, "link types in relList.");
  for (var j = 0; j < listLength; j++) {
    console.log(list[j]);
  }
}

(4)HTMLAreaElement.relList
是一个只读属性,返回一个元素的类属性的实时 DOMTokenList 集合。相比将 element.area作为以空格分隔的字符串来使用,relList是一种更方便的访问元素的类列表的方法。

var areas = document.getElementsByTagName("area");
var length = areas.length;

for (var i = 0; i < length; i++) {
  var list = areas[i].relList;
  var listLength = list.length;
  console.log("New area found.");
  for (var j = 0; j < listLength; j++) {
    console.log(list[j]);
  }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值