DOM

260 阅读18分钟

DOM document object model 文档对象模型

DOM的核心对象:是document对象

image.png

获取元素

常用获取元素的方法

getElementById

  • 通过id获取
  • 因为在一个页面中id是唯一的,所以获取到的就是一个元素
  • 【注】即使有两个或者两个以上相同的id,也只会获取到第一个遇到的id
<body>
    <div id="box"></div>
    <script>
        var box = document.getElementById('box')
        console.log(box)
    </script>
</body>

image.png

getElementsByClassName

  • 通过标签的class类名获取
  • 在页面中class名称不是唯一的,所以遇到相同的class会把获取到的元素放在一个伪数组里面
<body>
    <ul>
        <li class="newsitem">111</li>
        <li class="newsitem">111</li>
        <li class="newsitem">111</li>
        <li class="newsitem">111</li>
        <li class="newsitem">111</li>
    </ul>
    <script>
        var items = document.getElementsByClassName("newsitem")
        console.log(items)
    </script>
</body>

image.png

getElementsByName

  • 通过name属性获取
  • 返回一个伪数组
<body>
    <ul>
        <li class="newsitem">111</li>
        <li class="newsitem">222</li>
        <li class="newsitem" name="li">333</li>
        <li class="newsitem" name="li">444</li>
        <li class="newsitem">555</li>
    </ul>
    <script>
        var items = document.getElementsByName("li")
        for(var i=0;i<items.length;i++){
            items[i].style.backgroundColor = "skyblue"
        }
    </script>
</body>

image.png

getElementsByTagName

  • 通过标签获取
  • 返回一个伪数组
<body>
    <ul>
        <li>111</li>
        <li>222</li>
        <li>333</li>
        <li>444</li>
        <li>555</li>
        <p>我是p标签</p>
    </ul>

    <script>
        // 获取页面中所有的li标签
        var items = document.getElementsByTagName("li")
        // 给每一个li标签添加粉色的背景颜色
        for(var i=0;i<items.length;i++){
            items[i].style.backgroundColor = "pink"
        }
    </script>
</body>

image.png

querySelector

  • 只能获取到一个元素
  • ()中的元素,与css样式的选择器写法一致
<body>
    <div id="box1">我是id为box的盒子</div>
    <div class="box2">我是class为box2的盒子</div>
    <div class="box2">我是class为box2的盒子</div>
    <ul>
        <li>111</li>
        <li>222</li>
    </ul>
    <script>
        var box1 = document.querySelector("#box1")
        box1.style.backgroundColor = "skyblue"
        
        var box2 = document.querySelector(".box2")
        box2.style.backgroundColor = "pink"
        
        var item = document.querySelector("ul li")
        item.style.backgroundColor = "orange"
    </script>
</body>

【注】 这里获取不到第二个class名为box2div标签和第二个li标签,querySelector只能获取相应名字遇到的第一个元素

image.png

querySelectorAll

  • 可以获取到相应名字的所有元素
  • ()中的元素,与css样式的选择器写法一致
  • 返回一个伪数组
  • 通过下标的方式获取到每一个元素,可以使用数组的forEach方法遍历
<body>
    <div id="box">盒子1</div>
    <div class="cls">盒子2</div>
    <div class="cls">盒子3</div>
    <div name="myname">盒子4</div>
    <ul>
        <li>00001</li>
        <li>00002</li>
        <li>00003</li>
    </ul>
    <ol>
        <li>01</li>
        <li>02</li>
    </ol>
    <script>
        var oBox = document.querySelectorAll("#box")
        oBox[0].style.backgroundColor = "skyblue"

        var oCls = document.querySelectorAll(".cls")
        oCls.forEach(function(item){
            item.style.backgroundColor = "wheat"
        })

        var oList = document.querySelectorAll("ul li")
        console.log(oList)
        oList.forEach(function(item){
            item.style.backgroundColor = "pink"
        }) 
    </script>
