DOM节点的一些常见操作

DOM(document object model,文档对象模型)是JS操作HTML文档的接口,使文档操作变得非常优雅简便

特点:将文档表示为节点树

nodeType常用属性值

  • 1:元素节点
  • 3:文字节点
  • 8:注释节点
  • 9:document节点
  • 10:DTD节点

访问节点元素

依靠document对象

  • document.getElementById():通过Id得到元素
  • document.getElementsByTagName():通过标签名得到元素数组
  • document.getElementsByClassName():通过类名得到元素数组
  • document.querySelector():通过选择器名得到元素
  • document.querySelectorAll():通过选择器得到元素数组

getElementById

有相同id的元素,只能得到第一个

不管元素藏多深,都能通过id找到

延迟运行

在测试DOM代码时,通常JS代码一定要写道HTML节点后面,否则JS无法找到相应HTML节点

可以使用window.οnlοad=function(){}事件,使页面加载完毕后,在执行指定的代码

getElementsByTagName

数组方便遍历,从而可以批量操控元素节点

即使只有一个指定标签名的节点,也将得到长度为1的数组

任何一个节点元素也可以调用getElementsByTagName方法,从而得到其内部某种类的元素节点

getElementsByClassName

任何一个节点元素也可以调用getElementsByClassName方法,从而得到其内部某种类的元素节点

querySelector

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

<body>
<div id="box">
    <p>段落</p>
    <p class="spec">段落</p>
    <p>段落</p>
</div>
</body>
<script type="text/javascript">
    var the_p = document.querySelector('#box .spec');
    console.log(the_p);
</script>

document.querySelector("div[data-div]"); // 获取有 "data-div" 属性的第一个<div>元素

querySelectorAll

通过选择器得到元素数组

即使只有一个符合选择器的节点,也将得到长度为1的数组

节点的关系

父节点、子节点

  • 子节点:childNodes
  • 父节点:parentNode
  • 第一个子节点:firstChild
  • 最后一个子节点:lastChild
  • 前一个兄弟节点:previousSibling
  • 后一个兄弟节点:nextSibling

文本节点也属于节点

在标准W3C规范中,空白文本节点也应该算作节点,但因兼容问题

排除文本节点的干扰

  • 子节点:children
  • 父节点:parentNode
  • 第一个子节点:firstElementChild
  • 最后一个子节点:lastElementChild
  • 前一个兄弟节点:previousElementSibling
  • 后一个兄弟节点:nextElementSibling

封装节点关系函数

书写IE6也能兼容的寻找元素子节点、寻找前一个元素兄弟节点、获得某元素所有兄弟节点的函数

//寻找所有子节点
function getChildren(node){
    var children = [];
    for(var i=0;i<node.childNodes.length;i++){
        if(node.childNodes[i].nodeType==1){
            children.push(node.childNodes[i]);
        }
    }
    return children;
}
//寻找前一个兄弟节点
function getElementprevsibling(node){
    var a = node;
    while(a.previousSibling != null){
        if(a.previousSibling.nodeType==1){
            return a.previousSibling;
        }
        a = a.previousSibling;
    }
    return null;
}
//寻找所有兄弟节点
function getAllElementSibling(node){
    var prevs = [];
    var nexts = [];
    var a = node;
    while(a.previousSibling != null){
        if(a.previousSibling.nodeType==1){
            prevs.unshift(a.previousSibling);
        }
        a = a.previousSibling;
    }
    a = node;
    while(a.nextSibling != null){
        if(a.nextSibling.nodeType==1){
            nexts.push(a.nextSibling);
        }
        a = a.nextSibling;
    }
    return prevs.concat(nexts);
}

节点操作

如何改变元素节点中的内容

innerHTML:以HTML语法设置节点中的内容

innerText:以纯文本的形式设置节点中的内容

<body>
<div id="box"></div>
</body>
<script type="text/javascript">
    var oBox = document.getElementById('box');
    oBox.innerHTML = '慕课网';
    oBox.innerHTML = '<ul><li>1</li><li>2</li></ul>';
    oBox.innerText = '慕课网';
