简介

DOM(文档对象模型)是HTML和XML的编程接口,可以通过它查找和修改每一个节点的属性。DOM的表现形式类似于一棵树

document节点是每个文档的根节点,而html是它唯一的子节点,其他元素都在这个文档内。总共有12中类型的节点。这些节点统一由Node定义。

  • Node.nodeType:定义了节点的类型,总共有十二种类型
    • Node.ELEMENT_NODE(1): 元素,如html,head等
    • Node.ATTRIBUTE_NODE(2): 属性,例如图中的href
    • Node.TEXT_NODE(3): 元素中间的文本
    • Node.CDATA_SECTION_NODE(4)
    • Node.ENTITY_REFERENCE_NODE(5)
    • Node.ENTITY_NODE(6)
    • Node.PROCESSING_INSTRUCTION_NODE(7)
    • Node.COMMENT_NODE(8)
    • Node.DOCUMENT_NODE(9)
    • Node.DOCUMENT_TYPE_NODE(10)
    • Node.DOCUMENT_FRAGMENT_NODE(11)
    • Node.NOTATION_NODE(12)

访问节点:

  • Node.nodeName: 节点的名字,例如html,href等
  • Node.nodeValue: 节点的值,对于元素而言,节点的值始终是null
  • Node.childNodes: 节点的所有子节点
  • Node.parentNode: 父节点
  • Node.previousSibling: 前一个兄弟节点
  • Node.nextSibling:
  • Node.firstChild
  • Node.lastChild
  • Node.hasChildNodes():
  • childElementCount: 返回子元素的数量(不包括文本节点和注释)
  • firstElementChild: 第一个元素节点
  • lastElementChild:
  • previousElementSibling: 后一个Element节点
  • classList: 获得该节点下所有类名

操纵节点:

  • Node Node.appendChild(): 添加子节点到末尾,返回新添加的节点。如果添加原来就在DOM树上的节点作用相当于移动。
  • Node Node.insertBefore(child, now): 在now前插入
  • Node Node.replaceChild(new_node, now_node): 将现有节点替换成新节点
  • Node Node.removeChild(node): 移除该节点并返回。
  • Node Node.cloneNode(bool is_deep_clone): 复制节点,参数为是否深复制。如果是深复制,那么会复制整个dom树。否则只会复制当前节点并不指定父节点

查找元素