</body>

image.png

获取元素的定位父级

offsetParent

  • offsetParent:找最近一个带有定位的父元素,
    • 如果父级们都没有定位,默认找body
    • 返回一个dom对象
// 封装一个函数,获取从自身到body的距离
// obj是一个形参,传入的是一个dom对象
function offset(obj) {
    var o = { left: obj.offsetLeft, top: obj.offsetTop }
    while (obj.offsetParent) {
        // 如果obj.offsetParent是null,就说明没有定位的父元素了,
        // nulL就是false, false中断循环
        obj = obj.offsetParent
        o.left += obj.offsetLeft
        o.top += obj.offsetTop
    }
    return o
}

获取元素尺寸

  • 就是获取元素的“占地面积”

offsetWidth 和 offsetHeight

  • offsetwidth :获取的是元素内容+ padding + border的宽度
  • offsetHeight :获取的是元素内容+ padding + border的高度

clientWidth 和 clientHeight

  • clientwidth :获取的是元素内容+ padding的宽度
  • clientHeight :获取的是元素内容+ padding的高度

【注】

  • 获取到的尺寸是没有单位的数字
  • 当元素在页面中不占位置的的时候,获取到的值是0
    • 例如:元素添加了display:none之后,获取的值就是0

获取元素偏移值

offsetLeft 和 offsetTop

  • offsetLeft :获取的是元素偏移左边的距离
  • offsetTop :获取的是元素偏移上边的距离
  • 【注】
    • 偏移值的参考点:定位父级
    • 定位父级:就是元素往外找遇到的第一个有定位的父级元素
    • 如果父级元素都没有定位,那么偏移的距离就是相当于body

clientLeft 和 clientTop

  • clientLeft :获取的是元素border-left-width
  • clientTop :获取的是元素border-top-width

获取可视窗口的尺寸

  • document.documentElement.clientWidth:获取页面可视窗口的宽度
  • document.documentElement.clientHeight:获取页面可视窗口的高度
  • 【注】获取的高度和宽度都是不包含滚动条的

获取浏览器不可见区域的高度

  • 简单理解:就是滚动条向上滚动了多少距离

  • 兼容写法

    		function getSroll(){
                // 不管是否带有DTD(声明头<!DOCTYPE html>)都可以使用
                // 如果不是ie9+,执行后面的判断
                if(window.pageXOffset!=undefined){
                    return {
                        left:window.pageXOffset,
                        top:window.pageYOffset
                    }
                }
                // 带有DTD
                // 用ocument.compatMode可以检测有没有带DTD
                //  带了返回CSS1Compat
                //  不带返回BackCompat
                if(document.compatMode==="CSS1Compat"){
                    return {
                        left:document.documentElement.scrollLeft,
                        top:document.documentElement.scrollTop
                    }
                }
                // 不带DTD
                return {
                    left:document.body.scrollLeft,
                    top:document.body.scrollTop
                }
            }
    

操作元素

操作元素文本内容

innerHTML

  • 可以获取完整的html片段,包括html中的标签
  • 可以解析html中的标签

innerText

  • 不能获取完整的html片段,只能获取文本信息,获取不到里面的标签
  • 在innerText中书写的标签会当做字符串

value

  • 适用于inputselect等自带value值的标签

操作元素类名

className

  • 增删改功能都是通过直接赋值的方式
<div id="box" class="item">cxm</div>
<script>
    console.log(box.className)  //获取id为box盒子的类的名称
    box.className = "item item1"  //添加
    console.log(box.className)
    box.className = "item1"  //删
    console.log(box.className)
</script>

image.png

classList

  • 会把类名放在一个数组中
  • 每次只能操作一个元素

.add("类名"):增加一个类名

  • 在增加之前会判断原有的box中是否已经存在相同的类名
    • 如果存在就不添加,不存在就添加
  • 语法:box.classList.add("类名")

