七、JavaScript DOM

296 阅读30分钟

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 中,文档的所有部分(例如元素、属性、文本等)都会被组织成一个逻辑树结构(类似于族谱),树中每一个分支的终点称为一个节点,每个节点都是一个对象。

image.png

image.png

DOM节点树

一个文档的所有节点,按照所在的层级,可以抽象成一种树状结构。这种树状结构就是 DOM 树。它有一个顶层节点,下一层都是顶层节点的子节点,然后子节点又有自己的子节点,就这样层层衍生出一个金字塔结构,又像一棵树。

浏览器原生提供document节点,代表整个文档。

document
// 整个文档树

文档的第一层有两个节点,第一个是文档类型节点(<!doctype html>),第二个是 HTML 网页的顶层容器标签<html>。后者构成了树结构的根节点(root node),其他 HTML 标签节点都是它的下级节点。

除了根节点,其他节点都有三种层级关系。

  • 父节点关系(parentNode):直接的那个上级节点
  • 子节点关系(childNodes):直接的下级节点
  • 同级节点关系(sibling):拥有同一个父节点的节点

DOM 提供操作接口,用来获取这三种关系的节点。比如,子节点接口包括firstChild(第一个子节点)和lastChild(最后一个子节点)等属性,同级节点接口包括nextSibling(紧邻在后的那个同级节点)和previousSibling(紧邻在前的那个同级节点)属性。

image.png

image.png

节点操作

nodeType

nodeType属性返回一个整数值,表示节点的类型。

image.png

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属性是常用方法。

image.png

document

document节点对象代表整个文档,每张网页都有自己的document对象。window.document属性就指向这个对象。只要浏览器开始载入 HTML 文档,该对象就存在了,可以直接使用。

当浏览器加载一个 HTML 文档时,会创建一个 Document 对象,Document 对象是 DOM 树中所有节点的根节点。通过 Document 对象我们可以访问 HTML 文档中的所有元素。

提示:Document 对象是 Window 对象的一部分,所以您可以通过 window.document 来访问 Document 对象。

image.png

image.png

image.png

访问元素节点常用的方法

image.png

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')

上面代码中,两个方法都能选中idmyElement的元素,但是document.getElementById()document.querySelector()效率高得多。

另外,这个方法只能在document对象上使用,不能在其他元素节点上使用。

image.png getElementById()方法返回带有指定ID的元素,括号里面直接写ID名。

image.png 不要出现相同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>

动画.gif

延迟运行

window.onload = function() { };

image.png 平时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>

image.png 改进:

<!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>

image.png

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元素。

image.png

image.png

<!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>

image.png

getElementsByClassName( )

  • 返回的是数组
  • 从IE9开始兼容

document.getElementsByClassName()方法返回一个类似数组的对象(HTMLCollection实例),包括了所有class名字符合指定条件的元素,元素的变化实时反映在返回结果中。

var elements = document.getElementsByClassName(names);

由于class是保留字,所以 JavaScript 一律使用className表示 CSS 的class

参数可以是多个class,它们之间使用空格分隔。

var elements = document.getElementsByClassName('foo bar');

上面代码返回同时具有foobar两个class的元素,foobar的顺序不重要。

注意,正常模式下,CSS 的class是大小写敏感的。(quirks mode下,大小写不敏感。)

getElementsByTagName()方法一样,getElementsByClassName()方法不仅可以在document对象上调用,也可以在任何元素节点上调用。

// 非document对象上调用
var elements = rootElement.getElementsByClassName(names);

image.png

image.png

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

  • 只能得到页面上的一个元素
  • 若有多个复合条件,只能得到第一个元素

image.png

image.png

image.png

querySelectorAll()

document.querySelectorAll方法与querySelector用法类似,区别是返回一个NodeList对象,包含所有匹配给定选择器的节点。

elementList = document.querySelectorAll('.myclass');

这两个方法的参数,可以是逗号分隔的多个 CSS 选择器,返回匹配其中一个选择器的元素节点,这与 CSS 选择器的规则是一致的。

var matches = document.querySelectorAll('div.note, div.alert');

上面代码返回class属性是notealertdiv元素。

