1. H5自定义属性
目的:用于保存并使用数据。有些数据可以保存到页面中而不用保存到数据库中。
由于有些自定义属性很容易引起歧义,难以判断是元素内置属性还是自定义属性。
H5给我们新增了自定义属性:
1.1 设置H5自定义属性
H5规定自定义属性 date-开头作为属性名并且赋值
如:
<div date-index = "1" ></div>
- 兼容性获取
element.getAttribute('data-index')
- H5新增方法
element.dataset.index
或element.dataset['index']
获取。(ie11或以上才能使用) - 如果自定义属性里面有多个链接的单词,则使用驼峰命名法获取。
Warning
对于 element.dataset.index
和 element.dataset['index']
方法,注意 index
和 data-index
中的要对应。
<body>
<div getTime="20" data-index="2" data-list-name="lili"></div>
<script>
var div = document.querySelector('div');
console.log(div.getTime); //undefine
console.log(div.getAttribute('getTime')); //20
console.log(div.getAttribute('data-list-name')); //这种方法直接这样写
div.setAttribute('data-time', 20);
// H5新增的获取自定义属性的方法
// dataset是一个集合,里面存放了所有以data开头的自定义属性
console.log(div.dataset); //有两个 2和20
console.log(div.dataset.index); //2 (取其中的index)
// 如果自定义属性里有多个单词,新增方法用驼峰法获取
console.log(div.dataset.listName); //lili
console.log(div.dataset['listName']); //lili 也可以这样表示
</script>
</body>
2. 节点操作
2.1 为什么学习节点操作
获取元素通常使用两种方式:
-
利用DOM提供的方法获取元素,缺点:逻辑性不强、繁琐
document.getElementByld()
document.getElementsByTagName()
document.querySelector()
-
利用 节点层级关系 获取元素
- 利用父子兄节点关系获取元素
- 优点:逻辑性强
- 缺点:兼容性稍差
两种方式都可以获取节点,但节点操作更简单
2.2 节点概述
网页中的所有内容都是节点(标签、属性、文本、注释等),在DOM中,节点使用 node
来表示。
HTML DOM 树中的所有节点均可通过 JavaScript 进行访问,所有 HTML 元素(节点)均可被修改,也可以创建或删除。
一般地,节点至少拥有 nodeType
(节点类型)、nodeName
(节点名称)和 nodeValue
(节点值)这三个基本属性。
三种基本节点类型:
- 元素节点
nodeType
为1
- 属性节点
nodeType
为2
- 文本节点
nodeType
为3
(文字、空格、换行)
实际开发中,主要操作的还是元素节点。
2.3 节点层级
2.3.1 父级节点
获取离元素最近的父节点,若找不到则返回 null
。
node.parentNode
<body>
<div class="box">
<div class="erweima">x</div>
</div>
<script>
// 1. 父节点 parentNode
var erweima = document.querySelector('.erweima');
// var box =document.querySelector('.box'); //之前的方法要这样获取父元素
// 得到的是离元素最近的父级节点 如果找不到父节点就返回为null
console.log(erweima.parentNode);
</script>
</body>
2.3.2 子级节点
parentNode.childNodes
parentNode.childNodes
返回包含指定 节点的子节点的集合,该集合为即时更新的集合。
注意:parentNode.childNodes
包含了所有的子节点,包括元素节点,文本节点等。 如果只想要获得里面的元素节点,则需要专门处理。所以我们一般不提倡使用 childNodes
。
// 筛选元素节点
var ul = document.querySelector('ul');
for (var i = 0; i < ul.childNodes.length; i++) {
if (ul.childNodes[i].nodeType === 1) {
// ul.childNodes[i]是元素节点
console.log(ul.childNodes[i]);
}
}
更加好的方法:获取子元素节点
parentNode.children; (非标准)
parentNode.children是一个只读属性,返回所有的子元素节点。它只返回子元素节点,其余节点不返回(重点掌握)
<body>
<ul>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
</ul>
<script>
// 2. 子节点
// (1)childNodes 获取所有的子节点 包含 元素节点 文本节点等
// DOM提供的方法(API)获取
var ul = document.querySelector('ul');
var lis = document.querySelectorAll('li');
// 节点法获取
console.log(ul.childNodes); //[text, li, text, li, text, li, text, li, text]
console.log(ul.childNodes[0].nodeType); //3 索引号0是text 文本节点 为3
console.log(ul.childNodes[1].nodeType); //1 索引号0是li 元素节点 为1
// (2)children获取所有的子元素节点 实际开发中常用
console.log(ul.children); //[li, li, li, li]
</script>
</body>
子节点方法:
① 获取 所有结点 中的第一个和最后一个:
parentNode.firstChild
parentNode.lastChild
② 获取 元素节点中第一个和最后一个
parentNode.firstElementChild
parentNode.lastElementChild
(IE9或以上浏览器可用)
③ 实际开发:
parentNode.children[0]
parentNode.children[parentNode.children.length -1]
<body>
<ol>
<li>我是li1</li>
<li>我是li2</li>
<li>我是li3</li>
<li>我是li4</li>
</ol>
<script>
var ol = document.querySelector('ol');
// 1. firstChild 第一个子节点(包括元素节点、属性节点、文本节点)(lastChild一样)
console.log(ol.firstChild); //返回的是text
console.log(ol.lastChild); //返回的是text
// 2. firstElementChild 第一个字元素节点(只包括元素节点)
console.log(ol.firstElementChild); //返回的是第一个li
console.log(ol.lastElementChild); //返回的是最后一个li
// 3. 实际开发的写法,既没有兼容性问题又返回第一个子元素
console.log(ol.children[0]); //第一个li
console.log(ol.children[ol.children.length - 1]);//最后一个li
</script>
</body>
2.3.3 兄弟节点
-
返回下一个兄弟节点:
node.nextSibling
-
返回上一个的兄弟节点:
node.previousSibling
nextsibling
/previousSibling
返回当前元素的下/上一个兄弟节点,找不到则返回null
。这个兄弟节点可能是是所有的节点之一(即元素节点、文本节点等)。 -
返回下一个 兄弟元素节点(≥IE9)
node.nextElementSibling
-
返回上一个 兄弟元素节点(≥IE9)
node.previousElementSibling
nextElementSibling
/previousElementSibling
返回当前元素下/上一个兄弟元素节点,找不到则返回null
。
注意:这两个方法(nextElementSibling
/ previousElementSibling
)有兼容性问题,IE9 以上才支持。
<body>
<p></p>
<div>我是div</div>
<span>我是span</span>
<script>
// 3. 兄弟节点
var div = document.querySelector('div');
// nextSibling 下一个兄弟节点,包含 元素节点 文本节点等
console.log(div.nextSibling); //text
// nextElementSibling 下一个兄弟元素节点
console.log(div.nextElementSibling); //得到的是span
// previousSibling 上一个兄弟节点,包含 元素节点 文本节点等
console.log(div.previousSibling); //text
// previousElementSibling 上一个兄弟元素节点
console.log(div.previousElementSibling); //得到的是p
</script>
</body>
5. 如何解决兼容性问题:封装一个函数。
// 处理 <IE9 兼容性问题
function getNextElementSibling(element) {
var el = element;
while (el = el .nextSibling) {
if (el .nodeType === 1) {
return el ;
}
}
return null;
}
2.4 创建并添加节点
document.createElement("tagName");
document.createElement()
方法创建由 tagName
指定的 HTML 元素。因为这些元素原先不存在,是根据我们的需求动态生成的,所以我们也称为 动态创建元素节点。
在创建了元素节点后,还需要将节点添加到页面中。appendChild()
方法可以给元素节点添加子元素节点,若某元素已存在则重复添加,在页面已存在的元素 后面追加新节点。(无需引号)
parentNode.appendChild(chileNode)
类似的,也可以使用 insertBefore()
方法在指定元素的前面插入节点。(无需引号)
let insertedNode = parentNode.insertBefore(newNode, referenceNode)
<body>
<ul>
<li>lili</li>
</ul>
<script>
// 1. 创建元素节点
var li = document.createElement('li');
// 2. 添加节点 node.appendChild(child) node 父级 child 子级 后面追加元素,类似于数组中的push
var ul = document.querySelector('ul');
ul.appendChild(li); //后面添加
// 3. 添加节点 node.insertBefore(child,指定元素)
var lili = document.createElement('li');
ul.insertBefore(lili, ul.children[0]); //前面添加
</script>
</body>
参数说明:
insertedNode
:同newNode
,待插入的新节点。parentNode
:被插入的父节点。newNode
:待插入的新节点。referenceNode
:指定元素节点,新节点将插入到该元素节点的前面。
Tip. 在一个页面中要添加元素节点,先创建节点,然后添加节点。
2.5 删除节点
DOM 提供了 removeChild(childNode)
方法,删除一个子节点,并返回删除的节点。其中 childNode
为待删除的子节点。
// 方式一
parentNode.removeChild(childNode);
// 方式二
let oldChild = parentNode.removeChild(childNode);
<body>
<button>删除</button>
<ul>
<li>熊大</li>
<li>熊二</li>
<li>光头强</li>
</ul>
<script>
// 1. 获取元素
var ul = document.querySelector('ul');
var btn = document.querySelector('button');
// 2. 注册事件
// 点击按钮 依次删除里面的孩子
btn.onclick = function () {
if (ul.children.length == 0) {
this.disabled = true;
} else {
ul.removeChild(ul.children[0]);
}
}
</script>
</body>
2.6 复制节点
node.cloneNode()
方法返回调用该方法的节点的一个副本。也称为克隆节点/拷贝节点。其中 node
为被克隆的元素节点。
node.cloneNode([deep]);
或者
let newClone = node.cloneNode([deep]);
对于 deep
参数,可以为 true
或 false
,或为空的:
deep 参数值 | 含义 |
---|---|
true | 深拷贝,同时复制节点本身和里面的子节点 |
false | 浅拷贝,只复制节点本身,不复制子节点 |
空 | 空,同 false |
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
var ul = document.querySelector('ul');
// var lili = ul.children[0].cloneNode();//在最后面只生成一个点 . 不生成文字(浅拷贝)
var lili = ul.children[0].cloneNode([true]);//在最后面生成了 . 1(深拷贝)
ul.appendChild(lili);
</script>
</body>
2.7 三种动态创建元素的区别
document.write()
element.innerHTML
element.createElement()
区别:
document.write()
创建元素,是直接将内容写入页面的内容流,但是 当文档流执行完毕,会导致页面全部重绘。即覆盖原本的页面。innerHTML
是将内容写入某个 DOM 节点,不会导致页面全部重绘。innerHTML
创建多个元素效率更高(不要拼接字符串,采取数组形式拼接),结构稍微复杂。createElement()
创建多个元素效率稍低一点点,但是结构更清晰。
总结:不同浏览器下,innerHTML
效率要比 creatElement
高。
<body>
<button>点击</button>
<p>abc</p>
<div class="inner"></div>
<div class="create"></div>
<script>
// window.onload = function() {
// document.write('<div>123</div>');
// }
// 三种创建元素方式区别
// 1. document.write() 创建元素 如果页面文档流加载完毕,再调用这句话会导致页面重绘
// var btn = document.querySelector('button');
// btn.onclick = function() {
// document.write('<div>123</div>');
// }
// 2. innerHTML 创建元素
var inner = document.querySelector('.inner');
// for (var i = 0; i <= 100; i++) {
// inner.innerHTML += '<a href="#">百度</a>'
// }
var arr = [];
for (var i = 0; i <= 100; i++) {
arr.push('<a href="#">百度</a>');
}
inner.innerHTML = arr.join('');
// 3. document.createElement() 创建元素
var create = document.querySelector('.create');
for (var i = 0; i <= 100; i++) {
var a = document.createElement('a');
create.appendChild(a);
}
</script>
</body>
3. DOM 学习总结
关于 dom 操作,我们主要针对于元素的操作。主要有创(建)、增、删、改、查、属性操作、事件操作。
3.1 创
document.write
innerHTML
createElement
3.2 增
appendChild
insertBefore
3.3 删
removeChild
3.4 改
主要修改 dom 的元素属性,dom 元素的内容、属性,表单的值等。
- 修改元素属性:
src
、href
、title
等 - 修改普通元素内容:
innerHTML
、innerText
- 修改表单元素:
value
、type
、disabled
等 - 修改元素样式:
style
、className
3.5 查
主要获取查询dom的元素
- DOM提供的API 方法:
getElementById
、getElementsByTagName
(古老用法不太推荐) - H5提供的新方法:
querySelector
、querySelectorAll
提倡 - 利用节点操作获取元素:父(
parentNode
)、子(children
)、兄(previousElementSibling
、nextElementSibling
)提倡
6.6 属性操作
主要针对于自定义属性
setAttribute
:设置dom的属性值getAttribute
:得到dom的属性值removeAttribute
:移除属性
6.7 事件操作
给元素注册事件,格式:事件源.事件类型 = 事件处理程序
鼠标事件 | 触发条件 |
---|---|
onclick | 鼠标点击左键触发 |
onmouseover | 鼠标经过触发 |
onmouseout | 鼠标离开触发 |
onfocus | 获得鼠标焦点触发 |
onblur | 失去鼠标焦点触发 |
onmousemove | 鼠标移动触发 |
onmouseup | 鼠标弹起触发 |
onmousedown | 鼠标按下触发 |