.remove("类名"):删除一个类名

  • 语法:box.classList.remove("类名")

.toggle("类名"):切换

  • 简单理解:
    • 假设box中有这个类名,box.classList.toggle("类名")之后,就会移除掉这个类名
    • 反之,如果box中没有这个类名,box.classList.toggle("类名")之后,就会添加上这个类名

.contains("类名"):包含

  • 返回true和false
  • 包含返回true,不包含返回false

.replace():替换

  • box.classList.replace("原类名","新类名")

节点

节点分类

  • DOM的节点我们一般分为常用的三大类:元素节点、文本节点、属性节点 image.png

属性节点操作

  • attributes:查看所有属性节点
  • getAttribute(): 通过属性名获取对应的属性值
  • setAttribute(): 设置属性
  • removeAttribute(): 删除指定的属性
<body>
    <button>查看属性</button>
    <button>获取属性</button>
    <button>设置属性</button>
    <button>删除属性</button>
    <img src="./img/con2-2.jpg" alt="">
    <script>
        //获取元素
        var oBtns = document.querySelectorAll("button")
        var oImg = document.querySelector("img")
        
        // 查看属性 attributes
        oBtns[0].onclick = function () {
            console.log(oImg.attributes)
            // 数组方式
            // for(var i = 0;i<oImg.attributes.length;i++){
            //     console.log(oImg.attributes[i])
            // }

            // 对象方式
            for (var key in oImg.attributes) {
                // console.log(oImg.attributes[key])
                if (oImg.attributes[key].nodeType == 2) {
                    console.log(oImg.attributes[key])
                }
            }
        }

        // 获取属性 getAttribute("属性名称") 返回属性的值
        oBtns[1].onclick = function () {
            var res = oImg.getAttribute("src")
            console.log(res)
        }

        // 设置属性 setAttribute("属性名称","属性的值")
        oBtns[2].onclick = function () {
            oImg.setAttribute("src","./img/con-3.jpg")
        }

        // 删除属性 removeAttribute("属性名称")
        oBtns[3].onclick = function () {
            oImg.removeAttribute("src")
        }
    </script>
</body>
  • 点击查看属性按钮

image.png

  • 点击获取属性按钮

image.png

  • 点击设置属性按钮

image.png

  • 点击删除属性按钮,就把图片直接删除了

获取节点

  1. childNodes:所有的子一级节点
  2. children:所有的子一级元素节点
  3. firstChild:第一个节点
  4. firstElementChild:第一个元素节点
  5. lastChild:最后一个节点
  6. lastElementChild:最后一个元素节点
  7. previousSibling:上一个兄弟节点
  8. previousElementSibling:上一个兄弟元素节点
  9. nextSibling:下一个兄弟节点
  10. nextElementSibling:下一个兄弟元素节点
  11. parentNode:父节点
  12. parentElement:父元素节点
  13. attributes:所有属性节点

操作节点

  • 操作:增删改查

创建节点

  • createElement():创建元素节点
  • createTextNode():创建文本节点
  • 创建节点只是创建在了内存中,并不能再页面中显示
//创建一个div元素节点
var newEle = document.createElement('div');  

//创建一个文本节点
var textNode = document.createTextNode('文字文字文字...'); 

插入节点

  • appendChild():在父元素的最后面插入节点
  • insertBefore():在谁的前面插入节点
    • 语法:父元素.insertBefore(要插入的节点,在谁的前面插入)
    • 如果第二个参数写null,就会插入在父元素的最后,等价于appendChild()
  • 表单中的
    • insertRow() :创建行
    • insertCell():创建列(也就是行里面的每一个小格子)
    • deleteRow():删除行
//appendChild()
var oBox = document.getElementById('box'); 
var pNode = document.createElement('p');   //创建一个新元素节点<p> 
oBox.appendChild(pNode);   //把新元素节点<p>添加box节点的子节点末尾