这两个方法都支持复杂的 CSS 选择器。

// 选中 data-foo-bar 属性等于 someval 的元素
document.querySelectorAll('[data-foo-bar="someval"]');

但是,它们不支持 CSS 伪元素的选择器(比如:first-line:first-letter)和伪类的选择器(比如:link:visited),即无法选中伪元素和伪类。

如果querySelectorAll方法的参数是字符串*,则会返回文档中的所有元素节点。另外,querySelectorAll的返回结果不是动态集合,不会实时反映元素节点的变化。

最后,这两个方法除了定义在document对象上,还定义在元素节点上,即在元素节点上也可以调用。

image.png 定义与用法

  • querySelectorAl()方法返回文档中匹配指定CSS选择器的所有元素,返回NodeList对象。NodeList对象表示节点的集合。可以通过索引访问,索引值从О开始。

  • 提示:你可以使用NodelList对象的length属性来获取匹配选择器的元素属性,然后你可以遍历所有元素,从而获取你想要的信息。

访问元素节点的常用方法

image.png

动态获取

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个元素)︰

60641f8709f9152c09310204.png 如果改为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个元素)︰

6064201f093d15b714470179.png 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>

image.png

节点的关系

image.png

image.png

image.png

image.png

<!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>

image.png

封装节点关系函数

image.png

