js入门(7)DOM

基本概念

  • DOM是js操作html的文档接口,使文档操作变得简便
  • DOM最大的优点是将文档表示为节点树

DOM节点树


绿色是元素节点
红色是属性节点
蓝色是文本节点

nodeType常用属性值

  • 可以显示节点具体的类型
  • 打点调用

节点访问和位置关系

访问元素节点

认识document对象

  • document对象是DOM中最重要的东西,几乎所有的DOM的功能都封装在document对象中
  • document对象也表示整个HTML文档,它是DOM节点树的根
  • document对象的nodetype属性值是9
  • 在js中是对象

访问元素节点的常用方法

getElementById()

  • 通过document.getElementById()得到id元素节点
  • 不用#,直接写id名即可
注意
  • 如果页面上有相同的id,只能得到第一个

延迟运行

  • 测试DOM节点时,js要写在html后面,否则js无法找到对应的html节点
  • 可以使用window.onload=function()事件,使页面加载完毕后再执行指定的代码

getElementsByTagName()

  • getElementsByTagName()方法通过标签名得到节点数组
注意
  • 数组方便遍历,从而可以批量操控元素节点
  • 即使页面上只有一个指定标签名的节点,也将得到长度为1的数组
  • 任何一个节点元素也可以调用getElementsByTagName()方法,从而得到内部的某种类的元素节点

getElementsByClassName()

  • 方法的功能是通过类名得到节点数
注意
  • 从ie9开始兼容
  • 可以调用再调用

querySelector()

  • querySelector()方法的功能是通过选择器得到元素
  • 等于标准的选择器
注意
  • 只能得到一个元素,如果有多个只能得到都一个
  • 从ie8开始兼容

querySelectorAll()

  • querySelectorAll()方法的功能是通过选择器得到元素数组
  • 即使页面上只有一个符合选择器的节点,也将得到长度为1的数组

注意
  • 兼容到ie8

节点关系


注意

  • 文本节点也属于节点
  • 空白文本节点也应该算作节点,但是ie8之前的浏览器有一定的兼容问题,他们不把空文本节点当作节点。

排除文本节点干扰

  • 从ie9开始支持只考虑元素节点的属性

    例子:

    没有特别标记应该是7段

书写常见的节点关系函数

ie6需要使用封装达到寻找所有元素子节点,类似childern

ie6需要使用封装达到返回前一个元素兄弟节点,类似previousElementSibling

ie6需要使用封装达到返回所有元素兄弟节点

    function getAllElementSibling(node) {
        // 前面的元素兄弟节点
        var prevs = [];
        // 后面的元素兄弟节点
        var nexts = [];
        
        var o = node;
        // 遍历node的前面的节点
        while(o.previousSibling != null) {
            if(o.previousSibling.nodeType == 1){
                prevs.unshift(o.previousSibling);
            }
            o = o.previousSibling;
        }

        o = node;

        // 遍历node的后面的节点
        while(o.nextSibling != null) {
            if(o.nextSibling.nodeType == 1){
                nexts.push(o.nextSibling);
            }
            o = o.nextSibling;
        }

        // 将两个数组进行合并,然后返回
        return prevs.concat(nexts);
    }

看不明白

节点操作

改变节点中的内容

  • innerHTML
    可以用html语法设置节点中的内容

    书写不能出现空格
  • innerText
    只能用纯文本的形式设置节点中的内容

改变元素节点的css样式

改变元素节点的HTML属性

  • 标准的W3C属性,直接打点更新即可
  • 不是标准的属性
    要使用setAttribute()和getAttribu()来设置读取



节点的创建,操作,克隆

节点的创建

  • document.createElement()方法用于创建一个指定tagname的HTML元素

注意

  • 创建出来的节点是孤儿节点
  • 因为它没有被挂载再DOM树上,所以我们无法看见它
  • 必须继续使用appendChild()【追加子节点】或insert Before()【在。。。之前插入】方法将孤儿节点插入到DoM树上

