Correcting teacher:天蓬老师
Correction status:qualified
Teacher's comments:图片做得不错, 事件机制是个难点, 其实理解了也非常简单是不是?
forEach
遍历(用 for
遍历). 访问 HTMLCollection 对象的元素的方法:HTMLCollection[index]
: 类似数组访问元素的方式.HTMLCollection.item(index)
: 通过 HTMLCollection 对象的 item(index)
方法.HTMLCollection.nameItem(元素name属性值)
: 通过 HTML Collection 对象的 nameItem()
方法, 传入的参数是要获取的类数组中的元素的 name 属性值.forEach
和 item 方法.NodeList.childNodes[0]
.NodeList.item(0)
.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DOM对象</title>
</head>
<body>
<form action="test1.php"></form>
<form action="test2.php"></form>
<form action="test3.php" id='loginForm' name="login">
<input type="text" name="username" id="username" value="zhangsan">
</form>
</body>
<script>
// 给console.log()函数绑定一个别名
var cl = console.log.bind(console);
// 1. 查看当前文档类型, 即<!DOCTYPE html>
cl(document.doctype);
// 2. 查看根节点, 即<html>元素
/* document并不代表根节点, document.documentElement才是 */
cl(document.documentElement);
// 3. 查看head部分, 即<head>元素
cl(document.head);
// 4. 查看body部分, 即<body>元素
cl(document.body);
/* ↑有了document.head和document.body, 就能使用上下级关系访问节点树中的子节点 */
// 5. 查看<title>元素
/* 方法1: 使用节点树获取 */
cl(document.head.title);
/* 方法2: 使用document对象的title属性获取 */
cl(document.title);
// 6. 获取当前HTML文档中的所有表单
// 6.1 获取表单对象
/* 返回值为: HTMLCollection对象, 类数组, 但并不是数组 */
/* 遍历HTMLCollection对象中的元素, 见temp */
cl(document.forms);
// 6.2 获取表单中的元素/获取HTMLCollection对象中的元素
/* 方法1: 采用类似数组的获取方式. ↓获取第2个表单 */
cl(document.forms[1]);
/* 方法2: 使用HTMLCollection提供的item(元素索引)方法. ↓获取第3个表单 */
cl(document.forms.item(2));
/* 方法3: 根据元素id获取表单(document.getElementById(id值)可以根据id获取所有元素) */
cl(document.getElementById('loginForm'));
/* 方法4: 使用HTMLCollection对象提供的nameItem(name属性值)来获取HTMLCollection中的元素 */
cl(document.forms.namedItem('login'));
// 6.3 获取某个表单中的表单控件的值
/* input:text控件: 表单对象.控件的name属性值.value */
cl(document.forms.item(2).username.value);
</script>
</html>
通过 document
对象, 利用 HTML 文档元素的树形结构, 可以使用”父级元素. 子级元素”的方式获取 DOM 元素; 也可以使用 document
中提供的一些方法获取到一些固定类型的 DOM 元素, 如: form
元素, title
元素等. 详见 DOM 对象的实例.
还是通过 document
对象提供的方法, 利用元素的属性值来获取 DOM 元素.
document.getElementsByTagName(元素标签名)
, 返回值: HTMLCollection 对象; document.getElementById(id值)
, 返回值: DOM 元素(Element 对象).document.getElementsByClassName(样式类名)
, 返回值: HTMLCollection 对象; document.getElementsByName(name属性值)
, 返回值: HTMLCollection 对象; document.querySelector(css选择器表达式)
, 返回值: DOM 元素(Element 对象), 若匹配到多个元素, 则返回第一个.document.querySelector(css选择器表达式)
, 返回值: 元素数组;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>获取DOM元素</title>
</head>
<body>
<ul class="list">
<li class="item" name="first">item1</li>
<li class="item" id="item2">item2</li>
<li class="item active">item3</li>
<li class="item">item4</li>
<li class="item" id="item2">item5</li>
</ul>
</body>
<script>
var cl = console.log.bind(console);
// 2.1 根据标签名来获取元素: `document.getElementsByTagName(元素标签名)`, 返回值: HTMLCollection 对象;
/* 获取所有li标签元素 */
var lis = document.getElementsByTagName("li");
cl(lis);
// 2.2 根据 id 属性值来获取元素: `document.getElementById(id值)`, 返回值: DOM 元素(Element 对象).
/* 获取id属性值='item2'的元素, 匹配多个也只取1个 */
var item2 = document.getElementById("item2");
cl(item2);
// 2.3 根据样式类来获取元素: `document.getElementsByClassName(样式类名)`, 返回值: HTMLCollection 对象;
/* 获取设置有.list样式类的元素 */
var ul = document.getElementsByClassName("list");
cl(ul);
/* 获取同时设置有.item和.active样式类的元素 */
var activeItem = document.getElementsByClassName("item active");
cl(activeItem);
// 2.4 根据 name 属性值来获取元素: `document.getElementsByName(name属性值)`, 返回值: HTMLCollection 对象;
/* 获取name属性值为fist的元素, 因返回值是类数组, 所以调用其item(0)方法获取到第一个元素 */
var firstItem = document.getElementsByName("first").item(0);
cl(firstItem);
// 2.5 根据 css 选择器来获取一个元素: `document.querySelector(css选择器表达式)`, 返回值: DOM 元素(Element 对象), 若匹配到多个元素, 则返回第一个.
/* 获取css选择器表达式是.item匹配到的第一个元素 */
var item = document.querySelector(".item");
cl(item);
// 2.6 根据 css 选择器来获取多个元素: `document.querySelector(css选择器表达式)`, 返回值: **元素数组**;
/* 获取css选择器表达式是.item匹配到的所有元素 */
var items = document.querySelectorAll(".item");
cl(items);
/* 获取css选择器表达式是".item:nth-child(-n + 3)"(即前三个)匹配到的所有元素 */
var items = document.querySelectorAll(".item:nth-child(-n + 3)");
cl(items);
/* 遍历这3个元素, 并赋给红色字体样式 */
items.forEach(function (item, index, items) {
item.style.color = "red";
});
</script>
</html>
element.childNodes
, 返回类型为 NodeList 类型数据, 取元素值, 跟数组类似, 有 length 属性, 有 forEach 和 item 方法.NodeList.childNodes[0]
.NodeList.item(0)
.element.nodeType
.element.nodeName
.element.nodeValue
. 只有文本节点才能获得值.element.firstChild
.element.lastChild
.element.previousSibling
.element.nextSibling
.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>节点类型</title>
</head>
<body>
<ul>
<li>item1</li>
<li>item2</li>
<li>item3</li>
<li>item4</li>
<li>item5</li>
</ul>
</body>
<script>
var cl = console.log.bind(console);
// 获取第一个ul元素
var ul = document.querySelector("ul");
// 1. 获取ul元素的所有类型的子节点
var nodes = ul.childNodes;
/* 包括5个元素类型节点和6个文本类型节点(回车换行) */
cl(nodes);
// 2. 遍历ul元素的子节点, 打印其节点属性信息
nodes.forEach(function (node, index, nodes) {
cl(
"第",
index + 1,
"个节点信息: 节点类型: ",
node.nodeType,
"; 节点名称: ",
node.nodeName,
"; 节点值: ",
node.nodeValue
);
});
// 3. 获取ul元素的第一个子节点
cl(ul.firstChild);
// 4. 获取ul元素的最后一个子节点
cl(ul.lastChild);
// 5. 获取ul元素的第5个子节点的上一个节点/下一个节点
cl(ul.childNodes.item(6).previousSibling);
cl(ul.childNodes.item(6).nextSibling);
</script>
</html>
element.className
以文本的形式返回元素的 class 属性的值.class
属性名也是 js 的保留字, 所以用 className
来获取 class 属性的值.element.classList
提供了很多便捷的操作 class 属性值的方法element.classList.add(样式类名)
: 为元素添加样式类.element.classList.remove(样式类名)
: 为元素移除样式类.element.classList.toggle(样式类名)
: 为元素以开关的方式增加/移除样式类.element.classList.replace(旧样式类名, 新样式类名)
: 为元素把旧样式替换成新样式, 若没有旧样式, 则直接添加新样式.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>className和classList操作class属性值</title>
<style>
.red {
color: red;
}
.bgc {
background-color: yellow;
}
.blue {
color: blue;
}
</style>
</head>
<body>
<p class="red">hello JavaScript!</p>
<h3>hello JS!</h3>
<script>
var cl = console.log.bind(console);
var p = document.querySelector("p");
// 获取<p>的class属性, 注意, class是js是保留字, 不能用, 所以用className代替
cl(p.clasName);
// 1. 使用className属性操作元素样式类
// 样式类(增,删,替换等)
p.className = "bgc";
p.className = "red bgc";
// 上面的方式很麻烦, 使用classList对象就容易操作了
var h3 = document.querySelector("h3");
// 2. 使用classList属性操作元素样式类
// 为元素添加样式类
h3.classList.add("red");
h3.classList.add("bgc");
// 移除元素的样式类
h3.classList.remove("bgc");
// 自动切换, 即当前如果有某个样式类, 则删除该样式类, 没有, 则添加该样式类
h3.classList.toggle("red");
// 替换样式类replace(旧样式类, 新样式类)
h3.classList.replace("bgc", "red");
</script>
</body>
</html>
data-
为前缀. 如: data-id
, data-username
等.element.dataset.去掉前缀的自定义属性名
.data-user-name
, 获取时: element.dataset.userName
.element.dataset.去掉前缀的自定义属性名 = 新值
.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>dataset对象:用于处理用户自定义的数据属性</title>
</head>
<body>
<!-- html5中, 可以使用自定义的数据属性保存标签的附加信息, 以"data-"为前缀 -->
<div id="user" data-id="1" data-user-name="zhangsan" data-email="zs@qq.com">用户信息</div>
<script>
var cl = console.log.bind(console);
// 获取元素
var user = document.querySelector('div');
// 1. 获取自定义属性值
// dataset对象用于获取用户自定义的数据属性。使用方法:dataset(去掉"data-"前缀的数据属性名)
cl(user.dataset.id);
cl(user.dataset.email);
// 多单词属性,把中间连接线删除,第二个单词字母大写,形成驼峰命名
cl(user.dataset.userName);
// 2. 设置自定义属性值
// 同样也可以用dataset设置用户自定义数据属性的值
user.dataset.email = 'zhangsan@qq.com';
cl(user.dataset.email);
</script>
</body>
</html>
js常用的事件: https://www.cnblogs.com/theblogs/p/9972319.html
以添加点击事件为例:
<button onclick="...">提交</button>
button.onclick = function(event) {...}
.element.addEventListener(事件类型, 回调函数, 事件传递机制)
. 事件传递机制见第7点.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>事件添加方式</title>
</head>
<body>
<!-- 方式1: onclick属性指定处理的js函数或表达式 -->
<!-- 1.1: 直接写js代码 -->
<button onclick="var text=this.innerText;alert(text);">按钮1</button>
<!-- 1.2: 调用js函数 -->
<button onclick="show(this)">按钮2</button>
<!-- 方式2: 给html元素添加属性 -->
<button>按钮3</button>
<!-- 方式3: 监听器方式 -->
<button>按钮4</button>
<!-- 方式4: 事件派发 -->
<button>按钮5</button>
</body>
<script>
// 给console.log起别名
var cl = console.log.bind(console);
/* 方式1.2 */
function show(element) {
var text = element.innerText;
alert(text);
}
/* 方式2: 给html元素添加onclick属性 */
var btn3 = document.querySelector('button:nth-of-type(3)');
btn3.onclick = function() {
alert(this.nodeName);
}
/* 方式3: 监听器方式 */
// 监听器可以认为是一个内部方法, 几乎所有元素都有. element.addEventListener(事件类型, 事件回调函数, 传递机制);
// 其中传递机制有两种, 一种是捕获(值为true), 一种是冒泡(值为false), 即: 冒泡阶段触发. 见demo7.html
var btn4 = document.querySelector('button:nth-of-type(4)');
btn4.addEventListener('click', function(){
alert(this.innerText);
}, false);
/* 方式4: 事件派发(可以模拟自动触发, 而不需要用户点击触发) */
// 先给按钮5添加一个点击事件
var btn5 = document.querySelector('button:nth-of-type(5)');
btn5.addEventListener('click', function(){
console.log(this.innerText);
}, false);
// 创建一个点击事件对象
var event = new Event('click');
// 派发给btn5(执行派发语句后, 立刻触发btn5的点击事件,不用用户点击)
btn5.dispatchEvent(event);
// 事件派发使用场景: 轮播图
</script>
</html>
事件传递机制有两种: 捕获和冒泡.
<html>
)开始, 一直传递到事件触发的最小后辈元素为止. 事件触发传递到的元素上绑定有事件对应的处理脚本的, 则执行事件处理脚本.<html>
)为止. 事件触发传递到的元素上绑定有事件对应的处理脚本的, 则执行事件处理脚本.事件传递之捕获:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>事件传递之捕获</title>
<style>
.first {
padding: 30px;
background-color: yellow;
}
.second {
padding: 30px;
background-color: red;
}
.third {
width: 100px;
height: 100px;
background-color: lightgreen;
}
</style>
</head>
<body>
<div class="first">
<div class="second">
<div class="third">事件传递</div>
</div>
</div>
</body>
<script>
var cl = console.log.bind(console);
// 事件捕获与冒泡(两种处理父子元素之间事件传递的方式)
// 当最内层元素节点被点击时, 认为是从最外层元素节点, 向最内层元素节点传递事件, 最后由最内层元素节点触发事件. 这个由外向内传递事件的过程叫捕获
// 当最内层元素节点被点击时, 认为是从最内层元素节点, 向最外层元素节点传递事件, 最后由最外层元素节点触发事件. 这个由内向外传递事件的过程叫冒泡
var first = document.querySelector('.first');
var second = document.querySelector('.second');
var third = document.querySelector('.third');
// 根元素添加点击事件
document.documentElement.addEventListener('click', function (ev) {
cl('捕获阶段:' + '触发元素:' + ev.target.className + ';', '事件绑定的元素:' + ev.currentTarget.nodeName);
}, true);
// body元素添加点击事件
document.body.addEventListener('click', function (ev) {
cl('捕获阶段:' + '触发元素:' + ev.target.className + ';', '事件绑定的元素:' + ev.currentTarget.nodeName);
}, true);
// addEventListener()第三个入参值为true,表示捕获阶段触发事件.
first.addEventListener('click', function (ev) {
// ev:事件对象(Event),可以通过它了解很多事件信息
// 事件类型(是点击,还是双击,等等)
cl(ev.type);
// 触发事件的元素
cl(ev.target);
// 绑定事件的元素
cl(ev.currentTarget);
cl('捕获阶段:' + '触发元素:' + ev.target.className + ';', '事件绑定的元素:' + ev.currentTarget.className);
}, true);
second.addEventListener('click', function (ev) {
// ev:事件对象(Event),可以通过它了解很多事件信息
// 事件类型(是点击,还是双击,等等)
cl(ev.type);
// 触发事件的元素
cl(ev.target);
// 绑定事件的元素
cl(ev.currentTarget);
cl('捕获阶段:' + '触发元素:' + ev.target.className + ';', '事件绑定的元素:' + ev.currentTarget.className);
}, true);
third.addEventListener('click', function (ev) {
// ev:事件对象(Event),可以通过它了解很多事件信息
// 事件类型(是点击,还是双击,等等)
cl(ev.type);
// 触发事件的元素
cl(ev.target);
// 绑定事件的元素
cl(ev.currentTarget);
cl('捕获阶段:' + '触发元素:' + ev.target.className + ';', '事件绑定的元素:' + ev.currentTarget.className);
}, true);
</script>
</html>
事件传递之冒泡:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>事件传递之冒泡</title>
<style>
.first {
padding: 30px;
background-color: yellow;
}
.second {
padding: 30px;
background-color: red;
}
.third {
width: 100px;
height: 100px;
background-color: lightgreen;
}
</style>
</head>
<body>
<div class="first">
<div class="second">
<div class="third">事件传递</div>
</div>
</div>
</body>
<script>
var cl = console.log.bind(console);
// 事件捕获与冒泡(两种处理父子元素之间事件传递的方式)
// 当最内层元素节点被点击时, 认为是从最外层元素节点, 向最内层元素节点传递事件, 最后由最内层元素节点触发事件. 这个由外向内传递事件的过程叫捕获
// 当最内层元素节点被点击时, 认为是从最内层元素节点, 向最外层元素节点传递事件, 最后由最外层元素节点触发事件. 这个由内向外传递事件的过程叫冒泡
var first = document.querySelector('.first');
var second = document.querySelector('.second');
var third = document.querySelector('.third');
// 根元素添加点击事件
document.documentElement.addEventListener('click', function (ev) {
cl('捕获阶段:' + '触发元素:' + ev.target.className + ';', '事件绑定的元素:' + ev.currentTarget.nodeName);
}, false);
// body元素添加点击事件
document.body.addEventListener('click', function (ev) {
cl('捕获阶段:' + '触发元素:' + ev.target.className + ';', '事件绑定的元素:' + ev.currentTarget.nodeName);
}, false);
// addEventListener()第三个入参值为false,表示冒泡阶段触发事件. 目前冒泡是更流行的事件传递方式
first.addEventListener('click', function(ev){
// ev:事件对象(Event),可以通过它了解很多事件信息
// 事件类型(是点击,还是双击,等等)
cl(ev.type);
// 触发事件的元素
cl(ev.target);
// 绑定事件的元素
cl(ev.currentTarget);
cl('捕获阶段:' + '触发元素:' + ev.target.className + ';', '事件绑定的元素:' + ev.currentTarget.className);
}, false);
second.addEventListener('click', function(ev){
// ev:事件对象(Event),可以通过它了解很多事件信息
// 事件类型(是点击,还是双击,等等)
cl(ev.type);
// 触发事件的元素
cl(ev.target);
// 绑定事件的元素
cl(ev.currentTarget);
cl('捕获阶段:' + '触发元素:' + ev.target.className + ';', '事件绑定的元素:' + ev.currentTarget.className);
}, false);
third.addEventListener('click', function(ev){
// ev:事件对象(Event),可以通过它了解很多事件信息
// 事件类型(是点击,还是双击,等等)
cl(ev.type);
// 触发事件的元素
cl(ev.target);
// 绑定事件的元素
cl(ev.currentTarget);
cl('捕获阶段:' + '触发元素:' + ev.target.className + ';', '事件绑定的元素:' + ev.currentTarget.className);
}, false);
</script>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>冒泡实现事件委托案例</title>
<style>
.item {
height: 32px;
margin: 10px;
background-color: #f1f1f1;
}
</style>
</head>
<body>
<span>当某个元素中的多个子元素都拥有某个相同的事件时, 如果要为每个子元素添加事件, 会很麻烦, 使用事件委托, 即, 把事件绑定到父元素上, 通过冒泡的方式, 把事件传递到父元素上的同名事件上触发</span>
<ul>
<li class="item">item1</li>
<li class="item">item2</li>
<li class="item">item3</li>
<li class="item">item4</li>
<li class="item">item5</li>
</ul>
<script>
// 给console.log方法取别名
var cl = console.log.bind(console);
// 获取父元素
var ul = document.querySelector('ul')
// 事件绑定在父元素上
ul.addEventListener('click', function(e) {
// 在事件回调函数中, this===e.target, 即触发事件的元素.
cl(e.target.innerText + '被点击了, 但是它没有绑定点击处理函数, 所以它执行的是绑定在' + e.currentTarget.nodeName + '上的事件处理方法');
}, false);
</script>
</body>
</html>
NodeList和HTMLCollection的区别: 前者有forEach方法, 后者没有. 其他的, 如像数组一样访问元素的方式, item(index)
方法, length
属性, 元素键名从0开始, nameItem(name属性值)
方法 等, 二者都有.
在 JS 中, 节点类型共有 11 种, 跟 HTML 相关的有 6 种(其他 5 种和 XML 相关), 元素节点只是其中之一.
事件的添加方式有: 1. 为元素添加”on事件名”属性; 2. 在js脚本中, 以”元素.on事件名=匿名函数”的方式添加; 3. 使用监听器( addEventListener(事件名, 回调函数, 传递机制)
)的方式添加.
当需要为多个元素添加相同的事件处理脚本时, 可以把事件处理脚本绑定到这些元素共同的长辈元素上, 通过事件冒泡, 委托长辈元素执行事件处理脚本.