//insertBefore()
//通过父节点调用, 在box之前插入一个新节点p;
//第一个参数为新节点,第二个参数为在这个元素前插入
oBox.parentNode.insertBefore(p, oBox); 

删除节点

  • 删除子元素:removeChild()
    • 语法:父元素.removeChild(要删除元素的id或class值)
  • 删除自己以及自己的子元素:remove()
    • 语法:元素.remove()
//通过父节点调用, 来删除指定子节点
oBox.parentNode.removeChild(oBox)

//通过父节点调用,删除整个父元素
oBox.parentNode.remove()

替换节点

  • 替换节点:replaceChild()
    • 语法:父元素.replaceChild(新的节点,被替换的节点)
//通过父节点调用, 新节点p替换了旧节点div
//第一个参数为新节点, 第二个参数为旧节点
box.parentNode.replaceChild(p, oBox);

克隆节点

  • 克隆节点:cloneNode()
  • 语法:要克隆的元素.cloneNode()
  • 浅克隆:
    • ()里面不写:意思为false,只克隆自身,不克隆后代(也就是不克隆里面的元素)
  • 深克隆:
    • ()里写true:克隆自身以及自身的所有后代,但是不会克隆事件
//获取第一个子节点, true表示复制标签和内容 , false表示只复制标签
var box = document.getElementById('box');
Var newNode = box.firstChild.cloneNode(true); 
box.appendChild(newNode);  //添加到子节点列表末尾

DOM事件

一个事件的组成:

1、谁触发的事件

2、 触发的什么事件:事件类型

3、触发以后做什么:事件处理函数

初识事件

什么是事件

  • js属于事件驱动编程,把‘驱动’,执行,调用通过一些交互,触发一些函数
  • 事件:
    • 发起 --> 执行
    • 绑定事件 ---> 触发事件
    • on 绑定 , emit 触发 ,off 解绑

事件的模式

  • JavaScript有两种事件实现模式: 内联模式, 脚本模式.
  • 【注】当一个元素上绑定了内联模式和脚本模式,只执行脚本模式

内联模式 :

  • 直接在HTML标签中添加事件. 这种模型是最传统简单的一种处理事件的方法。但是这种模式中事件和HTML是混写的, 并没有将JS与HTML分离, 当代码量多了以后,对后期代码维护和扩展很不利.
<input type="button" value="按钮" onclick="alert('hello');" /> 
//注意: 单双引号

//执行自定义的JS函数 
<input type="button"value="按钮" onclick="btnClick();" /> 

//注意: 内联模式调用的函数不能放到window.onload里面, 否则会找不到该函数.

脚本模式:

  • 脚本模式能将JS代码和HTML代码分离, 符合代码规范.
  • 使用脚本模式我们需要先获取到元素节点对象, 再针对该节点对象添加事件;
例如: 
var box = document.getElementById('box'); 
//添加事件方式一 :  通过匿名函数,可以直接触发对应的代码 (推荐)
box.onclick = function() {  //给box节点对象添加点击事件onclick
    console.log('Hello world!'); 
};

//添加事件方式二 :  通过指定的函数名赋值的方式 来执行函数
box.onclick = func;    //注意这里不能写成func()
function func() {        //给box节点对象添加点击事件onclick
    console.log('Hello world!'); 
};

事件类型