appendChild()

  • 任何已经在DOM树上的节点,都可以调用appendChild()方法,它可以将孤儿节点挂载到它的内部,成为它最后一个子节点

insertBefore()

  • 任何已经在DOM树上的节点,都可以调用insertBefore()方法,它可以将孤儿节点挂载到它的内部,成为它的‘标杆子节点’之前的节点



    这里使用的是父节点插入

    这里使用的是标杆节点插入

小练习 创建一个20行12列的表格

小练习 创建九九乘法表

节点的操作

移动节点

  • 如果将已经挂载到DOM树上的节点成为appendChild()或者insertBdfore()的参数,这个节点将会被移动

  • 这意味着,一个节点不能同时位于BDOM树的两个位置

删除节点

  • removeChild()方法从DOM中删除一个子节点
  • 节点不能自己删除自己,必须由父节点删除它
  • 这个时候p就会被删除

克隆节点

  • cloneNode()方法可以克隆节点,克隆的节点是“孤儿节点”

  • 克隆之后因为是孤儿节点,所以还需要进行上树
  • 参数是一个布尔值,表示是否采用深度克隆:如果为true,表示深度克隆,false,表示只克隆节点本身


  • 当加上true之后

dom事件

事件监听

  • DOM允许我们书写js代码以让html元素对事件做出反应

什么是“事件”:用户与网页的交互动作:

  • 当用户点击元素时
  • 当鼠标移动到元素上时
  • 当文本框内容被改变的时候
  • 当网页加载完毕时等等

什么是“监听”:

  • 监听就是让计算机随时能过够发现这个事件发生了,从而执行程序员预先编写的一些程序
  • 设置事件监听主要有 onxxx和addEventListener()两种

最简单的设置事件监听方法

  • 最简单的给元素设置事件监听的方法就是设置他们的onxxx属性

  • 当点击盒子,会出现一个弹框
常见的鼠标事件监听

  • 鼠标抬起会优先于单机
常见的键盘事件监听

表单事件监听

  • oninput表示在表单中输入
  • 得到焦点就是选中输入框
常见的页面事件监听

addEventListener()方法

事件传播

当盒子嵌套时,事件监听的执行顺序




  • 实际,事件的传播时:先从外到内,再从内往外
  • 但实际上onxxx只能监听冒泡阶段
  • 如果使用addEventListener()方法


注意事项

  • 最内部元素不再区分捕获和冒泡阶段,会先执行写在前面的监听,然后执行后写的监听
  • 如果给元素设置相同的两个或多个同名事件,则DOM0级写法后面的会覆盖先写的,而DOM2级会按顺序执行

事件对象

  • 在本次事件处理函数提供一个形式参数,它时一个对象,封装了本次事件的细节
  • 这个参数通常用单词event或者字母e来表示

鼠标位置




  • 计算的是最内层的元素(如果盒子内再有盒子时,小盒子左上角也是0,0)

e.charCode和e.keyCode属性

  • e.charCode属性常用于onkeypress事件中,表示用户输入的字符的“字符码”
  • e.keyCode属性常用于onkeydown事件和onkeyup中,表示用户按下的按键的“健码”
  • 例子:


小案例

  • 制作一个特效:按方向键可以控制页面上的盒子移动
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>推盒子</title>
    <style>
        body{
            margin: 0;
            padding: 0;
        }

        #box{
            position: absolute;
            top: 200px;
            left: 200px;
            width: 100px;
            height: 100px;
            background-color: red;
        }
    </style>
</head>
<body>
    <div id="box"></div>

    <script>
        var oBox=document.getElementById('box');
        //设置全局变量tl,表示盒子top属性和left属性
        var t=200;
        var l=200;

        //监听document对象的键盘按下事件监听,表示用户在整个网页上按下按键的时候
        document.onkeydown=function (e) {
            switch (e.keyCode) {
                case 37:
                    l-=3;
                    break;
                case 38:
                    t-=3;
                    break;
                case 39:
                    l+=3;
                    break;
                case 40:
                    t+=3;
                    break;
            }
            //更改样式
            oBox.style.left=l+'px';
            oBox.style.top=t+'px';
        }
    </script>