</script>

如何改变元素节点的CSS样式

    <style>
        .box{
            width: 200px;
            height: 200px;
            border: 1px solid black;
        }
    </style>
</head>
<body>
<div id="box" class="box"></div>
</body>
<script type="text/javascript">
    var oBox = document.getElementById('box');
    oBox.style.backgroundColor='red';
</script>

如何改变元素节点的HTML属性

只需要直接打点进行更改即可

不符合W3C标准的属性,要使用setAttribute()和getAttribute()来设置、读取

节点的创建

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

var op = document.createElement('p');

新创建的的节点是孤儿节点,没有挂载到DOM树上

必须使用appendChild()或inserBefore()将孤儿节点插入到DOM树上

appendChild():挂到最后,追加

父节点.appendChild(孤儿节点);

父节点.inserBefore(孤儿节点,标签节点);

动态创建20行12列的表格

<body>
<table id="mytable"></table>
</body>
<script type="text/javascript">
    var mytable = document.getElementById('mytable');
    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);
        }
        mytable.appendChild(tr);
    }
</script>

移动节点

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

新父节点.appendChild(已有父亲的节点);

新父节点.inserBefore(已有父亲的节点,标签节点);

将会离开原来的父亲,前往新的父亲

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

删除节点

removeChild()从DOM中删除一个子节点

父节点.removeChild(要删除的节点);

节点不能主动删除自己,必须由父节点删除它

克隆节点

cloneNode()克隆出的节点是孤儿节点

var 孤儿节点 = 老节点.cloneNode();

var 孤儿节点 = 老节点.cloneNode(true);

true为深度克隆,则该节点的所有后代节点也都会被克隆,false为克隆节点本身不克隆内容

事件监听

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

常见的鼠标事件监听

  • onclick:当鼠标单击某个对象
  • ondblclick:当鼠标双击某个对象
  • onmousedown:当某个鼠标按键在某个对象上被按下
  • onmouseup:当某个鼠标按键在某个对象上被松开
  • onmousemove:当某个鼠标按键在某个对象上被移动
  • onmouseenter:当鼠标进入某个对象(相似事件onmouseover)
  • onmouseleave:当鼠标离开某个对象(相似事件onmouseout)
  • onmousewheel:鼠标滚轮事件,它的事件对象e提供deltaY属性,向下滚动为正值,向上滚动为负值

常见的键盘监听事件

  • onkeypress:当某个键被按下(系统按钮如箭头键和功能键无法得到识别)
  • onkeydown:当某个键盘的键被按下(系统按钮可以识别,并且会先于onkeypress发生)
  • onkeyup:当某个键盘的键被松开

常见的表单事件监听

  • onchange:当用户改变域的内容
  • onfocus:当某元素获得焦点(比如tab键或鼠标监听)
  • onblur:当某元素失去焦点
  • onsubmit:当表单被提交
  • onreset:当表单被重置
  • oninput:正在输入

常见页面事件监听

onload:当页面或图像被完成加载

onunload:当用户退出页面

事件传播

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

先从外到内(捕获阶段),再从内到外(冒泡阶段)

onxxx只能监听冒泡阶段

addEventListener()方法:

DOM0级事件监听:只能监听冒泡阶段

DOM2级事件监听:

box.addEventListener('click', function (){
    
},true)//true监听捕获阶段,false监听冒泡阶段

注意事项

最内部元素不再区分捕获和冒泡阶段,先执行写在最前面的监听

如果给的元素设置相同的两个或多个同名文件,则DOM0级写法后面写的会覆盖先写的,而DOM2级会按顺序执行

事件对象

事件处理函数提供一个形式参数,它是一个对象,封装了本次事件的细节

这个参数通常用单词event或字母e来表示

box1.onclick = function (e){
    console.log('我是box1');
}

对象e就是这次事件的事件对象