浏览器事件

  • load:页面加载事件
    • 特点
      1. dom结构加载完毕,且外部所有的资源加载完成,才触发
      2. 凡是带有src属性的标签都有一个load事件(img、input...)
  • unload:页面卸载事件
    • 触发:
      1. 关闭浏览器会触发一次
      2. 刷新页面也会触发一次
  • DOMContentLoaded:页面结构加载完毕就触发
  • scroll:浏览器滚动的时候触发
  • resize:窗口大小改变
  • wheel:页面上滚轮事件
    <img src="./images/1.png" alt="">
    <script>
        // 页面加载事件
        window.onload = function(){
            var endTime = new Date()
            console.log("全部资源加载完毕,执行onload")
            console.log("加载时间为"+((endTime-startTime)/1000))
        }

        // 卸载事件
        window.onunload = function(){
            console.log("资源卸载中....")
        }

        // DOMContentLoaded 页面加载完毕就触发
        window.addEventListener("DOMContentLoaded",function(){
            startTime=new Date()
            console.log("我不需等外部资源加载完,只页面机构加载完成,就触发!")
        })

        // 计算加载速度
        // onload - DOMContentLoaded 
        var startTime
        
        // 给浏览器的可视窗口绑定滚动条事件
        window.onscroll = function(){
            console.log("我是窗口的滚动条事件....")
            // document.documentElement.scrollTop  获取页面不可区域的高度
            // document.documentElement.scrollLeft  获取页面不可区域的宽度
            var top = document.documentElement.scrollTop
            console.log(top)
        }
        
        // 窗口大小改变
        window.onresize = function () {
            var w = document.documentElement.clientWidth
            if (w >= 0 && w < 400) {
                document.body.style.backgroundColor = "pink"
            } else if (w >= 400 && w <= 600) {
                document.body.style.backgroundColor = "skyblue"
            } else if (w >= 600 && w <= 800) {
                document.body.style.backgroundColor = "plum"
            } else if (w >= 800 && w <= 1000) {
                document.body.style.backgroundColor = "orange"
            } else{
                document.body.style.backgroundColor = "red"
            }
        }
    </script>

鼠标事件

  • click:单击事件
  • dblclick:双击事件
  • mousedown:鼠标左键按下事件
  • mouseup:鼠标左键抬起事件
    • 【注】单击一次,触发顺序:mousedown 先于 mouseup 先于 click
  • contextmenu:右键单击事件
  • mousemove:鼠标移动
  • 鼠标移入移出事件有两对,区别:
    • mouseover 和 mouseout :在父元素上设置,在父子元素上移入移出也会触发
      • 【注】触发顺序 image.png
    • mouseenter 和 mouseleave:在父元素上设置,在父子元素上移入移出不会触发
  • mousewheel:鼠标滚轮事件

键盘事件

  • 一般作用在:window、document、输入框、input
  • keydown:键盘按下事件
  • keyup:键盘抬起事件
  • keypress:非功能键(数字,字母,符号....可以触发,电脑的功能性按钮不能触发)

表单事件

  • focus:获取焦点事件
  • blur:失去焦点事件
  • change:表单内容改变事件
    • 【注】获取焦点时的内容 != 失去焦点时的内容不一致才会触发
  • input:表单内容输入事件
    • 每次内容不一样就会触发
  • submit:表单提交事件
    • 提交的时候,会先触发事件,再触发表单的默认提交行为
    • 阻止表单默认提交行为return false
  • reset:表单重置事件
    • 【注】:submit 和 reset 需要绑定在form表单上使用
  • onselect:输入框选中事件,并且失去焦点

事件的绑定与解绑

事件绑定

dom0

  • 语法:dom节点.on+事件类型 = function(){} ,例如:onclick

  • 将一个函数赋值给了一个事件处理属性onclick 这样的方法就是DOM0级,例如:

        <button id="btn" type="button"></button>
    	<script>
            var btn = document.getElementById('btn');
            btn.onclick = function() {
                console.log('Hello World');
            }
             btn.onclick = function() {
                console.log('11111');
            }
            //点击button这个按钮后,控制台会打印11111,后面这个处理函数覆盖了前面的处理函数
    	</script>
    
    • 【缺点】缺点在于一个处理程序无法同时绑定多个处理函数,后面绑定的处理函数会覆盖前面绑定的处理函数