</body>
</html>

方法

e.perventDefault()方法

  • e.perventDefault()方法用来阻止事件产生的“默认动作”
  • 一些特殊的业务需求,需要阻止事件的“默认动作”
  • 案例1:制作一个文本框,只能输入小写字母和数字,其他字符输入无效
  • 案例2:制作鼠标滚轮事件:当鼠标在盒子中向下滚动时,数字加1,否则减1
  • 鼠标滚轮事件时onmousewheel,它的事件对象而提供deltaY属性表示鼠标滚动方向,向下滚动时返回正值,向上滚动时,返回负值
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>鼠标滚轮案例</title>
    <style>
        body{
            height: 2000px;
            padding: 0;
            margin: 0;
        }
        #box{
            height: 100px;
            width: 200px;
            background-color: black;
        }
    </style>
</head>
<body>
<div id="box"></div>
<h1 id="info">0</h1>
<script>
    var Obox=document.getElementById('box');
    var Oinfo=document.getElementById('info');

    //设置info中显示的数字
    var a=0;

    //给box盒子添加鼠标滚轮事件监听
    Obox.onmousewheel=function (e) {
        //阻止默认事件,在盒子内滚动时,页面滚动条不会滚动
        e.preventDefault();
        if(e.deltaY>0){
            a++;
        }else {
            a--;
        }
        Oinfo.innerText=a;
    }
</script>
</body>
</html>

e.stopPropagation()方法

  • e.stopPropagation()方法用来阻止事件继续传播
  • 在一些场合,非常有必要切断事件继续传播,否则会造成页面特效显示出一些bug
  • 因为是onclick会采用冒泡,所以我是盒子也会显示出来
  • 小案例:制作一个弹出层:点击按钮显示弹出层,点击网页任意地方,弹出层关闭
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>弹出层</title>
</head>
<style>
#tcc{
    height: 100px;
    width: 200px;
    background-color: #444444;
    position: absolute;
    top: 50%;
    left: 50%;
    margin-top: -50px;
    margin-left: -100px;
    display: none;

}
</style>
<body>
<!--点击按钮,出现弹出层,点击除了弹出层的任意位置,弹出层关闭-->
<button id="btn">点我</button>
<div id="tcc">弹出层</div>
<script>
    var Obtn=document.getElementById('btn');
    var Otcc=document.getElementById('tcc');

    //点击按钮显示弹出框
    Obtn.onclick=function (e) {
        //因为是冒泡,所以要阻止事件传播
        e.stopPropagation();
        Otcc.style.display='block';
    }

    document.onclick=function () {
        Otcc.style.display='none';
    }
    Otcc.onclick=function (e) {
        //点击弹出框本身也要阻止事件传播
        e.stopPropagation();
    }
</script>
</body>
</html>

事件委托

  • 利用事件冒泡机制,将后代元素事件委托给祖先元素

e.target和e.currentTarget属性

  • 事件委托通常需要结合使用e.target属性
  • 例子:

    点击哪个,哪个变红
  • 例子:

事件委托使用场景

  • 有大量元素要批量添加事件监听时,使用事件委托可以减少内存消耗
  • 当有动态节点上树时,使用事件委托可以让新上树的元素具有事件监听

注意

  • 不冒泡相当于,选择他的父元素,也就是全部
  • target,会对最内层的元素生效