鼠标位置属性

  • clientX:鼠标指针相对于浏览器的水平坐标
  • clientY:鼠标指针相对于浏览器的垂直坐标
  • pageX:鼠标指针相对于整张网页的水平坐标
  • pageY:鼠标指针相对于整张网页的垂直坐标
  • offsetX:鼠标指针相对于事件源元素的水平坐标
  • offsetY:鼠标指针相对于事件源元素的垂直坐标

e.charCode属性:通常用于onkeypress事件中,表示用户输入的字符的字符码

e.keyCode属性:用于onkeydown事件和onkeyup,表示用户按下的按键的键码

charCode

  • 0-9:48-57
  • A-Z:65-90
  • a-z:97-122

keyCode

  • 0-9:48-57
  • a-z:65-90(不区分大小写)
  • 上下左右:37-40
  • 回车:13
  • 空格:32

e.preventDefault()方法:阻止事件产生默认动作

<p>
    <input type="text" id="field">
</p>
</body>
<script type="text/javascript">
    var field = document.getElementById('field');
    field.onkeypress = function (e){
        if(!(e.charCode>=48&&e.charCode<=57||e.charCode>=97&&e.charCode<=122)){
            e.preventDefault();//阻止了默认事件,即输入字符
        }
    }
<body>
<div id="box"></div>
<h1 id="info">0</h1>
</body>
<script type="text/javascript">
    var box = document.getElementById('box');
    var info = document.getElementById('info');

    var a = 0;
    box.onmousewheel = function (e){
        //阻止默认事件,当用户在盒子里滚动鼠标时,不会引发页面的滚动
        e.preventDefault();
        if(e.deltaY>0){
            a--;
        }else{
            a++;
        }
        info.innerText = a;
    }
</script>

e.storPropagation()方法:阻止事件继续传播

制作一个弹出层:点击按钮显示弹出层,点击网页任意地方,弹出层关闭

<body>
<button id="btn">按我</button>
<div class="model" id="box"></div>
</body>
<script type="text/javascript">
    var btn = document.getElementById('btn');
    var box = document.getElementById('box');

    btn.onclick = function (e){
        //阻止事件继续传播到页面
        e.stopPropagation();
        box.style.display = 'block';
    }
    document.onclick = function (){
        box.style.display = 'none';
    }
    box.onclick = function (e){
        //点击弹出层内部不能关闭
        e.stopPropagation();
    }
</script>

事件委托

批量添加事件监听

<script type="text/javascript">
    var list = document.getElementById('list');
    var lis = list.getElementsByTagName('li');

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

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

每一个事件监听注册都会消耗一定的系统内存,而批量添加事件会导致监听数量太多,内存消耗会非常大

实际上每个li的事件处理函数都是不同的函数,这些函数也会占用内存

新增元素动态绑定事件

点击按钮增加li元素,且每个li元素有点击事件监听

<button id="btn">按我添加新列表项</button>
<ul id="list"></ul>
</body>
<script type="text/javascript">
    var btn = document.getElementById('btn');
    var list = document.getElementById('list');
    //按钮点击事件
    btn.onclick = function (){
        //创建孤儿节点
        var li = document.createElement('li');
        li.innerHTML = '我是列表项';
        //上树
        list.appendChild(li);
        //给新创建的li节点添加点击事件
        li.onclick = function (){
            this.style.color = 'red';
        }
    }

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

e.target属性:触发此事件的最早元素,即事件源元素

e.currentTarget属性:事件处理程序附加到的元素

<body>
<ul id="list">
    <li>111</li>
    <li>111</li>
    <li>111</li>
</ul>
</body>
<script type="text/javascript">
    var list = document.getElementById('list');

    list.onclick = function (e){
        e.target.style.color='red';//li变红
        e.cueerntTarget.style.color='red';//全变红,即点击函数附加在了ul所以整个ul变红
    }