dom2

  • 语法1:dom节点.addEventListener('事件类型',处理函数,true/false)

  • 语法2:dom节点.addEventListener('事件类型',处理函数,{once:true,capture:true})

  • 【注】处理函数只写函数名,不加小括号

    • addEventListener 第三个参数默认是冒泡,第三个参数改为ture或{capture:ture}表示捕获
        <button id="btn" type="button">点我</button>
    	<script>
            var btn = document.getElementById('btn');    
            function showFn() {
                alert('Hello World');
            }    
            btn.addEventListener('click', showFn);
    	</script>
    

事件解绑

dom0

  • 语法:dom节点.onclick = null
  • 直接利用dom0的缺点,后面写的处理函数覆盖前面的处理函数,来解绑事件
<button id="btn">抽奖一次</button>
<script>
    var obtn = document.getElementById("btn")
    obtn.onclick = function(){
        //点击抽奖之后,函数执行,控制台打印出“谢谢惠顾”,
        console.log("谢谢惠顾")
        //给obtn按钮的点击事件赋值为null,用来达到事件解绑的功能
        this.onclick = null
    }
   
</script>

dom2

  • 语法:dom节点.removeEventListener('事件类型',处理函数)
  • 【注】处理函数只写函数名,不加小括号
    • addEventListener和removeEventListener的第3个参数,统一为true或false,否则移除不生效
<button id="btn">抽奖一次</button>
<script>
    var obtn = document.getElementById("btn")
    function fn(){
        console.log("谢谢惠顾")
        this.removeEventListener('click',fn)
    }
    obtn.addEventListener("click",fn)
</script>

事件对象

事件对象:就是触发事件的时候的一条记录信息,记录点击的位置,事件的类型,目标元素...

事件对象的兼容写法:var e=evt || window. event

  • button 监听按下了那个键
  • type 事件类型
  • charCode 字符编码
  • keyCode 按键编码
  • target 和 srcElement 目标
    • target:鼠标点击的目标元素,不一定等于this
  • altKey
  • shiftKey
  • ctrlKey
  • metaKey
  • clientX,clientY : client客户端
    • 距离浏览器可视窗口的左上角的坐标值

image.png

  • pageX,pageY : page页面
    • 距离页面的左上角的坐标值 image.png
  • offsetX,offsetY : offset偏移
    • 距离触发元素的的左上角的坐标值

image.png

  • screenX,screenY : screen屏幕
<body>
    <div class="box">
        <span>111111</span>
    </div>
    <script>
        var oBox = document.querySelector(".box")
        oBox.onmousedown = function (evt) {
            var e = evt || window.event
            // e.button需要结合onmousedown来实现
            // console.log(e.button)  //鼠标左键0,滚轮键1,鼠标右键2
        }
        window.onkeydown = function (evt) {
            var e = evt || window.event
            // console.log(e.key) //返回按键对应的字符
            // console.log(e.keyCode)  //返回按钮对应的ASCII码,字母只有大写,没有小写
        }
        oBox.onclick = function (evt) {
            var e = evt || window.event
            // console.log(e.clientX,e.clientY) //鼠标单击的位置,距离浏览器可视窗口左上角的距离
            // console.log(e.pageX,e.pageY) //鼠标单击位置的位置,距离页面左上角的距离
            // console.log(e.offsetX,e.offsetY) //鼠标单击位置,距离元素左上角的距离
            // console.log(e.screenX,e.screenY)    //鼠标单击位置,距离屏幕左上角的距离
            // console.log(e.altKey)   //按下alt键再单击,返回true,否则返回false
            // console.log(e.shiftKey)   //按下shift键再单击,返回true,否则返回false
            // console.log(e.ctrlKey)   //按下ctrl键再单击,返回true,否则返回false
            // console.log(e.mateKey)   //按下win键(田)再单击,返回true,否则返回false
        }
    </script>
</body>