批量添加事件监听

  • 页面上有一个ul无序列表,里面有20个li,实现效果,点击哪个li,哪个就会变红
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>批量事件监听</title>
</head>
<body>
<ul id="list">
    <li>列表项</li>
    <li>列表项</li>
    <li>列表项</li>
    <li>列表项</li>
    <li>列表项</li>
    <li>列表项</li>
    <li>列表项</li>
    <li>列表项</li>
    <li>列表项</li>
    <li>列表项</li>
    <li>列表项</li>
    <li>列表项</li>
    <li>列表项</li>
    <li>列表项</li>
    <li>列表项</li>
    <li>列表项</li>
    <li>列表项</li>
    <li>列表项</li>
    <li>列表项</li>
    <li>列表项</li>
</ul>
<script>
    var Olist=document.getElementById('list');
    var Olb= Olist.getElementsByTagName('li');



    for (var i=0;i < Olb.length;i++){
        Olb[i].onclick = function () {
            this.style.color='red';
        };
    }
</script>
</body>
</html>

批量添加事件监听的性能问题

  • 每一个事件监听都会消耗一定的系统内存,而批量添加事件会导致监听数量太多,内存小号会非常大
  • 实际上每个li的事件处理函数都是不同的函数,这些函数本身也会占用内存

新增元素动态绑定事件

  • 案例:页面上有一个无序列表ul,它内部没有li元素,请制作一个按钮,点击这个按钮就能增加一个li元素。并且要求每增加的li元素也要有点击事件监听,实现效果点击哪个li元素,哪个就会变红
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<button id="btn">点我添加新的列表项</button>
<ul id="list"></ul>
<script>
    var Obtn=document.getElementById('btn');
    var Olist=document.getElementById('list');
/*    var lis=Olist.getElementsByTagName('li');*/

    Obtn.onclick=function () {
        var Oli=document.createElement('li');
        Oli.innerHTML='我是新来的';
        Olist.appendChild(Oli);
        Oli.onclick=function () {
            this.style.color='red';
        };
    }
</script>
</body>
</html>

动态绑定事件的问题

  • 新增元素必须分别添加事件监听,不能自动获得事件监听
  • 大量事件监听/大量事件处理函数都会产生大量消耗内存

定时器,延时器

定时器

  • setInterval()函数可以重复调用一个函数,在每次调用之前具有固定的时间间隔

函数的参数

  • setInterval()函数可以接收第3,4,。。。个参数,他们将按顺序传入函数

具名函数也可以传入setLnterval()函数

  • 没有括号表示传入的是函数,有的话表示函数的执行,会失去定时器的意义

清除定时器

  • clearInterval()函数可以清除一个定时器
  • 案例:点击开始定时器开始计数,暂停则停止

延时器

  • setTimeout()函数可以设置一个延时器,当指定时间到了,会执行函数一次,不会重复执行

清除延时器

  • clearTimeout()函数可以清除延时器

异步

  • 举例:

实现动画

定时器实现动画

js+css3



函数节流

  • 一个函数执行一次之后,只有大于设定的执行周期后才允许执行第二次
  • 函数节流只需要借助延时器就可以了
  • 完成一个方块点击按钮从左到右,再次点击可以返回,中途点击不会有效果

动画效果开发

无缝连续滚动特效

<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .box {
            width: 1000px;
            height: 130px;
            border: 1px solid #000;
            margin: 50px auto;
            overflow: hidden;
        }

        .box ul {
            list-style: none;
            /* 设置大一点,这样li才能浮动 */
            width: 5000px;
            position: relative;
        }

        .box ul li {
            float: left;
            margin-right: 10px;
        }
    </style>
</head>