可以通过getElementById()和getElementsByTagName()查找特定的元素。

  • getElementById(id): 通过id查找对应的元素,例如
    <img id="test" src="...">...</img>
    let img = document.getElementById("test);
    console.log(img.src);
  • getElementsByTagName(): 查找该文档下所有该元素名的元素,如getElementsByTagName("p");。此外,获得之后可以通过中括号访问对应name的元素。以下面的文档为例:let inputs = getElementsByTagName("input"); console.log(inputs["color"]);
  • getElementByClassName(): 通过类名获得元素
  • getElementsByName(): 通过name获得元素,例如
    <fieldset>
    <legend>Which color do you Prefer?</legend>
    <ul>
    <li>
    <input type="radio" value="red" name="color" id="colorRed">
    <label for="colorRed">Red</label>
    </li>
    <li>
    <input type="radio" value="green" name="color" id="colorGreen">
    <label for="colorGreen">Green</label>
    </li>
    </ul>
    </fieldset>

    getElementsByName("color");

  • document.anchors: 返回所有带name属性<a>元素
  • document.images: 返回所有<img>元素
  • document.links: 返回所有带href<a>元素

焦点管理

通过document.activeElement可以获得当前获得焦点的DOM元素。并且可以通过捕捉用户输入然后调用focus()方法让某个元素获得焦点。

let button = document.getElementById("myButton");
button.focus();
console.log(document.activeElement == button);//true

默认情况下,加载完会设置body为焦点元素,没有加载完时是null。

使用hasFocus()方法判断是否有焦点

Element类型

element是元素类型,例如li,p,html等都是元素。他们具有以下特征:

  1. nodeType=1
  2. nodeName是元素标签名,也可以使用tagName进行获取
  3. nodeValue=null
  4. parentNode=Document或Element
  5. 子节点可以使Element、Text、Comment(注释)等

HTMLElement继承于Element,他除了上述属性外还有一些额外的属性:

  • id: 标识符
  • title: 一些额外信息,以提示条的形式展示
  • lang: 语言,很少用
  • dir: 语言书写方向,有”ltr”和”rtl”
  • className: 也就是class属性,由于class是关键字所以使用className代替

属性操作

属性也就是元素中的一些标签,如src,id,class等。js提供了一系列的方法来操作属性。属性名不分大小写

  • getAttribute(name): 通过属性名获得属性,例如element.getAttribute("id");。style属性会返回一个css字符串,而通过DOM访问会返回一个css对象。事件处理函数相同,如果使用getAttribute()会返回函数的字符串,而使用DOM访问会返回函数
  • setAttribute(name, value): 设置属性
  • insertAdjacentHTML(location, str): 在某个位置插入html字符串,location有
    • beforebegin: 插入当前元素的前面,作为一个兄弟节点
    • afterbegin: 插入当前元素内部,作为第一个子节点
    • beforeend: 当前元素后面
    • afterend: 当前元素最后一个子节点

此外,我们可以直接为DOM对象中的属性赋值,例如:

div.id = "other_id";
div.align = "left";

Element提供了attributes操作所有元素的属性。它使NamedNodeMap类型,每个属性都是里面的一个节点。每个节点都可以使用nodeValue获得值或者设置值。下面是一些操作方法:

  • getNamedItem(name): 返回属性名的node节点
  • removeNamedItem(name): 删除节点
  • setNamedItem(node): 设置属性节点,如果属性名没有就添加,有就覆盖
  • item(pos): 返回pos处的节点

例如

let id = element.attributes.getNamedItem("id").nodeValue;
简写
let id = element.attributes['id'].nodeValue;
//删除属性
element.attributes.removeNamedItem("id");

创建元素

可以使用document.crateElement(element_name)创建元素,参数为创建元素的标签名,例如创建div可以用document.createElement('div');

创建元素后可以便捷的添加属性,如:

div.id = "newDiv";
div.className = "box";

创建完成之后可以使用前面提到的appendChild等函数添加到DOM树中。

Text类型

Text是文本节点,它既可能是文本,也可能是转义后的HTML字符。

  • nodeType = 1
  • nodeName = “#text”
  • nodeValue = 文本
  • parentNode = Element

例如:

<html>
<head>
<title>hello world</title>
</head>
<body>
<p>test</p>
</body>
</html>
hello world和test都是文本节点,title的parentNode是title

text节点有一系列的文本操作方法:

  • appendData(text): 在末尾追加
  • deleteData(offset, count): 在offset处删除count个字符
  • insertData(offset, text): 在offset插入text
  • replaceData(offset, count, text): 用text替换offset开始的count个字符
  • splitText(offset): 从offset处将文本节点拆分成两个文本节点。原
  • substringData(offset, count): 提取offset到offset+count的文本
  • length:

除了上述方法进行操作外,我们还可以直接通过nodeValue进行修改,这些修改都会立刻反映。

<div>hello world</div>
let textNode = div.firstChild;
div.firstChild.nodeValue = "other message";
div.firstChild.nodeValue = "Some <strong>other</strong> message";
//会转义,输出为 Some &lt;strong&gt;other&lt;/strong&gt; message

jQuery

jQuery可以让我们以css选择器的形式快速对元素进行定位

搜索

  • querySelector(str): 接收css选择符,返回该模式下第一个后代元素,没有返回null。他会从当前节点的子节点开始搜索
    let selected = document.querySeelctor(".selected");//搜索selected类的第一个元素
    let img = document.body.querySelector("img.button");//选择类名为button的图片
  • querySelectorAll(str): 他会返回所有匹配的节点,返回格式为NodeList
  • match(str): 存在返回true,不存在返回false

HTMLDocument扩展

HTML5扩展了HTMLDocument属性,增添了一些新的功能。

  1. document.readyState: 他有两个值,loading和complete表示文档加载状态。
  2. document.compatMode: 表示当前位于什么渲染模式,有标准模式和混杂模式。标准模式是”CSS1Compat”,混杂模式是”BackCompat”.
  3. document.head: 可以通过这个访问<head>元素
  4. document.characterSet: 字符集,默认值是”UTF-16”

自定义属性

html5允许元素指定非标准的属性,但要是用data-告诉浏览器这是自定属性,例如:
<div id="myDiv" data-appid="12345" data-myName="Nicholas"></div>

自定属性可以通过dataset访问。dataSet是DOMStringMap实例,包含键值对映射。每个属性都可以使用data-name中的那么进行访问。

例如

let div = document.getElementById("myDiv");
let appid = div.dataset.appId;
let myName = div.dataset.myName;

innerHTML和outerHTML

innerHTML返回该元素所有后代的字符串,如果我们进行插入,他会把字符串清空变成我们插入的字符串,例如:

<div id="content">
<p>This is a <strong>paragraph</strong> with a list following it.</p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>

querySelector("#content").innerHTML

//返回
<p>This is a <strong>paragraph</strong> with a list following it.</p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>

document.body.getElementById("content").innerHTML = "hello world";

原结构变成
<div id="content">hello world</div>

outerHTML会返回调用者的HTML字符串,它的使用方式和innerHTML类似

滚动

  • scrollIntoView(alignToTop, scrollIntoViewOptions):
    • alignToTop: 如果是true,那么滚动后和窗口顶部平齐,否则和窗口底部平齐
    • scrollIntoViewOptions: 是一个对象,里面有三个属性。behavior,block,inline
      • behavior: 定义过渡动画,有”smooth”, “auto”
      • block: 定义垂直方向对齐,有”start”,”center”,”end”,”nearest”
      • inline: 定义水平方向对齐,和垂直方向相同
        document.froms[0].scrollIntoView();
        document.forms[0].scrollIntoView(true, {block: "start"});

尺寸

元素的布局如图所示

设置margin和border

  • offsetHeight: border的高度(包括里面的)
  • offsetWidth: border的宽度
  • offsetLeft: border到margin左边界的距离
  • offsetTop: border到margin上边界的距离
  • offsetParent: 当前节点的偏移量上的父节点

要计算一个元素在页面中的偏移量,需要把自己和所有父节点的offsetLeft和offsetTop相加

function getElementL
ocation(element)
{
let left = element.offsetLeft;
let top = element.offsetTop;
let current = element.offsetParent;
while(current !== null)
{
left += current.offsetLeft;
top += current.offsetTop;
current = current.offsetParent;
}
}

padding

  • clientWidth: padding整体宽度(包括最里面的)
  • clientHeight:高度

滚动尺寸

  • scrollHeight: 没有滚动条出现时元素总高度
  • scrollWidth: 总宽度
  • scrollLeft: 距离最左边的长度(包括隐藏的)
  • scrollTop: 距离最上面的长度

    样式设置

使用js可以动态修改样式表,样式表对应的元素属性是style。任何支持style属性的元素在js中都有一个对应的style属性,可以通过style对该元素样式表进行修改。

样式表创建共有三种方法:通过<link>创建、通过<style>创建、通过style属性创建。

样式表在装潢为js变量时名字有所改变,连字符全部删除,然后后面变成大写。例如:

background-image //js: style.backgroundImage
font-family //js: style.fontFamily

大多数属性名都是直接进行转换,但是float不能进行转换,因为它是关键字。因此在js的style中将它命名为cssFloat

属性和方法:

  • cssText: 返回css字符串,可以通过它一次性设置所有属性,例如:
    myDiv.style.cssText = "width: 25px; height: 100px; background-color: green";
    console.log(myDiv.style.cssText);
  • length: 返回属性数量
  • parentRule: 返回CSSRule对象
  • getPropertyPriority(propertyName): 如果使用了!important,则返回important,否则返回空字符串
  • getPropertyValue(name): 返回属性的字符串值
  • item(index): 返回index的属性名
  • removeProperty(name):
  • setProperty(name, value, priority): 设置属性

可以使用length对css属性进行遍历

let prop, value;
for(let i=0, len=myDiv.style.length; i<len; i++)
{
prop = myDiv.style[i];
value = myDiv.style.getPropertyValue(prop);
console.log(`prop: ${value}`);
}

style中只包含style属性为设个属性设置的值,不包含其他样式表继承过来的值,因此设置可能回合最终结果有偏差.可以通过getComputedStyle()获得最终结果,

  • getComputedStyle(element, str): 返回值是一个只读的style类型。element是想要获得可计算属性的元素,str是伪元素字符串(”:after”等)

例如

let myDive = document.getElementById("myDiv");
let computeStyle = document.defaultView.getComputedStyle(myDiv, null);

操作样式表

除了修改style提供的样式外,另外两种方法提供的样式我们也可以进行操作。HTMLLinkElement是通过<link>创建的样式表, HTMLStyleElement是通过<style>创建的样式表。CSSStyleSheet是通用样式表,可以表示上面两种,他只有一个属性可读写。

CSSStyleSheet中的属性:

  • disabled: 表示样式表是否被禁用,可读写
  • href: 如果是<link>形式创建的样式表,返回url
  • media: 表示支持媒体的集合,有一个length属性和item()方法
  • ownerNode: 拥有该样式表的节点,要么是<link>要么是<style>,如果是@import形式导入的那么是null
  • parentStyleSheet: 指向包含它的样式表
  • title: ownerNode的title属性
  • type: 样式表的类型
  • cssRules: 规则集合
  • ownerRule: 如果是由@import导入,则返回父样式表规则集合
  • deleteRule(index):
  • insertRule(rule, index);

规则

CSSRule表示一条规则, 一个选择器加样式就是一条规则。一般使用的是CSSStyleRule(其他@import,@font-face等很少用js操作)。下面是CSSStyleRule的属性

  • cssText: 返回规则的文本
  • parentRule: 如果被其他规则包含,返回父规则,否则返回null
  • parentStyleSheet: 返回当前样式表
  • style
  • type: 表示规则类型,值固定为1
  • selectorText: 返回规则的选择符文本,也就是# .等选择器

例如:

let sheet = document.styleSheets[0];
let rules = sheet.cssRules || sheet.rules;
let rule = rules[0];
rule.style.backgroundColor = "red";

  • insertRule(str, index): 添加新的规则,例如sheet.insertRule("body {background-color: silver}", 0);
  • deleteRule(index):

遍历

使用NodeIterator和TreeWalker可以进行深度优先遍历

NodeIterator

可以使用document.createNodeIterator(root, whatToshow, filter)构建实例

  • root: 根节点
  • whatToShow: 指定访问哪些类型的节点
    • NodeFilter.SHOW_ALL: 访问所有节点
    • NodeFilter.SHOW_ELEMENT: 元素节点
    • NodeFileter.SHOW_ATTRIBUTE: 属性节点,DOM中不需要
    • SHOW_TEXT: 文本节点
    • SHOW_CDATA_SECTION: CData节点
    • SHOW_ENTITY_REFERENCE: 实体引用节点
    • SHOW_ENTITY: 实体节点
    • SHOW_PROCESSING_INSTRUCTION: 处理指令节点
    • SHOW_COMMENT: 注释节点
    • SHOW_DOCUMENT: 文档节点
    • SHOW_DOCUMENT_TYPE: 文档类型节点
    • SHOW_DOCUMENT_FRAGMENT: 文档片段节点
    • SHOW_NOTATION: 记号节点
  • filter: NodeFilter对象或函数,表示应该接受或跳过节点类型

NodeFilter是一个抽象类,不能创建实例,只要实现acceptNode方法传给createNodeFilter()就可以了。acceptNode(node)返回值是NodeFilter.FILTER_ACCEPT或NodeFIlter.FILTER_SKIP。对该node进行筛选,如果是NodeFilter.FILTER_ACCEPT就接受。

let show = NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT;

let filter =
{
acceptNode(node)
{
return node.tagName.toLowerCase() == "p" ? NodeFilter.FILTER_ACCEPT : NodeFIlter.FILTER_SKIP;
}
}
let iter = document.createNodeIterator(root, NodeFilter.SHOW_ELEMENT, filter);
或者
let filter = function(node)
{
return node.tagName.toLowerCase() == "p" ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;

遍历方法

  • nextNode()/previousNode(): 返回深度优先搜索中的前一步或者后一步,如果到了根节点再调用previousNode会返回NULL, 同样如果到了最后一个节点再调用nextNode会返回null

TreeWalker

TreeWalker是NodeIterator的升级版,除了前面所说的方法外,还有一些额外的方法

  • document.createTreeWalker(): 创建TreeWalker
  • parentNode(): 遍历到父节点
  • firstChild(): 遍历到当前节点的子节点
  • lastChild()
  • nextSibling()
  • previousSibling()