</script>
<body>
<button id="btn">按我</button>
<ul id="list"></ul>
</body>
<script type="text/javascript">
    var btn = document.getElementById('btn');
    var list = document.getElementById('list');

    btn.onclick = function (){
        var li =document.createElement('li');
        li.innerText = '111';
        list.appendChild(li);
    }

    list.onclick = function (e){
        e.target.style.color='red';
    }
</script>

事件委托的使用场景

当有大量类似元素需要批量添加事件监听时,使用事件委托可以减少内存开销

当有动态元素节点上树时,使用事件委托可以让新上树的元素具有事件监听

注意事项

onmouseenteer和onmouseover的区别:onmouseenteer不冒泡,onmouseover冒泡

不能委托不冒泡的事件给祖先元素

最内层的元素不能再有额外的内层元素了

定时器和延时器

定时器

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

第一个参数是函数,第二个参数是间隔时间,以毫秒为单位,1000毫秒等于1秒

具名函数也能传入setInterval,具名函数在第一个参数不能加括号

清除定时器

clearInterva()清除一个定时器

var timer = setInterval(function (){},2000);
btn.onclick = function (){
    clearInterval(timer);
}
<body>
<h1 id="info">0</h1>
<button id="btn1">开始</button>
<button id="btn2">暂停</button>
</body>
<script type="text/javascript">
    var info = document.getElementById('info');
    var btn1 = document.getElementById('btn1');
    var btn2 = document.getElementById('btn2');
    var a = 0;
    var timer;//设置一个全局变量
    btn1.onclick = function (){
        //为了防止定时器叠加,在设置定时器之前先清除定时器
        clearInterval(timer);
        //更改全局变量timer的值为一个定时器实体
        timer = setInterval(function (){
            info.innerText = ++a;
        },1000);
    }
    //连续点击开始,会导致定时器效果叠加
    btn2.onclick = function (){
        clearInterval(timer);
    }
</script>

延时器

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

clearTimeout()函数取消延时器

初步认识异步语句

异步:不会阻塞CPU继续执行其他语句,当异步完成时,会执行回调函数(callback)

使用定时器实现动画

利用视觉暂留原理

JS和CSS3结合实现动画

<body>
<button id="btn">开始</button>
<div id="box"></div>
</body>
<script type="text/javascript">
    var btn = document.getElementById('btn');
    var box = document.getElementById('box');
    //设置标识量,指示当前盒子在左边还是右边
    var pos = 1;//1左边,2右边
    btn.onclick = function (){
        //把过渡加上
        box.style.transition = 'all 2s linear 0s'
        if(pos==1){
            box.style.left = '1100px';
            pos=2;
        }else{
            box.style.left = '100px';
            pos=1;
        }
        //瞬间移动,由于有过度所以是动画
    }
</script>

函数节流

一个函数执行一次后,只有大于设定的执行周期后才允许执行第二次

var lock = true;

function 需要节流的函数名(){
    //如果锁是关闭状态,则不执行
    if(!lock) return;
    //函数核心语句
    
    //关锁
    lock = false;
    
    //指定毫秒数后将锁打开
    setTimeout(function (){
        lock = true;
    },2000);
}

动画效果开发1——无缝连续滚动特效

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

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

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

    var timer;
    function move(){
        //设表先关,防止表累积
        clearInterval(timer);
        timer = setInterval(function (){
            left -= 4;

            if(left<=-1260){
                left = 0;
            }
            list.style.left = left + 'px';
        },20);
    }
    move();
    //鼠标进入停止计时器
    box.onmouseenter = function (){
        clearInterval(timer);
    }

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

注意将ul设置为相对定位

动画效果开发2——跑马灯轮播图特效

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .carousel{
            width: 650px;
            height: 360px;
            border: 1px solid black;
            margin: 50px auto;
            overflow: hidden;
            position: relative;  //儿子绝对定位,父亲相对定位
        }
        .carousel ul{
            list-style: none;
            width: 6000px;
            position: relative;
            left: 0px;  //如果不加left:0会导第一张切换第二张时没有过渡
            transition: left 0.5s ease 0s;
        }
        .carousel ul li{
            float: left;
        }
        .carousel .leftbtn{
            position: absolute;
            left: 20px;
            width: 50px;
            height: 50px;
            background-color: orange;
            border-radius: 50%;
            top: 50%;
            margin-top: -25px;
        }
        .carousel .rightbtn{
            position: absolute;
            right: 20px;
            width: 50px;
            height: 50px;
            background-color: orange;
            border-radius: 50%;
            top: 50%;
            margin-top: -25px;
        }
    </style>