<body>
    <div id="box" class="box">
        <ul id="list">
            <li><img src="images/number/0.png" alt=""></li>
            <li><img src="images/number/1.png" alt=""></li>
            <li><img src="images/number/2.png" alt=""></li>
            <li><img src="images/number/3.png" alt=""></li>
            <li><img src="images/number/4.png" alt=""></li>
            <li><img src="images/number/5.png" alt=""></li>
        </ul>
    </div>
    <script>
        var box = document.getElementById('box');
        var list = document.getElementById('list');

        // 复制多一遍所有的li
        list.innerHTML += list.innerHTML;

        // 全局变量,表示当前list的left值
        var left = 0;

        // 定时器,全局变量
        var timer;

        move();

        // 动画封装成函数
        function move() {
            // 设表先关,防止动画积累
            clearInterval(timer);

            timer = setInterval(function () {
                left -= 4;
                // 验收
                if (left <= - 1260) {
                    left = 0;
                }
                list.style.left = left + 'px';
            }, 20);
        }

        // 鼠标进入停止定时器
        box.onmouseenter = function () {
            clearInterval(timer);
        };

        // 鼠标离开继续定时器
        box.onmouseleave = function () {
            move();
        };
    </script>
</body>
</html>

轮播图特效

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .carousel {
            width: 650px;
            height: 360px;
            border: 1px solid #000;
            margin: 50px auto;
            position: relative;
            overflow: hidden;
        }
        .carousel ul {
            list-style: none;
            width: 6000px;
            position: relative;
            left: 0px;
            transition: left .5s ease 0s;
        }
        .carousel ul li {
            float: left;
        }
        .carousel .leftbtn {
            position: absolute;
            left: 20px;
            top: 50%;
            margin-top: -25px;
            width: 50px;
            height: 50px;
            background-color: rgb(28, 180, 226);
            border-radius: 50%;
        }
        .carousel .rightbtn {
            position: absolute;
            right: 20px;
            top: 50%;
            margin-top: -25px;
            width: 50px;
            height: 50px;
            background-color: rgb(28, 180, 226);
            border-radius: 50%;
        }
    </style>
</head>
<body>
    <div class="carousel">
        <ul id="list">
            <li><img src="images/beijing/0.jpg" alt=""></li>
            <li><img src="images/beijing/1.jpg" alt=""></li>
            <li><img src="images/beijing/2.jpg" alt=""></li>
            <li><img src="images/beijing/3.jpg" alt=""></li>
            <li><img src="images/beijing/4.jpg" alt=""></li>
        </ul>
        <a href="javascript:;" class="leftbtn" id="leftbtn"></a>
        <a href="javascript:;" class="rightbtn" id="rightbtn"></a>
    </div>
    <script>
        // 得到按钮和ul,ul整体进行运动
        var leftbtn = document.getElementById('leftbtn');
        var rightbtn = document.getElementById('rightbtn');
        var list = document.getElementById('list');

        // 克隆第一张图片
        var cloneli = list.firstElementChild.cloneNode(true);
        list.appendChild(cloneli);

        // 当前ul显示到第几张了,从0开始数
        var idx = 0;

        // 节流锁
        var lock = true;

        // 右边按钮监听
        rightbtn.onclick = function () {
            // 判断锁的状态
            if (!lock) return; 

            lock = false;

            // 给list加过渡,为什么要加??css中不是已经加了么??这是因为最后一张图片会把过渡去掉
            list.style.transition = 'left .5s ease 0s';
            idx ++;
            if (idx > 4) {
                // 设置一个延时器,延时器的功能就是将ul瞬间拉回0的位置,延时器的目的就是让过渡动画结束之后
                setTimeout(function() {
                    // 取消掉过渡,因为要的是瞬间移动,不是“咕噜”回去
                    list.style.transition = 'none';
                    list.style.left = 0;
                    idx = 0;
                }, 500);
            }
            list.style.left = -idx * 650 + 'px';

            // 函数节流
            setTimeout(function() {
                lock = true; 
            }, 500);
        }

        // 左边按钮监听
        leftbtn.onclick = function () {
            if (!lock) return;

            lock = false;

            // 判断是不是第0张,如果是,就要瞬间用假的替换真的
            if (idx == 0) {
                // 取消掉过渡,因为要的是瞬间移动,不是“咕噜”过去
                list.style.transition = 'none';
                // 直接瞬间移动到最后的假图片上
                list.style.left = -5 * 650 + 'px';
                // 设置一个延时器,这个延时器的延时时间可以是0毫秒,虽然是0毫秒,但是可以让我们过渡先是瞬间取消,然后再加上
                setTimeout(function() {
                    // 加过渡
                    list.style.transition = 'left .5s ease 0s';
                    // idx改为真正的最后一张
                    idx = 4;
                    list.style.left = -idx * 650 + 'px';
                }, 0);
            } else {
                idx --;
                list.style.left = -idx * 650 + 'px';
            }
            
            // 函数节流
            setTimeout(function() {
                lock = true; 
            }, 500);
        }
    </script>