案例:鼠标拖拽

  • 案例描述:在页面可视窗口范围内,鼠标按下后,盒子跟着鼠标移动,鼠标抬起时,盒子不再移动
  • 思考点:临界值的判断
		<style>
			*{
				padding: 0px;
				margin: 0px;
			}
			#box{
				width: 100px;
				height: 100px;
				background: pink;
				position: absolute;
			}
		</style>

		<div id="box"></div>

		<script>
			var obox = document.getElementById("box")
			//为obox添加鼠标按下事件
			obox.addEventListener("mousedown",down)
			//为obox添加鼠标抬起事件
			obox.addEventListener("mouseup",up)
			//鼠标按下处理函数
			function down(){
				//鼠标按下后:为obox添加鼠标移动事件
				obox.addEventListener("mousemove",move)
			}
			//鼠标移动事件处理函数
			function move(event){
				//为x、y分别为当前距离可视窗口的宽度、高度并且减去自身元素宽高的一办
				var y = event.clientY - this.offsetWidth/2
				var x = event.clientX - this.offsetHeight/2
				
				//判断临界值,不让obox跑到可视窗口之外
				//判断距离上边
				if(y<0){
					y = 0
				}
				//判断左边
				if(x<0){
					x = 0
				}
				//判断右边
				//右边的临界值会等于 = 浏览器可视窗口的宽度 - 元素的宽度
				if(x>=document.documentElement.clientWidth-this.offsetWidth){
					x=document.documentElement.clientWidth - this.offsetWidth
				}
				//判断下边
				//下边的临界值会等于 = 浏览器可视窗口的高度 - 元素的高度
				if(y>=document.documentElement.clientHeight - this.offsetHeight){
					y = document.documentElement.clientHeight - this.clientY
				}
				obox.style.top = y + "px"
				obox.style.left = x + "px"
			}
			//鼠标抬起处理函数
			function up(){
				//鼠标抬起后:删除obox的鼠标移动事件
				obox.removeEventListener("mousemove",move)
			}
		</script>

DOM事件流

标准的dom事件流

  • 标准的dom事件流分为三个阶段
    1. 捕获:window => document => body => 父元素 => 子元素
    2. 目标:子元素
    3. 冒泡:子元素 => 父元素 => body => document => window
  • 标准的dom事件流默认情况只在冒泡阶段触发
  • 按照dom2事件绑定,并进行配置,才能看到捕获的回调函数被触发 例如:
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>标准的dom事件流</title>
		<style>
			*{
				padding:0;
				margin:0;
			}
			#outer{
				width: 300px;
				height: 300px;
				background: pink;
				overflow: hidden;
			}
			#center{
				width: 200px;
				height: 200px;
				background: paleturquoise;
				overflow: hidden;
				margin: 50px auto;
			}
			#inner{
				width: 100px;
				height: 100px;
				background: plum;
				margin: 50px auto;
			}
		</style>
	</head>
	<body>
		<div id="outer">
			<div id="center">
				<div id="inner"></div>
			</div>
		</div>
		<script>
			inner.onclick = function(){
				console.log("inner")
			}
			center.onclick = function(){
				console.log("center")
			}
			outer.onclick = function(){
				console.log("outer")
			}
			document.body.onclick = function(){
				console.log("docuement.body")
			}
			document.documentElement.onclick = function(){
				console.log("docuement.documentElement")
			}
			document.onclick = function(){
				console.log("docuemnt")
			}
			window.onclick = function(){
				console.log("window")
			}
		</script>
	</body>
</html>
  • 当点击inner时,按标准的dom事件流分析
    1. 捕获:window => document => body => outer => center => inner
    2. 目标:inner
    3. 冒泡:inner => center => outer => body => document => window
  • 所以点击inner时,控制台最终会输出

image.png

阻止事件冒泡

  • e.stopPropagation():重点
  • e.cancelBubble=true:ie写法
  • 兼容写法:
// 阻止冒泡兼容写法
if (e.cancelBubble) {
    e.cancelBubble = true
} else {
    e.stopPropagation()
}