</head>
<body>
<div class="carousel">
    <ul id="list">
        <Li><img src="beijing/0.jpg" alt=""></Li>
        <Li><img src="beijing/1.jpg" alt=""></Li>
        <Li><img src="beijing/2.jpg" alt=""></Li>
        <Li><img src="beijing/3.jpg" alt=""></Li>
        <Li><img src="beijing/4.jpg" alt=""></Li>
    </ul>
    <a href="javascript:;" class="leftbtn" id="leftbtn"></a>
    <a href="javascript:;" class="rightbtn" id="rightbtn"></a>
</div>
</body>
<script type="text/javascript">
    //得到按钮和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显示第几张
    var idx = 0;

    //节流锁,使用户无法快速切换图片
    var lock = true;

    rightbtn.onclick = function (){
        //判断锁的状态
        if(!lock) return;
        lock = false;
        //因为最后一张图会把过渡去掉,所以这里再加
        list.style.transition = 'left 0.5s ease 0s';
        idx ++;
        if(idx > 4){
            //设置一个延时器,将ul瞬间拉回0的位置,目的是让过渡动画结束之后回去
            setTimeout(function (){
                //取消过渡,因为要的是瞬间移动,不是过渡回去
                list.style.transition = 'none';
                list.style.left = 0;
                idx = 0;
            },500); //过渡速度是0.5秒,所以这里用500毫秒
        }
        list.style.left =-idx*650 + 'px';

        setTimeout(function (){
            lock = true;
        },500);//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 0.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>
</html>

动画效果开发3——呼吸轮播图特效

<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    *{
      margin: 0;
      padding: 0;
    }
    .carousel{
      width: 650px;
      height: 360px;
      border: 1px solid black;
      margin: 50px auto;
      position: relative;  //儿子绝对定位,父亲相对定位
    }
    .carousel ul{
      list-style: none;
      width: 6000px;
      position: relative;
    }
    .carousel ul li{
      position: absolute;
      top: 0;
      left: 0;
      opacity: 0;
      transition: opacity 1s ease 0s;
    }
    .carousel ul li:first-child{
      opacity: 1;
    }
    .carousel .leftbtn{
      position: absolute;
      left: 20px;
      width: 50px;
      height: 50px;
      background-color: orange;
      border-radius: 50%;
      top: 50%;
      margin-top: -25px;
    }
    .carousel .rightbtn{
      position: absolute;
      right: 20px;
      width: 50px;
      height: 50px;
      background-color: orange;
      border-radius: 50%;
      top: 50%;
      margin-top: -25px;
    }
  </style>
</head>
<body>
<div class="carousel">
  <ul id="list">
    <Li><img src="beijing/0.jpg" alt=""></Li>
    <Li><img src="beijing/1.jpg" alt=""></Li>
    <Li><img src="beijing/2.jpg" alt=""></Li>
    <Li><img src="beijing/3.jpg" alt=""></Li>
    <Li><img src="beijing/4.jpg" alt=""></Li>
  </ul>
  <a href="javascript:;" class="leftbtn" id="leftbtn"></a>
  <a href="javascript:;" class="rightbtn" id="rightbtn"></a>
</div>
</body>
<script type="text/javascript">
  //得到按钮和ul
  var leftbtn = document.getElementById('leftbtn');
  var rightbtn = document.getElementById('rightbtn');
  var list = document.getElementById('list');
  var lis = document.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,此时是新图,新图淡入
    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,此时是新图,新图淡入
    lis[idx].style.opacity = 1;

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

</script>
</html>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值