</body>
</html>
  • 延时器可以让过渡失效

呼吸轮播图

  • 将所有的图片都设置在一个位置
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .carousel {
            width: 650px;
            height: 360px;
            border: 1px solid #000;
            margin: 50px auto;
            position: relative;

        }

        .carousel ul {
            list-style: none;
        }

        .carousel ul li {
            position: absolute;
            top: 0;
            left: 0;
            /* 透明度都是0 */
            opacity: 0;
            transition: opacity 1s ease 0s;
        }

        /* 只有第一张透明度是1 */
        .carousel ul li:first-child {
            opacity: 1;
        }

        .carousel .leftbtn {
            position: absolute;
            left: 20px;
            top: 50%;
            margin-top: -25px;
            width: 50px;
            height: 50px;
            background-color: rgb(28, 180, 226);
            border-radius: 50%;
        }

        .carousel .rightbtn {
            position: absolute;
            right: 20px;
            top: 50%;
            margin-top: -25px;
            width: 50px;
            height: 50px;
            background-color: rgb(28, 180, 226);
            border-radius: 50%;
        }
    </style>
</head>

<body>
    <div class="carousel">
        <ul id="list">
            <li><img src="images/beijing/0.jpg" alt=""></li>
            <li><img src="images/beijing/1.jpg" alt=""></li>
            <li><img src="images/beijing/2.jpg" alt=""></li>
            <li><img src="images/beijing/3.jpg" alt=""></li>
            <li><img src="images/beijing/4.jpg" alt=""></li>
        </ul>
        <a href="javascript:;" class="leftbtn" id="leftbtn"></a>
        <a href="javascript:;" class="rightbtn" id="rightbtn"></a>
    </div>
    <script>
        // 得到按钮和ul,ul整体进行运动
        var leftbtn = document.getElementById('leftbtn');
        var rightbtn = document.getElementById('rightbtn');
        var list = document.getElementById('list');
        var lis = list.getElementsByTagName('li');

        // 当前是第几张图显示
        var idx = 0;

        // 节流
        var lock = true;

        // 右按钮
        rightbtn.onclick = function () {
            // 判断节流
            if (!lock) return;

            lock = false;

            // 还没有改idx,此时的idx这个图片就是老图,老图淡出
            lis[idx].style.opacity = 0;
            idx++;
            if (idx > 4) idx = 0;
            // 改了idx,此时的idx这个图片就是新图,新图淡入
            lis[idx].style.opacity = 1;

            // 动画结束之后,开锁
            setTimeout(function () {
                lock = true;
            }, 1000);
        }

        // 左按钮
        leftbtn.onclick = function () {
            // 判断节流
            if (!lock) return;

            lock = false;

            // 还没有改idx,此时的idx这个图片就是老图,老图淡出
            lis[idx].style.opacity = 0;
            idx--;
            if (idx < 0) idx = 4;
            // 改了idx,此时的idx这个图片就是新图,新图淡入
            lis[idx].style.opacity = 1;

            // 动画结束之后,开锁
            setTimeout(function () {
                lock = true;
            }, 1000);
        }
    </script>
</body>

</html>
posted @ 2022-02-18 20:01  zongkm  阅读(252)  评论(0)    收藏  举报