DOM
简介
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"); |
默认情况下,加载完会设置body为焦点元素,没有加载完时是null。
使用hasFocus()方法判断是否有焦点
Element类型
element是元素类型,例如li,p,html等都是元素。他们具有以下特征:
- nodeType=1
- nodeName是元素标签名,也可以使用tagName进行获取
- nodeValue=null
- parentNode=Document或Element
- 子节点可以使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 <strong>other</strong> 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属性,增添了一些新的功能。
- document.readyState: 他有两个值,loading和complete表示文档加载状态。
- document.compatMode: 表示当前位于什么渲染模式,有标准模式和混杂模式。标准模式是”CSS1Compat”,混杂模式是”BackCompat”.
- document.head: 可以通过这个访问
<head>
元素 - 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"> |
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:高度
滚动尺寸
使用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; |
遍历方法
- nextNode()/previousNode(): 返回深度优先搜索中的前一步或者后一步,如果到了根节点再调用previousNode会返回NULL, 同样如果到了最后一个节点再调用nextNode会返回null
TreeWalker
TreeWalker是NodeIterator的升级版,除了前面所说的方法外,还有一些额外的方法
- document.createTreeWalker(): 创建TreeWalker
- parentNode(): 遍历到父节点
- firstChild(): 遍历到当前节点的子节点
- lastChild()
- nextSibling()
- previousSibling()