DOM基本概念
Document Object Model- 文档对象模型,通过DOM可以来任意来修改网页中各个内容
- 文档
- 文档指的是网页,一个网页就是一个文档
- 对象
- 对象指将网页中的每一个节点都转换为对象转换完对象以后,就可以以一种纯面向对象的形式来操作网页了
- 模型
- 模型用来表示节点和节点之间的关系,方便操作页面
- 节点(Node)
- 节点是构成网页的最基本的单元,网页中的每一个部分都可以称为是一个节点
- 虽然都是节点,但是节点的类型却是不同的
- 常用的节点
- 文档节点 (Document),代表整个网页
- 元素节点(Element),代表网页中的标签
- 属性节点(Attribute),代表标签中的属性
- 文本节点(Text),代表网页中的文本内容
DOM(Document Object Model)
- 文档对象模型
DOM 是 JavaScript 操作网页的接口,全称为“文档对象模型”(Document Object Model)。它的作用是将网页转为一个 JavaScript 对象,从而可以用脚本进行各种操作(比如增删内容)。
浏览器会根据 DOM 模型,将结构化文档(比如 HTML 和 XML)解析成一系列的节点,再由这些节点组成一个树状结构(DOM Tree)。所有的节点和最终的树状结构,都有规范的对外接口。
DOM 只是一个接口规范,可以用各种语言实现。所以严格地说,DOM 不是 JavaScript 语法的一部分,但是 DOM 操作是 JavaScript 最常见的任务,离开了 DOM,JavaScript 就无法控制网页。另一方面,JavaScript 也是最常用于 DOM 操作的语言。
当网页加载时,浏览器就会自动创建当前页面的文档对象模型(DOM)。在 DOM 中,文档的所有部分(例如元素、属性、文本等)都会被组织成一个逻辑树结构(类似于族谱),树中每一个分支的终点称为一个节点,每个节点都是一个对象。
DOM节点树
一个文档的所有节点,按照所在的层级,可以抽象成一种树状结构。这种树状结构就是 DOM 树。它有一个顶层节点,下一层都是顶层节点的子节点,然后子节点又有自己的子节点,就这样层层衍生出一个金字塔结构,又像一棵树。
浏览器原生提供document节点,代表整个文档。
document
// 整个文档树
文档的第一层有两个节点,第一个是文档类型节点(<!doctype html>),第二个是 HTML 网页的顶层容器标签<html>。后者构成了树结构的根节点(root node),其他 HTML 标签节点都是它的下级节点。
除了根节点,其他节点都有三种层级关系。
- 父节点关系(parentNode):直接的那个上级节点
- 子节点关系(childNodes):直接的下级节点
- 同级节点关系(sibling):拥有同一个父节点的节点
DOM 提供操作接口,用来获取这三种关系的节点。比如,子节点接口包括firstChild(第一个子节点)和lastChild(最后一个子节点)等属性,同级节点接口包括nextSibling(紧邻在后的那个同级节点)和previousSibling(紧邻在前的那个同级节点)属性。
节点操作
nodeType
nodeType属性返回一个整数值,表示节点的类型。
document.nodeType // 9
上面代码中,文档节点的类型值为9。
Node 对象定义了几个常量,对应这些类型值。
document.nodeType === Node.DOCUMENT_NODE // true
不同节点的nodeType属性值和对应的常量如下。
- 文档节点(document):9,对应常量
Node.DOCUMENT_NODE - 元素节点(element):1,对应常量
Node.ELEMENT_NODE - 属性节点(attr):2,对应常量
Node.ATTRIBUTE_NODE - 文本节点(text):3,对应常量
Node.TEXT_NODE - 文档片断节点(DocumentFragment):11,对应常量
Node.DOCUMENT_FRAGMENT_NODE - 文档类型节点(DocumentType):10,对应常量
Node.DOCUMENT_TYPE_NODE - 注释节点(Comment):8,对应常量
Node.COMMENT_NODE
确定节点类型时,使用nodeType属性是常用方法。
document
document节点对象代表整个文档,每张网页都有自己的document对象。window.document属性就指向这个对象。只要浏览器开始载入 HTML 文档,该对象就存在了,可以直接使用。
当浏览器加载一个 HTML 文档时,会创建一个 Document 对象,Document 对象是 DOM 树中所有节点的根节点。通过 Document 对象我们可以访问 HTML 文档中的所有元素。
提示:Document 对象是 Window 对象的一部分,所以您可以通过 window.document 来访问 Document 对象。
访问元素节点常用的方法
getElementById( )
document.getElementById()方法返回匹配指定id属性的元素节点。如果没有发现匹配的节点,则返回null。
var elem = document.getElementById('para1');
注意,该方法的参数是大小写敏感的。比如,如果某个节点的id属性是main,那么document.getElementById('Main')将返回null。
document.getElementById()方法与document.querySelector()方法都能获取元素节点,不同之处是document.querySelector()方法的参数使用 CSS 选择器语法,document.getElementById()方法的参数是元素的id属性。
document.getElementById('myElement')
document.querySelector('#myElement')
上面代码中,两个方法都能选中id为myElement的元素,但是document.getElementById()比document.querySelector()效率高得多。
另外,这个方法只能在document对象上使用,不能在其他元素节点上使用。
getElementById()方法返回带有指定ID的元素,括号里面直接写ID名。
不要出现相同id的元素
示例:修改button中按钮的文字
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input id="btn" type="button" value="按钮">
<script>
var btn = document.getElementById('btn'); // 获取按扭节点
btn.onclick = function () {
btn.value = "I'm button"; // 点击按钮,改变按钮中文字
};
</script>
</body>
</html>
延迟运行
window.onload = function() { };
平时js代码都写到html代码的后面,但使用了window.onload = function() {};事件,就可以将js代码写到html代码的前面了
window.onload 它表示为window对象添加onload事件监听
window.onload()方法用于在网页加载完毕后立刻执行的操作,即当 HTML 文档加载完毕后,立刻执行某个方法。
将js代码写到head标签中:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
var box1 = document.getElementById('boxOne');
var box2 = document.getElementById('boxTwo');
console.log(box1);
console.log(box2);
</script>
</head>
<body>
<div id="boxOne">盒子一</div>
<div id="boxTwo">盒子二</div>
</body>
</html>
改进:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
//为window对象添加onload事件监听。页面加载完毕后,再加载指定代码
window.onload = function () {
var box1 = document.getElementById('boxOne');
var box2 = document.getElementById('boxTwo');
console.log(box1);
console.log(box2);
};
</script>
</head>
<body>
<div id="boxOne">盒子一</div>
<div id="boxTwo">盒子二</div>
</body>
</html>
getElementsByTagName( )
- 返回的是数组
- getElementsByTagName()方法可返回带有指定标签名的对象的集合。
document.getElementsByTagName()方法搜索 HTML 标签名,返回符合条件的元素。它的返回值是一个类似数组对象(HTMLCollection实例),可以实时反映 HTML 文档的变化。如果没有任何匹配的元素,就返回一个空集。
HTML 标签名是大小写不敏感的,因此getElementsByTagName()方法的参数也是大小写不敏感的。另外,返回结果中,各个成员的顺序就是它们在文档中出现的顺序。
如果传入*,就可以返回文档中所有 HTML 元素。
var allElements = document.getElementsByTagName('*');
注意,元素节点本身也定义了getElementsByTagName方法,返回该元素的后代元素中符合条件的元素。也就是说,这个方法不仅可以在document对象上调用,也可以在任何元素节点上调用。
var firstPara = document.getElementsByTagName('p')[0];
var spans = firstPara.getElementsByTagName('span');
上面代码选中第一个p元素内部的所有span元素。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="box1">
<p>1</p>
<p>2</p>
<p>3</p>
</div>
<script>
// 选中第一个div元素内部的所有p元素
var box1 = document.getElementsByTagName('div')[0]; // 获取第一个div节点
console.log(box1);
var oBox1 = box1.getElementsByTagName('p'); // 获取第一个div节点后的所有p元素
console.log(oBox1);
</script>
</body>
</html>
getElementsByClassName( )
- 返回的是数组
- 从IE9开始兼容
document.getElementsByClassName()方法返回一个类似数组的对象(HTMLCollection实例),包括了所有class名字符合指定条件的元素,元素的变化实时反映在返回结果中。
var elements = document.getElementsByClassName(names);
由于class是保留字,所以 JavaScript 一律使用className表示 CSS 的class。
参数可以是多个class,它们之间使用空格分隔。
var elements = document.getElementsByClassName('foo bar');
上面代码返回同时具有foo和bar两个class的元素,foo和bar的顺序不重要。
注意,正常模式下,CSS 的class是大小写敏感的。(quirks mode下,大小写不敏感。)
与getElementsByTagName()方法一样,getElementsByClassName()方法不仅可以在document对象上调用,也可以在任何元素节点上调用。
// 非document对象上调用
var elements = rootElement.getElementsByClassName(names);
getElementsByName()
- 多用于input标签或select 里的name属性
- name属性是它俩独有的
- 返回的是数组
getElementsByName()方法可返回带有指定名称的对象的集合。
document.getElementsByName()方法用于选择拥有name属性的 HTML 元素(比如<form>、<radio>、<img>、<frame>、<embed>和<object>等),返回一个类似数组的的对象(NodeList实例),因为name属性相同的元素可能不止一个。
// 表单为 <form name="x"></form>
var forms = document.getElementsByName('x');
forms[0].tagName // "FORM"
querySelector()
document.querySelector方法接受一个 CSS 选择器作为参数,返回匹配该选择器的元素节点。如果有多个节点满足匹配条件,则返回第一个匹配的节点。如果没有发现匹配的节点,则返回null。
- 只能得到页面上的一个元素
- 若有多个复合条件,只能得到第一个元素
querySelectorAll()
document.querySelectorAll方法与querySelector用法类似,区别是返回一个NodeList对象,包含所有匹配给定选择器的节点。
elementList = document.querySelectorAll('.myclass');
这两个方法的参数,可以是逗号分隔的多个 CSS 选择器,返回匹配其中一个选择器的元素节点,这与 CSS 选择器的规则是一致的。
var matches = document.querySelectorAll('div.note, div.alert');
上面代码返回class属性是note或alert的div元素。
这两个方法都支持复杂的 CSS 选择器。
// 选中 data-foo-bar 属性等于 someval 的元素
document.querySelectorAll('[data-foo-bar="someval"]');
但是,它们不支持 CSS 伪元素的选择器(比如:first-line和:first-letter)和伪类的选择器(比如:link和:visited),即无法选中伪元素和伪类。
如果querySelectorAll方法的参数是字符串*,则会返回文档中的所有元素节点。另外,querySelectorAll的返回结果不是动态集合,不会实时反映元素节点的变化。
最后,这两个方法除了定义在document对象上,还定义在元素节点上,即在元素节点上也可以调用。
定义与用法
-
querySelectorAl()方法返回文档中匹配指定CSS选择器的所有元素,返回NodeList对象。NodeList对象表示节点的集合。可以通过索引访问,索引值从О开始。
-
提示:你可以使用NodelList对象的length属性来获取匹配选择器的元素属性,然后你可以遍历所有元素,从而获取你想要的信息。
访问元素节点的常用方法
动态获取
getElementsByClassName()方法和getElementsByTagName()方法可以动态获取元素,可以理解为:页面上增加或者删除元素时,获取的元素个数可以改变,而querySelectorAll()方法做不到此效果。口说无凭,快来看例子:
<body>
<ul id="list">
<li class="liEle">oldli</li>
<li class="liEle">oldli</li>
<li class="liEle">oldli</li>
<li class="liEle">oldli</li>
<li class="liEle">oldli</li>
</ul>
<script>
// 获取ul元素
var listEle = document.getElementById("list")
// 通过querySelectorAll方法获取元素
var lis = document.querySelectorAll("li")
// 没有利用循环生成li之前打印lis
console.log(lis)
// 通过循环,生成5个li标签,类名为liEle,内容为new li,生成之后,放在ul里面
for (var i = 0; i < 5; i++) {
var newli = document.createElement("li");
newli.className = "liEle"
newli.innerHTML = "new li"
listEle.appendChild(newli)
}
// 利用循环生成li之后打印lis
console.log(lis)
</script>
</body>
控制台(两次打印获取的都是5个元素)︰
如果改为getElementsByTagName()方法获取li元素,第一次打印是5个元素,第二次打印是10个元素,如下:
<body>
<ul id="list">
<li class="liEle">oldli</li>
<li class="liEle">oldli</li>
<li class="liEle">oldli</li>
<li class="liEle">oldli</li>
<li class="liEle">oldli</li>
</ul>
<script>
// 获取ul元素
var listEle = document.getElementById("list")
// 通过getElementsByTagName方法获取元素
var lis = document.getElementsByTagName("li")
// 没有利用循环生成li之前打印lis
console.log(lis)
// 通过循环,生成5个li标签,类名为liEle,内容为new li,生成之后,放在ul里面
for (var i = 0; i < 5; i++) {
var newli = document.createElement("li");
newli.className = "liEle"
newli.innerHTML = "new li"
listEle.appendChild(newli)
}
// 利用循环生成li之后打印lis
console.log(lis)
</script>
</body>
控制台(循环生成li之前是5个元素,循环生成li之后是10个元素)︰
getElementsByClassName()方法的打印结果和getElementsByTagName()方法的打印结果是一样的。
DOM节点获取(补充)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// 获取body标签
var body = document.body;
console.log(body);
// 获取html根标签
var html = document.documentElement;
console.log(html);
//获取所有标签
var all = document.all;
console.log(all);
console.log(all.length);
// 等价于上面的document.all
var all02 = document.getElementsByTagName('*');
console.log(all02);
console.log(all02.length);
</script>
</body>
</html>
节点的关系
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="box">
<p>第一个p</p>
<p id="para">第二个p</p>
<p>第三个p</p>
</div>
<script>
var box = document.getElementById('box');
var para = document.getElementById('para');
// 得到box后的所有子节点
console.log(box.childNodes); // p与p之间的空格也算一个文本节点
// 得到box后的所有子元素节点(IE9开始兼容:)
console.log(box.children);
console.log(box.children.para); //children是一个类数组对象,后面可以直接打点调用名为id的元素
// 得到box后第一个子节点
console.log(box.firstChild);
// 得到box后的第一个元素子节点(IE9开始兼容:)
console.log(box.firstElementChild);
// 得到box后的最后一个子节点
console.log(box.lastChild);
// 得到box后的最后一个元素子节点(IE9开始兼容:)
console.log(box.lastElementChild);
// 得到id为para的父节点
console.log(para.parentNode);
// 得到para的前一个兄弟节点
console.log(para.previousSibling);
// 得到para的前一个兄的元素节点(IE9开始兼容:)
console.log(para.previousElementSibling);
// 得到para的后一个兄弟节点
console.log(para.nextSibling);
// 得到para的后一个兄的元素节点(IE9开始兼容:)
console.log(para.nextElementSibling);
</script>
</body>
</html>
封装节点关系函数
获得所有子元素节点
- 返回一个元素的所有子元素节点(兼容至IE6),children的功能
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>获取一个元素的所有子元素节点(兼容至IE6)</title>
</head>
<body>
<div id="ele">
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
</div>
<script>
function getAllChildElement(node) {
var ret = [];
for (var i = 0; i < node.childNodes.length; i++) {
if (node.childNodes[i].nodeType == 1) {
ret.push(node.childNodes[i]);
}
}
return ret;
};
var ele = document.getElementById('ele');
console.log(getAllChildElement(ele)); //想得到box节点的所有子元素节点
</script>
</body>
</html>
获得前一个兄弟元素节点
- 返回一个元素的前一个兄弟节点元素(兼容至IE6),previousElementSibling的功能
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="ele">
<p>1</p>
<p id="para">2</p>
<p>3</p>
</div>
<script>
function getPreviousSiblingElement(node) {
var o = node; // 为不改变node原值,我们使用新变量
while (o.previousSibling != null) {
if (o.previousSibling.nodeType == 1) {
return o.previousSibling;
}
o = o.previousSibling; // 更新下一个新的节点
}
};
var para = document.getElementById('para');
console.log(getPreviousSiblingElement(para));
</script>
</body>
</html>
获得所有兄弟元素节点
- 返回一个元素的所有兄弟节点元素
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>获得一个元素的所有兄弟元素节点</title>
</head>
<body>
<div id="ele">
<p>1</p>
<p>2</p>
<p id="para">3</p>
<p>4</p>
<p>5</p>
</div>
<script>
function getAllSiblingElement(node) {
var o = node; // 为不改变原值,使用新的变量
var prevs = []; // 前面节点集合
var nexts = []; // 后面节点集合
while (o.previousSibling != null) {
if (o.previousSibling.nodeType == 1) {
prevs.unshift(o.previousSibling);
}
o = o.previousSibling; // 只要不是第一个元素节点,就要换下一个行的节点,进行下一次遍历
}
// 后面兄弟元素节点遍历
o = node;
while (o.nextSibling != null) {
if (o.nextSibling.nodeType == 1) {
nexts.push(o.nextSibling);
}
o = o.nextSibling;
}
return prevs.concat(node, nexts);
};
var para = document.getElementById('para');
console.log(getAllSiblingElement(para));
</script>
</body>
</html>
innerHTML
- innerHTML用于获取元素内部的HTML代码的
- 对于自结束标签,这个属性没有意义
如果需要读取元素节点属性,直接使用元素.属性名
例子:元素.id 元素.name 元素.value
注意:class属性不能采用这种方式,
读取class属性时需要使用元素.className
想读取什么属性就.什么 .type可以 .value可以 .name可以 但读类名只能.className
innerText
命名前加o
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="ele">
</div>
<script>
var ele = document.getElementById('ele');
ele.innerHTML = '<li>第一行</li> <li>第二行</li>'
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="ele">
</div>
<script>
var ele = document.getElementById('ele');
ele.innerText = '<li>第一行</li> <li>第二行</li>'
</script>
</body>
</html>
注意
它俩都是改变元素内的内容,并不是向其中添加
例子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<p>C</p>
<p>C++</p>
<p>Java</p>
<p>Go</p>
<script>
var allP = document.getElementsByTagName('p');
for (var i = 0; i <= allP.length; i++) {
allP[i].innerHTML = '一起来学' + allP[i].innerHTML;
}
</script>
</body>
</html>
习题1
习题2
改变元素节点的CSS样式
通常在工作中,class用来设置样式。id用来书写JavaScript
注意:
如果CSS的样式名中含有‘-’
这种名称在JS中是不合法的比如background-color需要将这种样式名修改为驼峰命名法,
去掉‘-’,然后将‘-’后的字母大写
我们通过style属性设置的样式都是内联样式, 而内联样式有较高的优先级,所以通过JS修改的样式往往会立即显示 但是如果在样式中写了!important,则此时样式会有最高的优先级,即使通过JS也不能覆盖该样式,此时将会导致JS修改样式失效 所以尽量不要为样式添加!important
不仅设置的是内联样式,读的也是内联样式, 通过style属性设置和读取的都是内联样式, 无法读取样式表中的样式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#ele {
font-size: 30px;
color: #bfa;
background-color: rgba(0, 0, 0, .4);
}
</style>
</head>
<body>
<div id="ele">
hello world!
</div>
<script>
var ele = document.getElementById('ele');
ele.style.fontSize = '18px';
ele.style.color = '#666';
ele.style.backgroundColor = 'rgba(0, 0, 0, .2)';
</script>
</body>
</html>
改变元素节点的HTML属性
如果需要读取元素节点属性,直接使用
元素.属性名
例子:元素.id 元素.name 元素.value
注意:class属性不能采用这种方式, 读取class属性时需要使用元素.className
想读取什么属性就.什么 .type可以 .value可以 .name可以 但读类名只能.className
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<img id="pic" src="./images/1.jpg" alt="">
<a id="link" href="https://www.baidu.com/">baidu</a>
<script>
// 更改img的src属性
var oPic = document.getElementById('pic');
oPic.src = './images/2.jpg';
// 更改a标签的href属性
var oLink = document.getElementById('link');
oLink.href = 'https://gitee.com/';
oLink.innerText = 'gitee';
</script>
</body>
</html>
setAttribute 与 getAttribute
注意:是标准的W3C规定的属性都是可以打点调用的,像 data-*属性 就不是w3c标准规定的
setAttribute方法
setAttribute()方法创建或改变某个新属性。如果指定属性已经存在,则只设置该值。
getAttribute方法
getAttribute()方法通过名称获取属性的值。
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="test"></div>
<script>
var oTest = document.getElementById('test');
// 添加自定义属性
oTest.setAttribute('fruit', 'apple banana');
// 读取属性的数据内容
console.log(oTest.getAttribute('fruit'));
</script>
</body>
</html>
data-*属性
在HTML5中添加了data-*的方式来自定义属性,所谓data-*实际上就是data-前缀加上自定义的属性名,使用这样的结构可以进行数据存放。使用data-*可以解决自定义属性混乱无管理的现状。
定义和用法
data-* 属性用于存储私有页面后应用的自定义数据。
data-* 属性可以在所有的 HTML 元素中嵌入数据。
自定义的数据可以让页面拥有更好的交互体验(不需要使用 Ajax 或去服务端查询数据)。
data-* 属性由以下两部分组成:
- 属性名不要包含大写字母,在 data- 后必须至少有一个字符。
- 该属性可以是任何字符串
注意 自定义属性前缀 "data-" 会被客户端忽略。
语法
<element data-*="somevalue">
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="ele"></div>
<script>
var ele = document.getElementById('ele');
ele.setAttribute('data-tele', '137789456'); // 也可以手动向标签中添加data-n属性
console.log(ele.getAttribute('data-tele'));
</script>
</body>
</html>
原生js中自定义属性
设置自定义属性的2种方式:
(1)第一种方式是可以直接在HTML标签上面书写:
<h2 data-weather="rain">天气有雨</h2>
上面data-weather就是一个自定义属性,值为rain。
注意:如果设置的自定义属性是多个单词的组合的话,需要用中横线(-)链接,比如:
<h2 data-birth-date="20220102">生日日期</h2>
dataset属性
(2)第二种方式是通过js的dataset属性来设置:
语法: 元素节点.dataset.命名 = '数据内容' ;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<h2>今天下雨啦</h2>
<script>
var h2 = document.querySelector('h2');
h2.dataset.weather = "rain";
</script>
</body>
</html>
这样也是设置了一个data-weater的自定义属性,值为 rain,HTML5中元素都会有一个dataset的属性,这是一个DOMStringMap类型的键值对集合。
注意:如果设置的是多个单词的组合的话,需要使用驼峰命名法来书写,如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<h2>今天下雨啦</h2>
<script>
var h2 = document.querySelector('h2');
h2.dataset.birthDate = "20221128";
</script>
</body>
</html>
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h2 id="ele"></h2>
<script>
var ele = document.getElementById('ele');
ele.dataset.fruitCollection = '苹果,橘子';
</script>
</body>
</html>
js读取自定义属性
读取的时候通过dataset属性,使用"."来获取自定义属性,需要去掉data-前缀,连字符需要转化为驼峰命名,如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<h2 data-weather="rain" data-birth-date="20201128">今天下雨啦</h2>
<script>
var h2 = document.querySelector('h2');
console.log(h2.dataset.weather); // rain
console.log(h2.dataset.birthDate); // 20201128
</script>
</body>
</html>
CSS读取自定义属性
CSS也可以通过自定义属性来书写样式,如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
h2[data-birth-date="20201128"] {
color: red;
}
</style>
</head>
<body>
<h2 data-birth-date="20201128">今天下雨啦</h2>
</body>
</html>
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
h2[data-birth-date='20220201'] {
color: blue;
}
</style>
</head>
<body>
<h2 id="ele" data-birth-date="20220201">生日日期</h2>
</body>
</html>
className属性的介绍和使用
前言:在之前的课程中,我们学到了如何使用style属性去控制元素的样式。例如:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="myBox">imooc</div>
<script>
var myBox = document.getElementById("myBox");
myBox.style.fontWeight = "bold";
myBox.style.fontSize = "1.2em";
myBox.style.background = "red";
</script>
</body>
</html>
从代码中的红框处可以看到,想要通过js添加多个样式,例如10条、20条的样式,就需要逐条添加,这无疑大大的增加了工作量,而且实现起来也比较繁琐。所以,这里给大家介绍一种相对简单的设置和获取元素class属性值的方法,即className属性。接下来,给大家介绍下className属性如何使用。
一、定义
className属性设置或返回元素的class属性值。
二、语法
1、className属性用于设置元素的class属性值
element.className="cName"
(1) element表示要设置class属性的目标元素
(2) cName表示的是class属性的值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="myDiv"></div>
<script>
var myDiv = document.getElementById("myDiv");
// 设置class属性值
myDiv.className = "fixBox";
</script>
</body>
</html>
2、className 属性用于获取元素的class 属性值: element.className
(1)直接获取html元素的class属性值:
使用示例: 代码中,myDiv表示要获取class属性值的目标元素。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="myDiv" class="fixBox"></div>
<script>
var myDiv= document.getElementById("myDiv");
// 获取class属性值
console.log(myDiv.className)
</script>
</body>
</html>
(2)先通过className设置元素的属性值,再获取该属性值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="myDiv"></div>
<script>
var myDiv = document.getElementById("myDiv");
// 设置class属性值
myDiv.className = "fixBox";
// 获取class属性值
console.log(myDiv.className)
</script>
</body>
</html>
(3)当元素有多个类名,通过className属性可以获取到全部的类名:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="myBox" class="oneBox twoBox threeBox"></div>
<script>
var myBox = document.getElementById("myBox");
console.log(myBox.className)
</script>
</body>
</html>
学习了className属性如何使用之后,我们来改造一下本节最开始的案例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
.oneBox {
font-weight: bold;
font-size: 1.2em;
background: red;
}
</style>
</head>
<body>
<div id="myBox">imooc</div>
<script>
var myBox = document.getElementById("myBox");
// myBox.style.fontWeight = "bold";
// myBox.style.fontSize = "1.2em";
// myBox.style.background = "red";
myBox.className = "oneBox"
</script>
</body>
</html>
上述代码不仅实现了行为与样式分离,还减少了代码量,所以在需求不确定的情况下,建议使用className这种方法来动态的修改元素的样式。
注意事项
(1)通过className设置元素样式会有一个特点,就是通过className属性设置元素的class属性值时,将替换(而不是追加)该元素所有原本存在的class属性,即无论原本存在几个class值,都会被替换,比如: 原本有一个类名的时候:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="myBox" class="oneBox"></div>
<script>
var myBox = document.getElementById("myBox");
// 设置class属性值
myBox.className = "fixBox";
</script>
</body>
</html>
当原本有多个类名的时候:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="myBox" class="oneBox twoBox threeBox"></div>
<script>
var myBox = document.getElementById("myBox");
myBox.className = 'fixBox'
</script>
</body>
</html>
在实际开发中往往很多时候我们需要追加class,那怎么办呢?如下:有一个简单的方法,适用于类名较少,并且掌握原本类名的情况:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<div id="myBox" class="oneBox"></div>
<script>
var myBox = document.getElementById("myBox");
// 把原本的类名也添加上,使用空格隔开
myBox.className = 'oneBox fixBox'
</script>
</body>
</html>
节点的创建、移除和克隆
节点创建有两步:
- 先使用
document.createElement创建一个节点 - 再使用
appendChild或insertBefore使创建的新节点上DOM树上面
document.createElement
document.createElement方法用来生成元素节点,并返回该节点。
var newDiv = document.createElement('div');
createElement方法的参数为元素的标签名,即元素节点的tagName属性,对于 HTML 网页大小写不敏感,即参数为div或DIV返回的是同一种节点。如果参数里面包含尖括号(即<和>)会报错。
document.createElement('<div>');
// DOMException: The tag name provided ('<div>') is not a valid name
注意,document.createElement的参数可以是自定义的标签名。
document.createElement('foo');
父节点.appendChild(需上树的节点)
append 追加
insert 插入
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="ele">
</div>
<script>
var p = document.createElement('p');
var ele = document.getElementById('ele');
ele.appendChild(p);
p.innerHTML = 'test';
</script>
</body>
</html>
父节点.insertBefore(需要上树的孤儿节点, 插入位置的前一个位置)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="ele">
<p>hello</p>
</div>
<script>
var p = document.createElement('p');
var ele = document.getElementById('ele');
ele.insertBefore(p, ele.lastChild); // 插入到最后一个子节点之前
</script>
</body>
</html>
练习题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
table td {
border: 1px solid #333;
width: 30px;
height: 30px;
}
</style>
</head>
<body>
<table></table>
<script>
var table = document.querySelector('table');
for (var i = 0; i < 20; i++) {
var tr = document.createElement('tr');
for (var j = 0; j < 12; j++) {
var td = document.createElement('td');
tr.appendChild(td);
}
table.appendChild(tr); // 若把它放到前面会引起回流重绘,会引起回流,从而重绘样式。在实际开发中一般会避免频繁操作DOM,优化性能。
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
table td {
border: 1px solid #333;
width: 30px;
height: 30px;
}
</style>
</head>
<body>
<table></table>
<script>
var table = document.querySelector('table');
for (var i = 1; i <= 9; i++) {
var tr = document.createElement('tr');
for (var j = 1; j <= i; j++) {
var td = document.createElement('td');
tr.appendChild(td);
td.innerHTML = j + 'x' + i + '=' + j * i;
}
table.appendChild(tr); // 若把它放到前面会引起回流重绘,会引起回流,从而重绘样式。在实际开发中一般会避免频繁操作DOM,优化性能。
}
</script>
</body>
</html>
移动节点
移动语法
- 移动的新父节点.appendChild(需要移动的节点);
- 移动的新父节点.insertBefore(需要移动的节点,移动的目标位置);
示例1:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="move">move</div>
<div id="ele">
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
</div>
<script>
var ele = document.getElementById('ele');
var move = document.getElementById('move');
ele.appendChild(move);
</script>
</body>
</html>
示例2:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="move">move</div>
<div id="ele">
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
</div>
<script>
var ele = document.getElementById('ele');
var move = document.getElementById('move');
var p = document.querySelectorAll('p');
ele.insertBefore(move, p[3]);
</script>
</body>
</html>
删除节点
父节点.removeChild
- 父节点.removeChild(需要删除的节点);
- 用途:删除父节点下的子节点
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="test">
<p>1</p>
<p>2</p>
<p>3</p>
</div>
<script>
var test = document.getElementById('test');
var p = document.querySelectorAll('p');
test.removeChild(p[0]);
</script>
</body>
</html>
克隆节点
- 克隆后的节点是孤儿节点,需要追加使用appendChild或insertBefore
语法
- var 新的节点变量 = 需要被克隆的节点.cloneNode();
- var 新的节点变量 = 需要被克隆的节点.cloneNode(true);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="test">
<ul>
<li>hello</li>
<li>world</li>
<li>welcome</li>
<li>study</li>
</ul>
</div>
<div id="cloneAfter">
</div>
<script>
var test = document.getElementById('test');
var clone = test.cloneNode(); // 未使用深克隆
var cloneAfter = document.getElementById('cloneAfter');
cloneAfter.appendChild(clone);
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="test">
<ul>
<li>hello</li>
<li>world</li>
<li>welcome</li>
<li>study</li>
</ul>
</div>
<div id="cloneAfter">
</div>
<script>
var test = document.getElementById('test');
var clone = test.cloneNode(true); // 使用深克隆
var cloneAfter = document.getElementById('cloneAfter');
cloneAfter.appendChild(clone);
</script>
</body>
</html>
node.replaceChild()
Node.replaceChild() 方法用指定的节点替换当前节点的一个子节点,并返回被替换掉的节点。
replaceChild()
-
可以使用指定的子节点替换已有的子节点
-
语法:父节点.replaceChild(新节点,旧节点);
参数
| 参数 | 类型 | 描述 |
|---|---|---|
| newnode | Node 对象 | 必须。你要插入的节点对象。 |
| oldnode | Node object | 必须。你要移除的节点对象。 |
未替换前:
替换后:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="ele">
<p>1</p>
<p>2</p>
<p>3</p>
</div>
<div id="test">
<p>4</p>
<p>5</p>
<p>6</p>
</div>
<script>
var ele = document.getElementById('ele');
var pOne = document.querySelectorAll('#ele p')[0];
var pSix = document.querySelectorAll('#test p')[2];
ele.replaceChild(pSix, pOne); //将6替换掉1
</script>
</body>
</html>
DOM事件
事件监听简介
JS 事件(event)是当用户与网页进行交互时发生的事情,例如单机某个链接或按钮、在文本框中输入文本、按下键盘上的某个按键、移动鼠标等等。当事件发生时,您可以使用 JavaScript 中的事件处理程序(也可称为事件监听器)来检测并执行某些特定的程序。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="box"></div>
<script>
var oBox = document.getElementById('box');
oBox.onclick = function () {
//点击盒子时,将执行这里的语句
};
</script>
</body>
</html>
常见的鼠标事件监听
onmouseenter和onmouseover都表示“鼠标进入”,它们有什么区别呢?
答:onmouseenter不冒泡,onmouseover冒泡。
使用事件委托时要注意:不能委托不冒泡的事件给祖先元素
onmouseenter
- 没有冒泡过程(不冒泡),并不是从里层往上冒泡,它没有这个过程。
- 这个属性天生就是“不冒泡”的,相当于你事件处理函数附加给了哪个DOM节点就是哪个DOM节点自己触发的事件,没有冒泡过程
- 不要将它使用在事件委托的祖先元素上
onmouseover
- 冒泡
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#ele {
width: 100px;
height: 100px;
background-color: #bfa;
}
</style>
</head>
<body>
<div id="ele"></div>
<script>
var oEle = document.getElementById('ele');
oEle.onclick = function () {
console.log('我是onclick');
};
oEle.ondblclick = function () {
console.log('我是ondblclick');
};
oEle.onmousedown = function () {
console.log('我是onmousedown');
};
oEle.onmouseup = function () {
console.log('我是onmouseup');
};
oEle.onmouseenter = function () {
console.log('我是onmouseenter');
};
oEle.onmouseleave = function () {
console.log('我是onmouseleave');
};
oEle.onmousemove = function () {
console.log('我是onmousemove');
};
</script>
</body>
</html>
常见的键盘事件监听
键盘事件由用户击打键盘触发,主要有keydown、keypress、keyup三个事件,它们都继承了KeyboardEvent接口。
- onkeypress和onkeydown都是按下触发
- 按箭头键、F功能键、退格键时,只有onkeydown、onkeyup触发。onkeypress是不能监听系统按键的
如果用户一直按键不松开,就会连续触发键盘事件,触发的顺序如下。
- keydown
- keypress
- keydown
- keypress
- ...(重复以上过程)
- keyup
常见的表单事件监听
还有一个
oninput 输入框中内容只要有改变就会触发
- onchange、onfocus、onblur、oninput是表单域(元素)监听的
- onsubmit、onreset是给form监听的
blur 变得模糊不清
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="" id="myForm">
<p>
姓名:
<input type="text" name="nameField">
</p>
<p>
年龄:
<input type="text" name="ageField">
</p>
<input type="submit">
<input type="reset">
</form>
<script>
var myForm = document.getElementById('myForm');
// form表单可以使用 父节点id.子节点name属性值的形式来获取节点
var nameField = myForm.nameField; // 获取第一个输入框节点
nameField.onchange = function () {
console.log('内容修改完毕');
};
nameField.onfocus = function () {
console.log('已聚焦');
};
nameField.onblur = function () {
console.log('失去焦点');
};
nameField.oninput = function () {
console.log('内容正在被修改');
};
// form监听事件
myForm.onsubmit = function () { // 因为提交表单会刷新页面,所以使用alert来测试
alert('数据已提交');
};
myForm.onreset = function () {
console.log('数据已重置');
};
</script>
</body>
</html>
小知识:按TAB键可以从输入框1的聚焦跳到输入框2
按SHITF+TAB又可以从输入框2聚焦跳回输入框1
常见的页面事件监听
| onunload | 改变当前页面时触发此事件 |
|---|
onload事件是在网页中的元素(图片、外部关联文件等)都完全加载到浏览器之后才触发执行。
习题
keyCode通常用于onkeydown和onkeyup事件中,表示触发事件时的键的字符代码,可以通过这个字符代码判断用户具体按下的是那个键,然后执行相应的操作。例如:根据keyCode判断用户按下的是否是左右方向键,然后在对应的方向移动元素。
事件传播
在 JavaScript 中,我们将事件发生的顺序称为“事件流”,当我们触发某个事件时,会发生一些列的连锁反应,例如有如下所示的一段代码:
<body>
<div id="wrap">
<p class="hint">
<a href="#">Click Me</a>
</p>
</div>
</body>
如果给每个标签都定义事件,当我们点击其中的<a>标签时,会发现绑定在<div>和<p>标签上的事件也被触发了,这到底是为什么呢?为了解答这一问题,微软和网景两公司提出了两种不同的概念,事件捕获与事件冒泡:
- 事件捕获:由微软公司提出,事件从文档根节点(Document 对象)流向目标节点,途中会经过目标节点的各个父级节点,并在这些节点上触发捕获事件,直至到达事件的目标节点;
- 事件冒泡:由网景公司提出,与事件捕获相反,事件会从目标节点流向文档根节点,途中会经过目标节点的各个父级节点,并在这些节点上触发捕获事件,直至到达文档的根节点。整个过程就像水中的气泡一样,从水底向上运动。
提示:上面提到的目标节点指的是触发事件的节点。
后来,W3C 为了统一标准,采用了一个折中的方式,即将事件捕获与事件冒泡合并,也就是现在的“先捕获后冒泡”,如下图所示:
事件捕获
在事件捕获阶段,事件会从 DOM 树的最外层开始,依次经过目标节点的各个父节点,并触发父节点上的事件,直至到达事件的目标节点。以上图中的代码为例,如果单击其中的<a>标签,则该事件将通过document -> div -> p -> a的顺序传递到<a>标签。
事件冒泡
事件冒泡正好与事件捕获相反,事件冒泡是从目标节点开始,沿父节点依次向上,并触发父节点上的事件,直至文档根节点,就像水底的气泡一样,会一直向上。
on... 的事件监听方式,只能监听冒泡阶段,是监听不到捕获阶段的,实际上事件传播是先从外到内,然后再从内到外
先捕获阶段(capturing phase)再冒泡阶段(bubbling phase)
addEventListener()方法
EventTarget.addEventListener()用于在当前节点或对象上(即部署了 EventTarget 接口的对象),定义一个特定事件的监听函数。一旦这个事件发生,就会执行监听函数。该方法没有返回值。
target.addEventListener(type, listener[, useCapture]);
该方法接受三个参数。
type:事件名称,大小写敏感。listener:监听函数。事件发生时,会调用该监听函数。useCapture:布尔值,如果设为true,表示监听函数将在捕获阶段(capture)触发。该参数可选,默认值为false(监听函数只在冒泡阶段被触发)。
下面是一个例子。
function hello() {
console.log('Hello world');
}
var button = document.getElementById('btn');
button.addEventListener('click', hello, false);
上面代码中,button节点的addEventListener()方法绑定click事件的监听函数hello(),该函数只在冒泡阶段触发。
参数
- 参数1:指定的事件名(不加on)
- 参数2:函数(触发时执行的函数)
- 参数3:true或false(true监听捕获阶段,false捕获冒泡阶段)
语法
element.addEventListener(event, function, useCapture)
参数值
| 参数 | 描述 |
|---|---|
| event | 必须。字符串,指定事件名。 注意: 不要使用 "on" 前缀。 例如,使用 "click" ,而不是使用 "onclick"。 提示: 所有 HTML DOM 事件,可以查看我们完整的 HTML DOM Event 对象参考手册。 |
| function | 必须。指定要事件触发时执行的函数。 当事件对象会作为第一个参数传入函数。 事件对象的类型取决于特定的事件。例如, "click" 事件属于 MouseEvent(鼠标事件) 对象。 |
| useCapture | 可选。布尔值,指定事件是否在捕获或冒泡阶段执行。 可能值:- true - 事件句柄在捕获阶段执行。 false - 默认,事件句柄在冒泡阶段执行 |
DOM1级是没有对事件监听进行任何修改的,所以,没有一级的事件监听
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=1, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="ele">假按钮</div>
<script>
var ele = document.getElementById('ele');
ele.addEventListener('click', function () {
console.log('我被点击了');
}, false);
</script>
</body>
</html>
执行顺序
- DOM0级,后面的会覆盖前面的
- DOM2级,会按顺序执行,不会覆盖
- 若DOM0级与DOM2级,同时存在,并且同时指向同一个事件对象,则DOM2级是不会覆盖DOM0级的
DOM0级执行顺序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button>按钮</button>
<script>
var btn = document.querySelector('button');
btn.onclick = function () {
alert('触发第一次');
};
btn.onclick = function () {
alert('触发第二次');
};
</script>
</body>
</html>
后面会覆盖前面的
DOM2级执行顺序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button>按钮</button>
<script>
var btn = document.querySelector('button');
btn.addEventListener('click', function () {
console.log('执行第一次');
}, false);
btn.addEventListener('click', function () {
console.log('执行第二次');
}, false);
</script>
</body>
</html>
会依次按顺序执行
实现计算器功能
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input id="num1" type="text">
<select name="" id="">
<option value="+">+</option>
<option value="-">-</option>
<option value="*">*</option>
<option value="/">/</option>
</select>
<input id="num2" type="text">
<input id="btn" type="button" value="=">
<input id="ret" type="text">
<script>
var btn = document.getElementById('btn');
btn.onclick = function () {
var num1 = Number(document.getElementById('num1').value);
var num2 = Number(document.getElementById('num2').value);
var ret = document.getElementById('ret');
var calc = document.querySelector('select');
if (calc.value == '+') {
ret.value = num1 + num2;
} else if (calc.value == '-') {
ret.value = num1 - num2;
} else if (calc.value == '*') {
ret.value = num1 * num2;
} else if (calc.value == '/') {
ret.value = num1 / num2;
}
};
</script>
</body>
</html>
事件对象(1)
事件对象
- 当事件的响应函数被触发时,浏览器每次都会将一个事件对象作为实参传递进响应函数, 在事件对象中封装了当前事件相关的一切信息,比如:鼠标的坐标、键盘哪个按键被按下、鼠标滚轮滚动的方向等等。。。。
- e是事件对象,给DOM元素绑定事件时,事件处理函数都会提供一个参数,这个参数就是事件对象,通常用单词event或字母e来表示
- e是事件自带的对象,事件触发后就能获取到e,不是传入的实参。
offsetX/offsetY
- 是内部盒子的坐标位置
它俩计算的是最内层的元素,鼠标指针到盒子边界的距离
示例:
将事件监听给外层元素
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.outer {
width: 200px;
height: 200px;
border: 1px solid red;
margin: 0 auto;
padding-top: 40px;
box-sizing: border-box;
}
.inner {
width: 100px;
height: 100px;
border: 1px solid cornflowerblue;
margin: 0 auto;
}
#info {
font-size: 30px;
}
</style>
</head>
<body>
<div class="outer">
<div class="inner"></div>
</div>
<div id="info">
</div>
<script>
var outer = document.getElementsByClassName('outer')[0];
var inner = document.getElementsByClassName('inner')[0];
// 将事件监听给外层元素
outer.onmousemove = function (e) {
document.getElementById('info').innerHTML = e.offsetX + ',' + e.offsetY;
};
</script>
</body>
</html>
将事件监听给内层元素
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.outer {
width: 200px;
height: 200px;
border: 1px solid red;
margin: 0 auto;
padding-top: 40px;
box-sizing: border-box;
}
.inner {
width: 100px;
height: 100px;
border: 1px solid cornflowerblue;
margin: 0 auto;
}
#info {
font-size: 30px;
}
</style>
</head>
<body>
<div class="outer">
<div class="inner"></div>
</div>
<div id="info">
</div>
<script>
var outer = document.getElementsByClassName('outer')[0];
var inner = document.getElementsByClassName('inner')[0];
// 将事件监听给内层元素
inner.onmousemove = function (e) {
document.getElementById('info').innerHTML = e.offsetX + ',' + e.offsetY;
};
</script>
</body>
</html>
clientX/clientY
client 客户,客户端
- 是鼠标在元素中相较于视口的坐标位置
- 元素的位置不同,鼠标相较于视口的坐标就会不同
注意
需要清除默认的css样式。
不然,它会将默认的margin、padding算到坐标中
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
}
.box {
width: 100px;
height: 100px;
background-color: #bfa;
margin-top: 100px;
margin-left: 200px;
}
.info {
font-size: 30px;
}
</style>
</head>
<body>
<div class="box">
</div>
<div class="info">
</div>
<script>
var box = document.getElementsByClassName('box')[0];
box.onmousemove = function (e) {
document.getElementsByClassName('info')[0].innerHTML = e.clientX + ',' + e.clientY;
};
</script>
</body>
</html>
元素不同的位置:会影响坐标的不同
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
}
.box {
width: 100px;
height: 100px;
background-color: #bfa;
/* margin-top: 100px; */
/* margin-left: 200px; */
}
.info {
font-size: 30px;
}
</style>
</head>
<body>
<div class="box">
</div>
<div class="info">
</div>
<script>
var box = document.getElementsByClassName('box')[0];
box.onmousemove = function (e) {
document.getElementsByClassName('info')[0].innerHTML = e.clientX + ',' + e.clientY;
};
</script>
</body>
</html>
pageX/pageY
- 是相较于整个网页的坐标位置(包括网页的总长度,总宽度)
- 即使是翻动滚动条,坐标数值也不会变,因为它是相较于整个页面的宽高,不是相较于视口
1、pageY是获取鼠标点击处,距离整个页面顶部的距离,包括body被卷过的长度;滚动时,元素在整个页面上的位置并不会改变,所以pageY不变:
pageX同理,是获取鼠标点击处距离页面左侧的距离。
2、获取鼠标点击点的位置,可以实现一些页面特效。比如我想点击页面的时候,在点击处“弹出一颗心”,此时需要知道点击处的位置坐标,就可以考虑用pageX等属性实现。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.outer {
width: 200px;
height: 400px;
border: 2px solid #bfa;
}
</style>
</head>
<body style="height: 1000px;">
<div class="outer">
</div>
<div class="info">
</div>
<script>
var outer = document.querySelector('.outer');
outer.onmousemove = function (e) {
document.querySelector('.info').innerHTML = e.pageX + ',' + e.pageY;
};
</script>
</body>
</html>
事件对象(2)
e.charCode
- 字符码就是ASCII码
charCode区分大小写,但不能获取系统按键- charCode通常用于onkeypress事件
字符码不包括系统按键,例如上下左右键、F键、BACKSPACE键等等
而键码包括所有的按键
e.keyCode
- keyCode不分大小写,但是可以获取所有键
- keyCode通常用于onkeydown 与 onkeyup
常用键码需记忆
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<p>
<input type="text" id="inputField01">
</p>
<p id="info01"></p>
<p>
<input type="text" id="inputField02">
</p>
<p id="info02"></p>
<script>
var input01 = document.getElementById('inputField01');
input01.onkeypress = function (e) {
document.getElementById('info01').innerText = '您输入的字符编码为:' + e.charCode;
};
var input02 = document.getElementById('inputField02');
input02.onkeyup = function (e) {
document.getElementById('info02').innerText = '您输入的键码为:' + e.keyCode;
};
</script>
</body>
</html>
案例:移动盒子
onkeydown需要绑定在能键入的元素以及document文档上。而box盒子不能键入,所以不能绑定。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#box {
width: 100px;
height: 100px;
background-color: #bfa;
position: absolute;
top: 200px;
left: 200px;
}
</style>
</head>
<body>
<div id="box"></div>
<script>
var box = document.getElementById('box');
var t = 200;
var l = 200;
document.onkeydown = function (e) { // onkeydown需要绑定在能键入的元素以及document文档上。而box盒子不能键入,所以不能绑定。
switch (e.keyCode) {
case 37:
l -= 10;
break;
case 38:
t -= 10;
break;
case 39:
l += 10;
break;
case 40:
t += 10;
break;
}
box.style.top = t + 'px';
box.style.left = l + 'px';
};
</script>
</body>
</html>
事件对象(3)
e.preventDefault( ) 方法
Event.preventDefault方法取消浏览器对当前事件的默认行为。比如点击链接后,浏览器默认会跳转到另一个页面,使用这个方法以后,就不会跳转了;再比如,按一下空格键,页面向下滚动一段距离,使用这个方法以后也不会滚动了。该方法生效的前提是,事件对象的cancelable属性为true,如果为false,调用该方法没有任何效果。
注意,该方法只是取消事件对当前元素的默认影响,不会阻止事件的传播。如果要阻止传播,可以使用stopPropagation()或stopImmediatePropagation()方法。
prevent 阻止、防止
default 默认
// HTML 代码为
// <input type="checkbox" id="my-checkbox" />
var cb = document.getElementById('my-checkbox');
cb.addEventListener(
'click',
function (e){ e.preventDefault(); },
false
);
上面代码中,浏览器的默认行为是单击会选中单选框,取消这个行为,就导致无法选中单选框。
案例1:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text">
<script>
var input = document.querySelector('input');
input.onkeypress = function (e) {
if (!(e.charCode >= 48 && e.charCode <= 57 || e.charCode >= 97 && e.charCode <= 122)) {
e.preventDefault(); //阻止默认事件
}
};
</script>
</body>
</html>
案例2:
onmousewheel
鼠标滚轮事件
wheel 轮、轮子
e.deltaY
- 可用于判别滚轮 滚动的方向
- 向下滚动返回正值
- 向上滚动返回负值
它是返回正值或者负值,只能返回150或-150
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body {
height: 1500px;
}
#box {
width: 200px;
height: 200px;
border: 1px solid #bfa;
font-size: 30px;
}
</style>
</head>
<body>
<div id="box">0</div>
<script>
var box = document.getElementById('box');
box.onmousewheel = function (e) {
// 阻止默认事件:就是说当用户在盒子里面滚动鼠标滚轮的时候,此时不会引发页面的滚动条的滚动
e.preventDefault(); // 若网页过长,可阻止在盒子中滚动的bug
if (e.deltaY > 0) {
box.innerHTML--;
} else if (e.deltaY < 0) {
box.innerText++;
}
};
</script>
</body>
</html>
e.stopPropagation() 方法
阻止事件的传播,捕获阶段、冒泡阶段都可以阻止
案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#ele {
width: 500px;
height: 300px;
background-color: rgba(0, 0, 0, .2);
margin: 0 auto;
display: none;
}
</style>
</head>
<body>
<button>点击弹出</button>
<div id="ele"></div>
<script>
var btn = document.querySelector('button');
var ele = document.querySelector('div');
btn.onclick = function (e) {
ele.style.display = 'block';
e.stopPropagation(); // 为防止点击按钮后,onclick事件会冒泡到document上,阻止传播
};
document.onclick = function () {
ele.style.display = 'none';
};
ele.onclick = function (e) {
e.stopPropagation(); // 为防止点击弹出层本身,弹出层会消失,阻止onclick事件传播到document身上
};
</script>
</body>
</html>
事件委托(1)
批量添加事件监听
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
li {
cursor: pointer;
}
</style>
</head>
<body>
<ul>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
</ul>
<script>
var allLi = document.querySelectorAll('li');
for (var i = 0; i < allLi.length; i++) {
allLi[i].onclick = function () {
this.style.color = 'red';
};
}
</script>
</body>
</html>
问题1:
新增元素动态绑定
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
li {
cursor: pointer;
}
</style>
</head>
<body>
<button>点击添加元素</button>
<ul>
</ul>
<script>
var btn = document.querySelector('button');
var ul = document.querySelector('ul');
btn.onclick = function () {
var li = document.createElement('li');
ul.appendChild(li);
li.innerHTML = '我是新添加的li';
// 为新创建的元素添加事件监听(而不能在外部获取新添加的li节点)
li.onclick = function () {
li.style.color = 'red';
};
};
</script>
</body>
</html>
问题2
事件委托(2)
e.target
e.currentTarget
currentTarget与this十分相似,它俩所指的都是相同的对象
解决上述所遇到到的问题:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
li {
cursor: pointer;
}
</style>
</head>
<body>
<ul>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
</ul>
<script>
var ul = document.querySelector('ul');
// e.target代表真正点击的元素,触发事件的源元素
ul.onclick = function (e) {
e.target.style.color = 'red';
};
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button>点击添加新元素</button>
<ul>
</ul>
<script>
var btn = document.querySelector('button');
var ul = document.querySelector('ul');
btn.onclick = function (e) {
var li = document.createElement('li');
li.innerHTML = '我是新li元素';
ul.appendChild(li);
};
ul.onclick = function (e) {
e.target.style.color = 'red';
};
</script>
</body>
</html>
使用场景与优点
事件委托注意事项
onmouseenter
- 没有冒泡过程(不冒泡),并不是从里层往上冒泡,它没有这个过程。
- 这个属性天生就是“不冒泡”的,相当于你事件处理函数附加给了哪个DOM节点就是哪个DOM节点自己触发的事件,没有冒泡过程
不要将它使用在事件委托的祖先元素上
onmouseover
- 冒泡
使用事件委托时,不能再有多余的内层元素了,否则只有最内层的元素会达到效果。
例如:
定时器和延时器
定时器
JavaScript 提供定时执行代码的功能,叫做定时器(timer),主要由setTimeout()和setInterval()这两个函数来完成。它们向任务队列添加定时任务。
setInterval()函数
interval 间隔、间歇
- 每隔多长时间,执行一次函数
- 第一个参数:函数
- 第二个参数:时间间隔(ms),不用书写单位
setInterval函数的用法与setTimeout完全一致,区别仅仅在于setInterval指定某个任务每隔一段时间就执行一次,也就是无限次的定时执行。
var timer = setInterval(function() {
console.log(2);
}, 1000)
上面代码中,每隔1000毫秒就输出一个2,会无限运行下去,直到关闭当前窗口。
与setTimeout一样,除了前两个参数,setInterval方法还可以接受更多的参数,它们会传入回调函数。
其他参数
a,b是定义函数时的形参,88 和 66 是传入函数的实参, 使用88和66 直接替换a和b作为参数,不符合语法规范。
注意:
第一个参数:不仅匿名函数可以当作参数,具名函数也可作为参数
若把具名函数作为参数,书写上不要加(),加了()就是一条语句了。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var num = 0;
// 一个具名函数
function fun() {
console.log(num);
num++;
};
setInterval(fun, 1000); //将具名函数当作参数传入
</script>
</body>
</html>
它会无限的执行下去,如果不清除定时器的话
clearInterval()
- 清除定时器
clearInterval和clearTimeout函数,就可以取消对应的定时器。
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="start">点击运行</button>
<button id="end">停止运行</button>
<script>
var start = document.getElementById('start');
var end = document.getElementById('end');
var timer; // 设置为全局变量,否则外部访问不到
start.onclick = function () {
clearInterval(timer); // 为防止多次点击而造成的定时器叠加(速度变快)
var num = 0;
timer = setInterval(function () {
console.log(num++);
}, 500);
};
end.onclick = function () {
clearInterval(timer);
};
</script>
</body>
</html>
即使疯狂点击也不会出现输出无限加速的bug,因为在设置定时器前,先清除了上一次运行的定时器
为防止多次点击开始按钮,而造成的定时器叠加(会导致增长速度加快),所以在设置定时器之前,先清除上一次运行的定时器
延时器
setTimeout
timeout 超时、暂停、延时
setTimeout函数用来指定某个函数或某段代码,在多少毫秒之后执行。它返回一个整数,表示定时器的编号,以后可以用来取消这个定时器。
var timerId = setTimeout(func|code, delay);
上面代码中,setTimeout函数接受两个参数,第一个参数func|code是将要推迟执行的函数名或者一段代码,第二个参数delay是推迟执行的毫秒数。
console.log(1);
setTimeout('console.log(2)',1000);
console.log(3);
// 1
// 3
// 2
上面代码会先输出1和3,然后等待1000毫秒再输出2。注意,console.log(2)必须以字符串的形式,作为setTimeout的参数。
如果推迟执行的是函数,就直接将函数名,作为setTimeout的参数。
function f() {
console.log(2);
}
setTimeout(f, 1000);
setTimeout的第二个参数如果省略,则默认为0。
setTimeout(f)
// 等同于
setTimeout(f, 0)
除了前两个参数,setTimeout还允许更多的参数。它们将依次传入推迟执行的函数(回调函数)。
setTimeout(function (a,b) {
console.log(a + b);
}, 1000, 1, 1);
上面代码中,setTimeout共有4个参数。最后那两个参数,将在1000毫秒之后回调函数执行时,作为回调函数的参数。
setTimeout()
- 设置一个延时器
- 当设置的时间到了之后,会执行函数一次
- 参数1:函数
- 参数2:时间(ms)
clearTimeout()
- 清除延时器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=1, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button>点击弹出</button>
<script>
var btn01 = document.querySelectorAll('button')[0];
var timer;
btn01.onclick = function () {
timer = setTimeout(function () {
alert('两秒后,我弹出了');
}, 2000);
};
</script>
</body>
</html>
初始异步语句
异步(asynchronous)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>asynchronous</title>
</head>
<body>
<script>
setTimeout(function () {
console.log('A');
}, 2000);
console.log('B');
</script>
</body>
</html>
简单动画demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#box {
width: 200px;
height: 200px;
background-color: #bfa;
position: absolute;
top: 240px;
}
</style>
</head>
<body>
<input type="button" value="开始动画">
<input type="button" value="暂停动画">
<div id="box"></div>
<script>
var btn01 = document.querySelectorAll('input')[0];
var btn02 = document.querySelectorAll('input')[1];
var box = document.getElementById('box');
var timer;
var left = 0;
btn01.onclick = function () {
clearInterval(timer);
timer = setInterval(function () {
box.style.left = left + 'px';
left++;
}, 1);
};
btn02.onclick = function () {
clearInterval(timer);
};
</script>
</body>
</html>