获得所有子元素节点

  • 返回一个元素的所有子元素节点(兼容至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>

image.png

获得前一个兄弟元素节点

  • 返回一个元素的前一个兄弟节点元素(兼容至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>

image.png

image.png

获得所有兄弟元素节点

  • 返回一个元素的所有兄弟节点元素
<!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>

动画.gif

innerHTML

  • innerHTML用于获取元素内部的HTML代码的
  • 对于自结束标签,这个属性没有意义

如果需要读取元素节点属性,直接使用元素.属性名

例子:元素.id 元素.name 元素.value

注意:class属性不能采用这种方式, 读取class属性时需要使用元素.className

想读取什么属性就.什么 .type可以 .value可以 .name可以 但读类名只能.className

innerText

image.png

命名前加o

image.png

<!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>

image.png

<!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>

image.png 注意 它俩都是改变元素内的内容,并不是向其中添加

例子:

<!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>

image.png

习题1

image.png

习题2

image.png

改变元素节点的CSS样式

image.png 通常在工作中,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>

image.png

改变元素节点的HTML属性

image.png 如果需要读取元素节点属性,直接使用元素.属性名

例子:元素.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

image.png 注意:是标准的W3C规定的属性都是可以打点调用的,像 data-*属性 就不是w3c标准规定的

setAttribute方法

setAttribute()方法创建或改变某个新属性。如果指定属性已经存在,则只设置该值。

image.png getAttribute方法

getAttribute()方法通过名称获取属性的值。

image.png 示例:

<!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>

image.png

image.png

data-*属性

在HTML5中添加了data-*的方式来自定义属性,所谓data-*实际上就是data-前缀加上自定义的属性名,使用这样的结构可以进行数据存放。使用data-*可以解决自定义属性混乱无管理的现状。

定义和用法

data-* 属性用于存储私有页面后应用的自定义数据。

data-* 属性可以在所有的 HTML 元素中嵌入数据。

自定义的数据可以让页面拥有更好的交互体验(不需要使用 Ajax 或去服务端查询数据)。

data-* 属性由以下两部分组成:

  1. 属性名不要包含大写字母,在 data- 后必须至少有一个字符。
  2. 该属性可以是任何字符串

注意  自定义属性前缀 "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>

image.png

image.png

原生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>

image.png

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>

image.png

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>

5fbdcea80959827403710089.png

从代码中的红框处可以看到,想要通过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>

5fbdd0870914f6fb06580241.png 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>

5fbdd126092c1ba603650161.png (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>

5fbdd1810966d2db06630470.png (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>

5fbdd2b00965982203540137.png 学习了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>

5fbdd356091ad3c103620085.png 上述代码不仅实现了行为与样式分离,还减少了代码量,所以在需求不确定的情况下,建议使用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>

5fbe36500905de0b05140167.png 当原本有多个类名的时候:

<!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>

5fbe372209981f0c05640205.png 在实际开发中往往很多时候我们需要追加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创建一个节点
  • 再使用appendChildinsertBefore使创建的新节点上DOM树上面

document.createElement

document.createElement方法用来生成元素节点,并返回该节点。

var newDiv = document.createElement('div');

createElement方法的参数为元素的标签名,即元素节点的tagName属性,对于 HTML 网页大小写不敏感,即参数为divDIV返回的是同一种节点。如果参数里面包含尖括号(即<>)会报错。

document.createElement('<div>');
// DOMException: The tag name provided ('<div>') is not a valid name

注意,document.createElement的参数可以是自定义的标签名。

document.createElement('foo');

image.png

image.png

父节点.appendChild(需上树的节点)

image.png 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>

image.png

父节点.insertBefore(需要上树的孤儿节点, 插入位置的前一个位置)

image.png

<!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>

image.png

练习题

image.png

<!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>

image.png

<!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>

image.png

移动节点

移动语法

  • 移动的新父节点.appendChild(需要移动的节点);
  • 移动的新父节点.insertBefore(需要移动的节点,移动的目标位置);

image.png 示例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>

image.png

image.png

image.png 示例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>

image.png

删除节点

父节点.removeChild

  • 父节点.removeChild(需要删除的节点);
  • 用途:删除父节点下的子节点

image.png

<!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>

image.png

克隆节点

  • 克隆后的节点是孤儿节点,需要追加使用appendChild或insertBefore

语法

  • var 新的节点变量 = 需要被克隆的节点.cloneNode();
  • var 新的节点变量 = 需要被克隆的节点.cloneNode(true);

image.png

<!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>

image.png

<!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>

image.png

node.replaceChild()

Node.replaceChild() 方法用指定的节点替换当前节点的一个子节点,并返回被替换掉的节点。

replaceChild()

  • 可以使用指定的子节点替换已有的子节点

  • 语法:父节点.replaceChild(新节点,旧节点);

参数

参数类型描述
newnodeNode 对象必须。你要插入的节点对象。
oldnodeNode object必须。你要移除的节点对象。

未替换前:

image.png 替换后:

<!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>

image.png

DOM事件

事件监听简介

JS 事件(event)是当用户与网页进行交互时发生的事情,例如单机某个链接或按钮、在文本框中输入文本、按下键盘上的某个按键、移动鼠标等等。当事件发生时,您可以使用 JavaScript 中的事件处理程序(也可称为事件监听器)来检测并执行某些特定的程序。

image.png

image.png

image.png

<!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>

常见的鼠标事件监听

image.png 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>

常见的键盘事件监听

键盘事件由用户击打键盘触发,主要有keydownkeypresskeyup三个事件,它们都继承了KeyboardEvent接口。

image.png

  • onkeypress和onkeydown都是按下触发
  • 按箭头键、F功能键、退格键时,只有onkeydown、onkeyup触发。onkeypress是不能监听系统按键的

如果用户一直按键不松开,就会连续触发键盘事件,触发的顺序如下。

  1. keydown
  2. keypress
  3. keydown
  4. keypress
  5. ...(重复以上过程)
  6. keyup

常见的表单事件监听

image.png 还有一个 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

常见的页面事件监听

image.png

onunload改变当前页面时触发此事件

onload事件是在网页中的元素(图片、外部关联文件等)都完全加载到浏览器之后才触发执行。

习题

image.png keyCode通常用于onkeydown和onkeyup事件中,表示触发事件时的键的字符代码,可以通过这个字符代码判断用户具体按下的是那个键,然后执行相应的操作。例如:根据keyCode判断用户按下的是否是左右方向键,然后在对应的方向移动元素。

事件传播

image.png 在 JavaScript 中,我们将事件发生的顺序称为“事件流”,当我们触发某个事件时,会发生一些列的连锁反应,例如有如下所示的一段代码:

<body>
    <div id="wrap">
        <p class="hint">
            <a href="#">Click Me</a>
        </p>
    </div>
</body>

如果给每个标签都定义事件,当我们点击其中的<a>标签时,会发现绑定在<div><p>标签上的事件也被触发了,这到底是为什么呢?为了解答这一问题,微软和网景两公司提出了两种不同的概念,事件捕获与事件冒泡:

  • 事件捕获:由微软公司提出,事件从文档根节点(Document 对象)流向目标节点,途中会经过目标节点的各个父级节点,并在这些节点上触发捕获事件,直至到达事件的目标节点;
  • 事件冒泡:由网景公司提出,与事件捕获相反,事件会从目标节点流向文档根节点,途中会经过目标节点的各个父级节点,并在这些节点上触发捕获事件,直至到达文档的根节点。整个过程就像水中的气泡一样,从水底向上运动。

提示:上面提到的目标节点指的是触发事件的节点。

后来,W3C 为了统一标准,采用了一个折中的方式,即将事件捕获与事件冒泡合并,也就是现在的“先捕获后冒泡”,如下图所示:

09441W5b-0.gif 事件捕获

在事件捕获阶段,事件会从 DOM 树的最外层开始,依次经过目标节点的各个父节点,并触发父节点上的事件,直至到达事件的目标节点。以上图中的代码为例,如果单击其中的<a>标签,则该事件将通过document -> div -> p -> a的顺序传递到<a>标签。

事件冒泡

事件冒泡正好与事件捕获相反,事件冒泡是从目标节点开始,沿父节点依次向上,并触发父节点上的事件,直至文档根节点,就像水底的气泡一样,会一直向上。

image.png

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(eventfunctionuseCapture)

参数值

参数描述
event必须。字符串,指定事件名。 注意:  不要使用 "on" 前缀。 例如,使用 "click" ,而不是使用 "onclick"。 提示:  所有 HTML DOM 事件,可以查看我们完整的 HTML DOM Event 对象参考手册
function必须。指定要事件触发时执行的函数。 当事件对象会作为第一个参数传入函数。 事件对象的类型取决于特定的事件。例如, "click" 事件属于 MouseEvent(鼠标事件) 对象。
useCapture可选。布尔值,指定事件是否在捕获或冒泡阶段执行。 可能值:- true - 事件句柄在捕获阶段执行。 false - 默认,事件句柄在冒泡阶段执行

image.png 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>

image.png

执行顺序

image.png

  • 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>

后面会覆盖前面的 image.png 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>

image.png 会依次按顺序执行

实现计算器功能

<!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>

动画.gif

事件对象(1)

事件对象

  • 当事件的响应函数被触发时,浏览器每次都会将一个事件对象作为实参传递进响应函数, 在事件对象中封装了当前事件相关的一切信息,比如:鼠标的坐标、键盘哪个按键被按下、鼠标滚轮滚动的方向等等。。。。

image.png

  • e是事件对象,给DOM元素绑定事件时,事件处理函数都会提供一个参数,这个参数就是事件对象,通常用单词event或字母e来表示
  • e是事件自带的对象,事件触发后就能获取到e,不是传入的实参。

image.png

offsetX/offsetY

  • 是内部盒子的坐标位置

它俩计算的是最内层的元素,鼠标指针到盒子边界的距离

image.png 示例:

将事件监听给外层元素

<!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>

image.png 将事件监听给内层元素

<!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>

image.png

clientX/clientY

client 客户,客户端

  • 是鼠标在元素中相较于视口的坐标位置
  • 元素的位置不同,鼠标相较于视口的坐标就会不同

image.png 注意 需要清除默认的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>

动画.gif 元素不同的位置:会影响坐标的不同

<!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>

动画.gif

pageX/pageY

  • 是相较于整个网页的坐标位置(包括网页的总长度,总宽度)
  • 即使是翻动滚动条,坐标数值也不会变,因为它是相较于整个页面的宽高,不是相较于视口

image.png 1、pageY是获取鼠标点击处,距离整个页面顶部的距离,包括body被卷过的长度;滚动时,元素在整个页面上的位置并不会改变,所以pageY不变:

62c794162960164e06501000.png 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事件

image.png

image.png

image.png 字符码不包括系统按键,例如上下左右键、F键、BACKSPACE键等等

而键码包括所有的按键

e.keyCode

  • keyCode不分大小写,但是可以获取所有键
  • keyCode通常用于onkeydown 与 onkeyup

image.png 常用键码需记忆

示例:

<!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>

动画.gif

案例:移动盒子

image.png 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>

动画.gif

事件对象(3)

e.preventDefault( ) 方法

Event.preventDefault方法取消浏览器对当前事件的默认行为。比如点击链接后,浏览器默认会跳转到另一个页面,使用这个方法以后,就不会跳转了;再比如,按一下空格键,页面向下滚动一段距离,使用这个方法以后也不会滚动了。该方法生效的前提是,事件对象的cancelable属性为true,如果为false,调用该方法没有任何效果。

注意,该方法只是取消事件对当前元素的默认影响,不会阻止事件的传播。如果要阻止传播,可以使用stopPropagation()stopImmediatePropagation()方法。

prevent 阻止、防止

default 默认

image.png

// HTML 代码为
// <input type="checkbox" id="my-checkbox" />
var cb = document.getElementById('my-checkbox');

cb.addEventListener(
  'click',
  function (e){ e.preventDefault(); },
  false
);

上面代码中,浏览器的默认行为是单击会选中单选框,取消这个行为,就导致无法选中单选框。

案例1:

image.png

<!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>

动画.gif

案例2:

image.png

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>

动画.gif

e.stopPropagation() 方法

image.png 阻止事件的传播,捕获阶段、冒泡阶段都可以阻止

image.png

案例

image.png

<!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>

动画.gif

事件委托(1)

批量添加事件监听

image.png

<!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:

image.png

新增元素动态绑定

image.png

<!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>

动画.gif 问题2

image.png

事件委托(2)

image.png

e.target

e.currentTarget

image.png currentTargetthis十分相似,它俩所指的都是相同的对象

解决上述所遇到到的问题:

<!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>

动画.gif

<!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>

动画.gif

使用场景与优点

image.png

事件委托注意事项

image.png onmouseenter

  • 没有冒泡过程(不冒泡),并不是从里层往上冒泡,它没有这个过程。
  • 这个属性天生就是“不冒泡”的,相当于你事件处理函数附加给了哪个DOM节点就是哪个DOM节点自己触发的事件,没有冒泡过程
  • 不要将它使用在事件委托的祖先元素上

onmouseover

  • 冒泡

使用事件委托时,不能再有多余的内层元素了,否则只有最内层的元素会达到效果。

例如: image.png

定时器和延时器

定时器

JavaScript 提供定时执行代码的功能,叫做定时器(timer),主要由setTimeout()setInterval()这两个函数来完成。它们向任务队列添加定时任务。

setInterval()函数

interval 间隔、间歇

  • 每隔多长时间,执行一次函数
  • 第一个参数:函数
  • 第二个参数:时间间隔(ms),不用书写单位

image.png setInterval函数的用法与setTimeout完全一致,区别仅仅在于setInterval指定某个任务每隔一段时间就执行一次,也就是无限次的定时执行。

var timer = setInterval(function() {
  console.log(2);
}, 1000)

上面代码中,每隔1000毫秒就输出一个2,会无限运行下去,直到关闭当前窗口。

setTimeout一样,除了前两个参数,setInterval方法还可以接受更多的参数,它们会传入回调函数。

其他参数

image.png a,b是定义函数时的形参,88 和 66 是传入函数的实参, 使用88和66 直接替换a和b作为参数,不符合语法规范。

image.png

注意:

第一个参数:不仅匿名函数可以当作参数,具名函数也可作为参数

若把具名函数作为参数,书写上不要加(),加了()就是一条语句了。

image.png

<!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>

动画.gif 它会无限的执行下去,如果不清除定时器的话

clearInterval()

  • 清除定时器 clearIntervalclearTimeout函数,就可以取消对应的定时器。

image.png 示例:

<!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>

动画.gif 即使疯狂点击也不会出现输出无限加速的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)

image.png

clearTimeout()

  • 清除延时器

image.png

<!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)

image.png

image.png

<!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>

image.png

简单动画demo

image.png

image.png

<!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>

动画.gif