阻止默认行为

  • 常见的默认行为:
    1. 表单,提交,重置行为
    2. a标签的跳转行为
    3. 图片拖拽的行为
    4. 右键菜单,点击鼠标右键显示菜单...
  • 阻止默认行为的方式(4种)
// 阻止默认行为的四种方式
// 1.重点
return false;

// 2.了解
e.returnValue = false

// 3. 重点
e.preventDefault()

// 4.兼容写法(了解)
if (e.preventDefault) {
    e.preventDefault()
} else {
    e.returnValue = false
}

案例:右键自定义菜单

<!DOCTYPE html>
<html lang="en">

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

        li {
            list-style: none;
            font-size: 12px;
            line-height: 20px;
            padding-left: 5px;
            margin-top: 2px;
        }

        li.active {
            background: #ccc;
        }

        .menu {
            width: 70px;
            display: none;
            box-shadow: 1px 1px 5px 1px #ccc;
            border-radius: 5px;
            overflow: hidden;
            padding: 5px 0px;
            position: absolute;
            top: 50px;
            left: 50px;
        }
    </style>
</head>

<body>
    <div class="menu">
        <li class="active">复制</li>
        <li>粘贴</li>
        <li>剪切</li>
        <li>删除</li>
        <li>查看</li>
    </div>
    <script>
        var oMenu = document.querySelector(".menu")
        document.oncontextmenu = function (e) {
            // 显示菜单
            oMenu.style.display = "inline-block"
            // 菜单位置
            // 【注】记得 + 单位
            oMenu.style.top = e.clientY + "px"
            oMenu.style.left = e.clientX + "px"
            // 阻止事件默认行为
            return false
        }
        // 事件委托
        oMenu.onmouseover = function (e) {
            if (e.target.nodeName != "LI") {
                return
            }
            // 获取菜单中所有的li标签
            var oLis = e.target.parentNode.children
            // 排他思想
            for (var i = 0; i < oLis.length; i++) {
                oLis[i].classList.remove("active")
            }
            e.target.classList.add("active")
        }
        // 事件委托
        oMenu.onclick = function (e) {
            if (e.target.nodeName != "LI") {
                return
            }
            // 弹出点击内容
            alert(e.target.innerText)
            // 隐藏菜单
            this.style.display = "none"
        }
    </script>
</body>

</html>

image.png

事件委托(重点)

  • 事件委托:委托给父级们,让它们执行事件,目标元素值发起

  • 利用e.target可以在父元素上控制子元素的事件处理

  • 优点:

    1. 减少事件绑定带来的开销问题
    2. 可以随意给动态添加或删除的子元素绑定事件
  • 缺点:代码繁琐,需要寻找目标元素target

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        li {
            line-height: 40px;
        }

        button {
            margin-left: 20px;
        }
    </style>
</head>

<body>

    <div id="box"></div>
    <script>
        var arr = ["111", "222", "333", "444", "555"]
        var oBox = document.querySelector("#box")
        for (var i = 0; i < arr.length; i++) {
            // 创建li标签
            var oli = document.createElement("li")
            // 把数组arr中的值依次赋值给li标签
            oli.innerHTML = arr[i]
            // 创建button按钮
            var obutton = document.createElement("button")
            // 给按钮添加文本
            obutton.innerHTML = "del"
            // 把按钮追加到里标签中
            oli.appendChild(obutton)
            // 把li标签追加到oBox中
            oBox.appendChild(oli)
        }
        //事件委托
        oBox.addEventListener("click", function (e) {
            console.log(e.target.nodeName)
            // 判断目标元素的节点名称是否等于BUTTON
            // nodeName获取到的是大写的标签
            // 如果不是BUTTON就return终止函数
            if (e.target.nodeName != 'BUTTON') {
                return
            }
            // 到这里就说明是BUTTON
            // 找到目标元素的父节点,删除
            e.target.parentElement.remove()
        })
    </script>
</body>

</html>

image.png