DOM操作与事件| 青训营笔记

87 阅读5分钟

DOM

课程目标

  • 理解DOM基础概念及浏览器的工作原理(*)
  • 熟练使用document对象的属性和API获取、修改和删除元素(及其内容和属性)(***)
  • 理解DOM级别,熟练掌握事件的添加和删除,及常用的事件类型(***)
  • 掌握事件对象的使用,理解事件流及DOM事件的传播阶段,掌握浏览器默认行为的阻止方式,能够利用事件委托提高程序可维护性(***)
  • 理解数据驱动的概念及其应用方式(**)
  • 掌握JS操作CSS样式的方式(**)

网页交互流程

DOM基础概念

DOM(document object model),即文档对象模型。HTML文档是由标签构成的,浏览器解析网页文档时,会将页面的元素整合为document对象,通过这个对象提供的一系列方法可以允许我们操作HTML元素。比如元素的增删改查,CSS样式的修改.

浏览器工作原理(重点)

  • 用户输入URL,按下回车

  • 域名解析(通过域名获取目标服务器的IP地址)

  • TCP连接(与目标服务器建立连接)

  • 浏览器向服务器发送HTTP请求(对应一个网站来说一般就是一个HTML文件)

  • 服务器向浏览器返回HTTP响应(HTML文件)

  • 浏览器解析HTML文档(重点)

    • 处理HTML标签,生成DOM树。浏览器会将HTML文档中的标签整合为document对象,这个对象作为网页内容的入口,也就是DOM树。文档中的每一个元素都是DOM树上的一个节点。

    • 处理CSS,构建CSSOM树。与DOM树类似,浏览器遍历CSS规则,创建CSSOM节点树。任何一个元素的最终样式,都是从浏览器的默认样式和通用规则开始,到这个节点的最上层元素,一层一层合并具体规则,得出最终样式(就是级联)。

    • JS脚本的解析、编译和执行

  • 浏览器渲染

    • 布局:解析生成的DOM树和CSSOM树会组合称为渲染树(Render树),所有拥有内容和计算样式后可见的节点都保存在渲染树上面,渲染树会计算和确定每个节点的大小和位置,这个过程叫做布局(Layout)。

      • PS:第一次确定节点的大小和位置称为布局,随后重新计算节点大小位置称为回流
    • 绘制:将渲染树上面的各个节点绘制在屏幕上(将元素的可见部分转换为屏幕的实际像素)

  • 用户和渲染的页面进行交互

获取元素

基础使用

 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>获取元素(基础使用)</title>
 </head>
 <body>
     <p>hello world</p>
     <script>
         // document属性:获取document对象
         console.log(document);
 ​
         // document.documentElement: 获取html元素
         console.log(document.documentElement);
 ​
         // document.body: 获取body元素
         console.log(document.body); 
 ​
         // document.title: 获取页面标题
         console.log(document.title);
         
         // document.write(内容): 向当前的HTML文档写入指定内容
         document.write('哇牛');
     </script>
 </body>
 </html>

API

 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>获取元素(API)</title>
 </head>
 <body>
     <div id="container">
         <h2>任务清单</h2>
         <ul>
             <li>项目名称</li>
             <li class="item">起床</li>
             <li class="item">洗漱</li>
             <li class="item">吃饭</li>
             <li class="item">上学</li>
             <li class="item important">睡觉</li>
         </ul>
     </div>
     <script>
         // document.getElementById(id值): 获取指定id值的元素
         let element = document.getElementById('container');
         console.log(element);
 ​
         // document.getElementsByTagName(标签名): 获取页面指定标签的所有元素
         let list = document.getElementsByTagName('li');
         console.log(list); // 输出所有找到的元素
         for (let i=0; i<list.length; i++) {
             console.log(list[i]);
         }
         // list.splice(0, 0, 'hello'); // 类数组对象不能直接调用数组的API
 ​
         // document.getElementsByClassName(类名): 获取页面包含指定类名的所有元素
         list = document.getElementsByClassName('item');
         console.log(list);
         list = document.getElementsByClassName('item important');
         console.log(list);
 ​
         // CSS选择元素
         // 父元素.querySelector(选择器): 返回满足指定选择器条件的第一个元素
         let divElement = document.querySelector('#container'); // 找document下面id为container的第一个元素
         console.log(divElement);
         let lastLiElement = divElement.querySelector('li:last-child'); // 找div元素下面最后一个li的元素(或者.item.important)
         console.log(lastLiElement);
 ​
         // 父元素.querySelectorAll(选择器): 返回满足指定选择器条件的所有元素
         let liList = document.querySelectorAll('li'); // 返回document下面所有的li元素(NodeList类型)
         // HTMLCollection: 即时更新的(live);当其所包含的文档结构发生改变时,它会自动更新
         // NodeList: 一个静态集合,也就意味着随后对文档对象模型的任何改动都不会影响集合的内容
         console.log(liList);
 ​
         liList = document.querySelectorAll('.item'); // 返回document下面所有包含item类名的元素
         for (let li of liList) { // 遍历元素列表,并输出
             console.log(li);
         }
     </script>
 </body>
 </html>

元素内容

 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>元素内容</title>
 </head>
 <body>
     <div id="container">
         <h2>任务清单</h2>
         <ul>
             <li>项目名称</li>
             <li class="item">起床</li>
             <li class="item">洗漱</li>
             <li class="item">吃饭</li>
             <li class="item">上学</li>
             <li class="item important">睡觉</li>
         </ul>
     </div>
     <script>
         // 元素.innerText 获取元素的文本内容
         let elem = document.querySelector('h2');
         console.log(elem.innerText); // 任务清单
         elem = document.querySelector('ul'); 
         console.log(elem.innerText); // 一般不会这么做,这样会输出ul下面所有子元素的文本内容
 ​
         // 元素.innerText = '内容' 更新元素的文本内容
         elem = document.querySelector('.important');
         elem.innerText = '打游戏';
 ​
         // 元素.innerHTML 获取元素下面的子元素及其内容
         elem = document.querySelector('ul'); 
         console.log(elem.innerHTML);
 ​
         // 元素.innerHTML = '内容';
         elem.innerText = '<li>hehe</li>'; // 不解析标签,只当作普通文本
         elem.innerHTML = '<li>hehe</li>'; // 可以解析HTML标签
     </script>
 </body>
 </html>

元素属性

 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>元素属性</title>
 </head>
 <body>
     <img src="test.jpg" alt="test" class="cover">
     <input type="text" placeholder="请输入姓名" id="username">
     <script>
         // 元素.属性 获取元素的属性值
         let imgElement = document.querySelector('img');
         let inputElement = document.querySelector('#username');
         console.log(imgElement.src); // file:///C:/Users/he/Desktop/web16/web16_20230626/test.jpg
         console.log(imgElement.alt); // test
         console.log(inputElement.placeholder); // 请输入姓名
 ​
         // 元素.属性 = 值
         imgElement.src = 'woniu.png';
         imgElement.alt = '蜗牛'; 
 ​
         // 元素.getAttribute(属性名) 返回对应的属性值
         console.log(imgElement.getAttribute('src')); // woniu.png
         console.log(inputElement.getAttribute('type')); // text
 ​
         // 元素.setAttribute(属性名, 属性值); 
         // 有则更新,无则添加
         inputElement.setAttribute('type', 'password');
         imgElement.setAttribute('class', 'avatar');
     </script>
 </body>
 </html>

事件基础

事件:用户跟网页之间产生的交互行为就是事件。我们可以通过JS让某些元素监听特定的事件,当事件发生的时候,就会执行预设的代码

事件三要素:

  • 事件源:触发事件的元素(让哪个元素监听)
  • 事件类型:在事件源上监听哪种交互(让元素监听什么)
  • 事件处理程序:触发事件时的处理函数(事件发生了做什么)

DOM级别

  • DOM0级:早期还未形成标准的试验性的DOM,不同浏览器之间存在操作的兼容性问题

  • DOM1级:定义了HTML文档的底层结构,添加了针对HTML对象的操作方法,即document对象,可以允许我们进行方便的增删改查

  • DOM2级:增加了更多的方法接口:事件监听和处理、CSS样式、遍历操作等

  • DOM3级:统一了加载和保存文档的方法,提供了XML文档的支持

    目前使用最广泛的还是DOM0级和DOM2级

事件属性

 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>事件属性</title>
     <style>
         div {
             width: 400px;
             height: 200px;
             background-color: red;
             color: white;
             font-size: 100px;
             text-align: center;
             line-height: 200px;
             margin: 50px auto;
             cursor: pointer;
             border-radius: 100px;
         }
     </style>
 </head>
 <body>
     <div onclick="show()">点我</div>
     <script>
         /*  
             通过HTML事件属性设置事件
             语法: 
             - 添加: <元素 onclick="函数名()"></元素>
             - 移除: element.removeAttribute(attrName);element.removeAttribute(attrName);
                     元素.removeAttribute(事件属性)
             注意:
             - 不推荐使用:将HTML和JS混到一起,维护复杂
         */
         function show() {
             alert('hello world');
         }
 ​
         let div = document.querySelector('div');
         div.removeAttribute('onclick');
     </script>
 </body>
 </html>

DOM0级

 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>DOM0级</title>
     <style>
         div {
             width: 400px;
             height: 200px;
             background-color: red;
             color: white;
             font-size: 100px;
             text-align: center;
             line-height: 200px;
             margin: 50px auto;
             cursor: pointer;
             border-radius: 100px;
         }
     </style>
 </head>
 <body>
     <div>点我</div>
     <script>
         /*  
             DOM0级
             语法: 
             - 添加: 元素.on事件类型 = function() {}
             - 移除: 元素.on事件类型 = null;
             注意:
             - 解决了事件属性将HTML和JS绑定的问题,提高了可维护性
             - 不足:无法给同一个元素的同一事件添加多个事件处理程序(新的会覆盖旧的)
         */
         let div = document.querySelector('div');
         // click: 鼠标单击事件
         div.onclick = function() {
             alert('葵花点穴手');
         }
 ​
         div.onclick = () => {
             alert('排山倒海');
         }
 ​
         // 移除单击事件
         div.onclick = null;
     </script>
 </body>
 </html>

DOM2级

 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>DOM2级</title>
     <style>
         div {
             width: 400px;
             height: 200px;
             background-color: red;
             color: white;
             font-size: 100px;
             text-align: center;
             line-height: 200px;
             margin: 50px auto;
             cursor: pointer;
             border-radius: 100px;
         }
     </style>
 </head>
 <body>
     <div>点我</div>
     <button>我的事件可以移除</button>
     <script>
         /*  
             DOM2级
             语法: 
             - 添加:元素.addEventListener(事件类型, 处理函数)
             - 移除:元素.removeEventListener(事件类型, 处理函数)
             注意:
             - DOM2级语法可以为同一个元素,同一事件类型添加多个事件处理程序
             - 移除事件处理程序的函数必须和添加的函数是同一个,否则无法移除
         */
         let div = document.querySelector('div');
         // 事件类型不要加on
         div.addEventListener('click', function() {
             alert('我被点了');
         });
         div.addEventListener('click', function() {
             alert('我还能显示吗')
         });
         div.addEventListener('click', () => {
             alert('我是第三个弹窗');
         });
 ​
         // 移除事件处理程序(此处无法移除,因为传递的第二个函数参数是一个新的函数)
         div.removeEventListener('click', function() {
             alert('我被点了');
         });
 ​
         
         // 标准移除事件处理函数的方式
         function hello() {
             alert('hello world');
         }
 ​
         let btn = document.querySelector('button');
         // 如果希望移除事件处理程序,需要使用具名函数,参数传递的是函数的名字,而不是函数调用的结果
         btn.addEventListener('click', hello);
         // 移除事件
         btn.removeEventListener('click', hello);
     </script>
 </body>
 </html>

数据驱动

数据驱动:以数据为主导,通过对数据的增删改查完成页面的渲染

对象数据渲染页面(诗词)

 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>诗词展示</title>
 </head>
 <body>
     <div class="container">
     </div>
     <script>
         let data = {
             title: '静夜思-李白',
             sentence: [
                 '床前明月光',
                 '疑是地上霜',
                 '举头望明月',
                 '低头思故乡'
             ]
         };
 ​
         // 死数据
         let str = '';
         str += '<h2>静夜思</h2>';
         str += '<p>床前明月光</p>';
         str += '<p>疑是地上霜</p>';
         str += '<p>举头望明月</p>';
         str += '<p>低头思故乡</p>';
         let div = document.querySelector('div');
         div.innerHTML = str;
 ​
         // 对象数据
         let str = '';
         str += `<h2>${data.title}</h2>`;
         for (let item of data.sentence) {
             str += `<p>${item}</p>`;
         }
         console.log(str);
         let div = document.querySelector('div');
         div.innerHTML = str;
     </script>
 </body>
 </html>

多首诗词

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>多首诗词</title>
</head>
<body>
    <div class="container"></div>
    <script>
        let data = [
            {
                title: '无题·昨夜星辰昨夜风',
                sentence: [
                    '昨夜星辰昨夜风',
                    '画楼西畔桂堂东',
                    '身无彩凤双飞翼',
                    '心有灵犀一点通',
                    '隔座送钩春酒暖',
                    '分曹射覆蜡灯红',
                    '嗟余听鼓应官去',
                    '走马兰台类转蓬'
                ]
            },
            {
                title: '夜雨寄北',
                sentence: [
                    '君问归期未有期',
                    '巴山夜雨涨秋池',
                    '何当共剪西窗烛',
                    '却话巴山夜雨时'
                ]
            },
            {
                title: '琵琶行',
                sentence: [
                    '我闻琵琶已叹息',
                    '又闻此语重唧唧',
                    '同是天涯沦落人',
                    '相逢何必曾相识'
                ]
            },
            {
                title: '别董大',
                sentence: [
                    '千里黄云白日曛',
                    '北风吹雁雪纷纷',
                    '莫愁前路无知己',
                    '天下谁人不识君',
                    '六翮飘飖私自怜',
                    '一离京洛十馀年',
                    '丈夫贫践应未足',
                    '今日相逢无酒钱'
                ]
            },
            {
                title: '渡汉江',
                sentence: [
                    '岭外音书断',
                    '经冬复历春',
                    '近乡情更怯',
                    '不敢问来人'
                ]
            }
        ];
        let str = '';
        for (let item of data) {
            // 通过每首诗的数据构建字符串,拼接到str上
            str += `<h2>${item.title}</h2>`;
            for (let i of item.sentence) {
                // 将每一句诗放在p元素中,拼接到str上
                str += `<p>${i}</p>`;
            }
        }
        console.log(str);
        let container = document.querySelector('.container');
        container.innerHTML = str;
    </script>
</body>
</html>

诗词切换(加入事件)

 <!DOCTYPE html>
 <html lang="en">
 ​
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>诗词切换</title>
     <style>
         * {
             padding: 0;
             margin: 0;
         }
 ​
         .container {
             width: 500px;
             margin: 50px auto;
             text-align: center;
         }
 ​
         .content {
             background-color: antiquewhite;
             color: black;
             text-align: center;
             padding: 10px;
         }
 ​
         p {
             margin-top: 10px;
         }
 ​
         button {
             margin-top: 20px;
             font-size: 25px;
             padding: 5px;
         }
     </style>
 </head>
 ​
 <body>
     <div class="container">
         <div class="content"></div>
         <button>切换下一首</button>
     </div>
     <script>
         let data = [
             {
                 title: '无题·昨夜星辰昨夜风',
                 sentence: [
                     '昨夜星辰昨夜风',
                     '画楼西畔桂堂东',
                     '身无彩凤双飞翼',
                     '心有灵犀一点通',
                     '隔座送钩春酒暖',
                     '分曹射覆蜡灯红',
                     '嗟余听鼓应官去',
                     '走马兰台类转蓬'
                 ]
             },
             {
                 title: '夜雨寄北',
                 sentence: [
                     '君问归期未有期',
                     '巴山夜雨涨秋池',
                     '何当共剪西窗烛',
                     '却话巴山夜雨时'
                 ]
             },
             {
                 title: '琵琶行',
                 sentence: [
                     '我闻琵琶已叹息',
                     '又闻此语重唧唧',
                     '同是天涯沦落人',
                     '相逢何必曾相识'
                 ]
             },
             {
                 title: '别董大',
                 sentence: [
                     '千里黄云白日曛',
                     '北风吹雁雪纷纷',
                     '莫愁前路无知己',
                     '天下谁人不识君',
                     '六翮飘飖私自怜',
                     '一离京洛十馀年',
                     '丈夫贫践应未足',
                     '今日相逢无酒钱'
                 ]
             },
             {
                 title: '渡汉江',
                 sentence: [
                     '岭外音书断',
                     '经冬复历春',
                     '近乡情更怯',
                     '不敢问来人'
                 ]
             }
         ];
         /*  
             - 进入页面默认显示第一首诗
             - 点击下一首按钮,显示下一首诗
             - 如果是最后一首,弹窗提示“今天的诗词已经学完了”
         */
 ​
         let index = 0;
 ​
         function render() {
             let item = data[index];
             let str = '';
             str += `<h2>${item.title}</h2>`;
             for (let i of item.sentence) {
                 str += `<p>${i}</p>`;
             }
             let content = document.querySelector('.content');
             content.innerHTML = str;
         }
 ​
         render();
         
 ​
         let btn = document.querySelector('button');
         btn.addEventListener('click', () => {
             index++;
             if (index >= data.length) {
                 alert('今天的诗词已经学完了');
                 return;
             }
             render();
 ​
             // if (index < data.length) {
             //     render();
             // } else {
             //     alert('今天的诗词已经学完了');
             //     return;
             // }
             
         })
     </script>
 </body>
 ​
 </html>

商品渲染(给动态添加的元素绑定事件)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>商品渲染</title>
    <style>
        * {
            padding: 0;
            margin: 0;
            list-style: none;
        }

        .container {
            width: 600px;
            margin: 50px auto;
            text-align: center;
        }

        p {
            margin: 20px 0;
            color: gold;
        }

        div>img {
            border: 1px solid black;
            height: 400px;
        }

        ul {
            display: flex;
            justify-content: center;
        }

        ul>li>img {
            width: 80px;
        }
        
        .active {
            border: 1px solid black;
        }
    </style>
</head>
<body>
    <div class="container">
        <h3></h3>
        <p></p>
        <img src="" alt="">
        <ul></ul>
    </div>
    <script>
        let data = {
            id: 1,
            name: '小米13 ultra',
            price: 5999.00,
            images: ['1.png', '2.png', '3.png', '4.png']
        };
        // 更新名称
        let h3 = document.querySelector('h3');
        h3.innerText = data.name;
        // 更新价格
        let p = document.querySelector('p');
        p.innerText = data.price.toFixed(2);
        // 更新大图
        let cover = document.querySelector('div>img');
        cover.src = data.images[0];
        cover.alt = data.images[0];
        // 更新小图
        let str = '';
        data.images.forEach((img, index) => {
            if (index === 0) {
                str += `<li><img src="${img}" alt="${img}" class="active"></li>`;
            } else {
                str += `<li><img src="${img}" alt="${img}"></li>`;
            }
        })
        let ul = document.querySelector('ul');
        ul.innerHTML = str;
        // 找到所有li中的img,添加点击事件
        let imgs = ul.querySelectorAll('img');
        imgs.forEach(img => {
            img.addEventListener('click', () => {
                // 找到所有的小图元素将class属性移除(去掉所有的黑框)
                for (let item of imgs) {
                    item.removeAttribute('class');
                }
                // 将当前被点击元素的src和alt更新到cover大图上
                cover.src = img.src;
                cover.alt = img.alt;
                // 给当前元素添加class为active的类,让当前元素显示黑框
                img.setAttribute('class', 'active');
            })
        });
    </script>
</body>
</html>

数据更新后的渲染

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>数据更新后的渲染</title>
    <style>
        * {
            padding: 0;
            margin: 0;
        }

        table {
            width: 600px;
            margin: 50px auto;
            border: 1px solid black;
            border-collapse: collapse;
            text-align: center;
        }

        th,
        td {
            border: 1px solid black;
            padding: 5px;
        }

        div {
            text-align: center;
            margin-top: 20px;
        }

        button {
            padding: 5px 15px;
            font-size: 16px;
            margin: 0 10px;
            border: none;
            cursor: pointer;
            border: 1px solid white;
        }

        button:hover {
            border: 1px solid black;
        }

        button:first-child {
            background-color: green;
            color: white;
        }

        button:nth-child(2) {
            background-color: yellow;
            color: black;
        }

        button:last-child {
            background-color: red;
            color: white;
        }
    </style>
</head>

<body>
    <div>
        <button id="create">添加</button>
        <button id="update">更新</button>
        <button id="delete">删除</button>
    </div>
    <table>
        <thead>
            <tr>
                <th>编号</th>
                <th>名称</th>
                <th>价格</th>
            </tr>
        </thead>
        <tbody></tbody>
    </table>
    <script>
        /*  
            使用初始数据渲染页面,页面提供三个功能
            - {id: 1, name: 'iphone 14 plus', 'price': 6999.00}
            - 添加:接收用户输入编号,名称和价格,将数据保存到对象数组中
            - 更新:
                - 接收用户输入编号,如果编号不存在,就弹窗提示“编号错误”
                - 编号正确,就接收用户输入的名称和价格,更新到对应编号的数据中
            - 删除:
                - 接收用户输入的编号,将对应编号的数据删除
        */
        let data = [
            { id: 1, name: 'iphone 14 plus', price: 6999.00 },
            { id: 2, name: '富光水杯', price: 29.99 },
            { id: 3, name: '紫米充电头', price: 129.00 },
            { id: 4, name: 'thinkpad x13', price: 5999.00 },
            { id: 5, name: '小米手环8', price: 239.00 }
        ];

        function render() {
            let content = '';
            data.forEach(item => {
                content += `<tr>
                <td>${item.id}</td>
                <td>${item.name}</td>
                <td>${item.price.toFixed(2)}</td>
            </tr>`
            })
            let tbody = document.querySelector('tbody');
            tbody.innerHTML = content;
        }

        render();

        document.querySelector('#create').addEventListener('click', () => {
            // 接收用户输入的编号
            let id = +prompt('请输入编号');
            // 使用find方法找对应id的数据,如果返回的不是undefined,证明数据存在,弹窗提示
            let existsItem = data.find(item => item.id === id);
            if (existsItem) {
                alert('编号重复');
                return;
            }
            // 数据没有重复,就继续输入名称和价格
            let name = prompt('请输入名称');
            let price = +prompt('请输入价格');
            // 将数据保存到对象数组中
            data.push({
                id,
                name,
                price
            });
            render();
        });

        document.querySelector('#update').addEventListener('click', () => {
            // 接收用户输入的编号
            let id = +prompt('请输入编号');
            // 使用find方法找对应id的数据,如果返回的是undefined,证明数据不存在,弹窗提示并退出
            let existsItem = data.find(item => item.id === id);
            if (!existsItem) {
                alert('编号不存在');
                return;
            }
            let name = prompt('请输入名称');
            let price = +prompt('请输入价格');
            existsItem.name = name;
            existsItem.price = price;
            render();
        });

        document.querySelector('#delete').addEventListener('click', () => {
            // 接收用户输入的编号,将对应编号的数据删除
            let id = +prompt('请输入要删除的编号');
            // 找到对应id的数据
            let target = data.find(item => item.id === id);
            // 在data数组中找到这个target对象的下标
            let index = data.indexOf(target);
            if (index !== -1) {
                // 通过splice方法将数组中target数据删除
                data.splice(index, 1);
                render();
            }

            // data = data.filter(item => item.id !== id)
            // render()
        });
    </script>
</body>

</html>

操作CSS样式

setAttribute和getAttribute

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Attribute</title>
    <style>
        .red {
            color: red;
        }

        .large {
            font-size: 50px;
        }
    </style>
</head>
<body>
    <p>我是一段文字,没撒意义</p>
    <script>
        /*  
            setAttribute和getAttribute
        */
        let p = document.querySelector('p');
        p.setAttribute('class', 'red'); // 字体变红
        p.setAttribute('class', 'large'); // 字体变大
        p.setAttribute('class', 'red large'); // 字体变红变大
        console.log(p.getAttribute('class')); // 获取class属性值并输出
    </script>
</body>
</html>

className属性

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>className</title>
    <style>
        .red {
            color: red;
        }

        .large {
            font-size: 50px;
        }
    </style>
</head>
<body>
    <p>我是一段文字,没撒意义</p>
    <script>
        /*  
            className
        */
        let p = document.querySelector('p');
        console.log(p.className); // 获取className
        p.className = 'red';
        p.className = 'large';
        p.className = 'large red'; // 同时拥有large和red两个类
        console.log(p.className);
    </script>
</body>
</html>

classList属性

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>classList</title>
    <style>
        .red {
            color: red;
        }

        .large {
            font-size: 50px;
        }
    </style>
</head>
<body>
    <p class="large">我是一段文字,没撒意义</p>
    <button>切换红色</button>
    <script>
        /*  
            classList属性:类数组对象保存class值
            add(类名1, 类名2,...): 添加类
            remove(类名1, 类名2, ...): 删除类
            toggle(类名): 有就移除,没有就添加
        */
        let p = document.querySelector('p');
        console.log(p.classList); // 类数组数据
        p.classList.remove('red', 'large'); // 同时删除red和large两个类
        p.classList.add('large', 'nothing'); // 同时添加large和nothing两个类

        document.querySelector('button').onclick = () => {
            p.classList.toggle('red'); // 每次点击动态的切换red类
        }
    </script>
</body>
</html>

style属性

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>style属性</title>
    <style>
        .red {
            color: red;
        }

        .large {
            font-size: 50px;
        }
    </style>
</head>
<body>
    <p class="red large">我是一段文字,没撒意义</p>
    <script>
        /*  
            style属性:设置和获取内联样式,不能获取内部样式和外部样式
            提示:
            - 多个单词构成的CSS属性最好改为小驼峰的形式设置
        */
        let p = document.querySelector('p');
        // p.style = "color:blue;"; 相当于 p.className = 'red';
        p.style.color = 'blue'; // 设置内联样式颜色为蓝色
        p.style.fontWeight = '900'; // 设置内联样式font-weight为900
        console.log(p.style.fontWeight); // 900
        p.style = 'color: black; font-weight: 100; background-color: red;'; // 同时设置三个内联样式
    </script>
</body>
</html>

getComputedStyle方法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>getComputedStyle</title>
    <style>
        .red {
            color: red;
        }

        .large {
            font-size: 50px;
        }
    </style>
</head>
<body>
    <p class="large red" style="color: blue; font-weight: 900;">我是一段文字,没撒意义</p>
    <script>
        /*
            getComputedStyle: 获取元素最终的样式
        */
        let p = document.querySelector('p');
        let value = getComputedStyle(p);
        console.log(value.color); // rgb(0,0,255)
        console.log(value.fontWeight); // 900
        console.log(value.fontSize); // 50px
    </script>
</body>
</html>

添加元素

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>添加元素</title>
</head>
<body>
    <ul>
        <li>画楼西畔桂堂东</li>
        <li>身无彩凤双飞翼</li>
    </ul>
    <script>
        /*  
            添加元素步骤:
            1. createElement创建新元素
            2. 为元素设置属性和内容
            3. appendChild或insertBefore添加元素到DOM树中
            语法:
            document.createElement(标签名) 返回对应标签的新元素,没有内容和属性
            父元素.appendChild(子元素) 将子元素放到父元素的子元素列表的最后一个位置
            父元素.insertBefore(子元素,参考元素) 将子元素插入到父元素中参考元素之前
            注意:
            - 如果appendChild或者insertBefore要插入的元素已经在DOM中,这个操作实际就是将元素移动到新的位置
        */

        // 创建“心有灵犀一点通”的li元素,放到ul的最后一位
        let newLi = document.createElement('li');
        newLi.innerText = '心有灵犀一点通'; // 因为没有添加新元素到DOM树中,所以看不见
        let ul = document.querySelector('ul');
        // appendChild将newLi放到ul子元素的最后
        ul.appendChild(newLi);

        // 创建“昨夜星辰昨夜风”到ul的第一位
        let firstLi = document.createElement('li');
        firstLi.innerText = '昨夜星辰昨夜风';
        // 获取目标参考元素<li>画楼西畔桂堂东</li>
        let target = ul.querySelector('li');
        // insertBefore将firstLi放在target之前
        ul.insertBefore(firstLi, target);

        // 移动已存在的DOM元素
        ul.insertBefore(firstLi, newLi);
    </script>
</body>
</html>

删除元素

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>删除元素</title>
</head>
<body>
    <ul>
        <li>昨夜星辰昨夜风</li>
        <li>画楼西畔桂堂东</li>
        <li>身无彩凤双飞翼</li>
        <li>心有灵犀一点通</li>
    </ul>
    <script>
        /*  
            删除元素语法
            1. 元素.remove() 删除当前的元素
            2. 父元素.removeChild(子元素) 父元素删除指定的子元素
        */
        // 使用remove删除<li>画楼西畔桂堂东</li>
        let li = document.querySelector('li:nth-child(2)');
        li.remove();

        // 使用removeChild删除心有灵犀一点通
        let ul = document.querySelector('ul');
        let li2 = document.querySelector('li:last-child');
        ul.removeChild(li2);

        // 遍历删除所有剩余的li
        for (let item of document.querySelectorAll('li')) {
            item.remove();
        }

        // 清空子元素:innerHTML = '';
    </script>
</body>
</html>

父子和兄弟元素

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>父子兄弟元素</title>
</head>
<body>
    <div>
        <h2>标题</h2>
        <p>内容</p>
        <ul>
            <li>点赞</li>
            <li>投币</li>
            <li>收藏</li>
        </ul>
    </div>
    <script>
        /*  
            父子兄弟元素:通过一个元素,快速的获取关联的元素,不需要一个一个querySeletor获取
            元素.parentElement 获取该元素的父元素
            元素.firstElementChild 获取该元素的第一个子元素
            元素.lastElementChild 获取该元素的最后一个子元素
            元素.children 获取该元素所有的子元素
            元素.previousElementSibling 获取该元素前面的兄弟元素
            元素.nextElementSibling 获取该元素后面的兄弟元素
        */
        let ul = document.querySelector('ul');
        // parentElement
        console.log(ul.parentElement); // div
        console.log(ul.parentElement.parentElement); // body

        // firstElementChild
        console.log(ul.firstElementChild); // <li>点赞</li>

        // lastElementChild
        console.log(ul.lastElementChild); // <li>收藏</li>

        // children
        console.log(ul.children);

        let p = document.querySelector('p');
        // previousElementSibling
        console.log(p.previousElementSibling); // <h2>标题</h2>

        // nextElementSibling
        console.log(p.nextElementSibling); // ul
        console.log(p.nextElementSibling.nextElementSibling); // null
        console.log(p.nextElementSibling.previousElementSibling); // p
    </script>
</body>
</html>

复制节点

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>复制元素</title>
</head>

<body>
    <div>
        <img src="2.jpg" alt="花千骨">
        <ul>
            <li>hehe</li>
            <li>haha</li>
        </ul>
    </div>
    <script>
        /*  
            cloneNode 复制节点元素
            语法:
            待克隆元素.cloneNode(deep) 返回被克隆的元素
            注意:
            - deep默认为false,只克隆当前的元素(不包括内容)
            - deep设置为true,克隆该元素的所有内容
        */
        // 克隆img元素到div的末尾
        let img = document.querySelector('img');
        let newImg = img.cloneNode();
        let div = document.querySelector('div');
        div.appendChild(newImg);

        // 克隆ul(deep设置为false)
        let ul = document.querySelector('ul');
        let newUl = ul.cloneNode();
        div.appendChild(newUl); // 添加了一个空的ul

        // deep设置true
        let doubleNewUl = ul.cloneNode(true);
        div.appendChild(doubleNewUl); // 添加的ul包括了下面所有的内容
    </script>
</body>

</html>

新式方法

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>新式方法</title>
</head>
<body>
    <div>
        <h2>不知道</h2>
        <ul>
            <li>生当作人杰</li>
            <li>死亦为鬼雄</li>
        </ul>
    </div>
    <script>
        /*  
            语法
            元素.append(新元素) 该元素将新元素,添加到子元素列表末尾
            元素.prepend(新元素) 该元素将新元素,添加到子元素列表开头
            元素.before(新元素) 将新元素放到该元素的前面
            元素.after(新元素) 将新元素放到该元素的后面
        */
        let ul = document.querySelector('ul');

        // 添加新的li到ul的末尾
        let lastLi = document.createElement('li');
        lastLi.innerText = '李清照';
        ul.append(lastLi);

        // 添加新的li到ul的开头
        let firstLi = document.createElement('li');
        firstLi.innerText = '锄禾日当午';
        ul.prepend(firstLi);

        // 添加新的p元素到ul的前面
        let title = document.createElement('p');
        title.innerText = '夏日绝句';
        ul.before(title);

        // 添加新的p元素到ul的后面
        let end = document.createElement('p');
        end.innerText = '到此结束';
        ul.after(end);
    </script>
</body>
</html>

输入框事件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>输入框事件</title>
</head>
<body>
    <input type="text" id="username" name="username" value="hello">
    <script>
        let input = document.querySelector('input');
        // value属性可以获取input值
        console.log(input.value);
        // value属性也可以赋值
        input.value = 'world';

        // 获取焦点事件 focus : 输入前对准备数据(状态)进行验证,获取焦点以后修改样式等
        input.addEventListener('focus', () => {
            console.log('战斗已经开始');
        })

        // 失去焦点事件 blur : 对输入完成的数据进行验证,修改样式
        input.addEventListener('blur', () => {
            console.log('战斗已经结束');
        })

        // 输入内容触发input事件 : 输入过程中一直对数据进行验证,只要数据修改不满足要求,就可以立即给出提示
        input.addEventListener('input', () => {
            console.log(input.value);
        })
    </script>
</body>
</html>

单选复选框事件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>单选复选框事件</title>
</head>
<body>
    <div>
        <input type="radio" name="gender" id="male"><input type="radio" name="gender" id="female"></div>
    <div>
        <input type="checkbox" name="language" id="js">JavaScript
        <input type="checkbox" name="language" id="java">Java
        <input type="checkbox" name="language" id="py">Python
    </div>
    <script>
        // 单选、复选元素可以通过click或change事件触发改动
        let radios = document.querySelectorAll('[name="gender"]');
        // 为所有单选按钮绑定click事件,触发时打印元素的id值
        radios.forEach(radio => {
            radio.addEventListener('click', () => {
                // 可以获取当前选中元素的id值
                console.log(radio.id);
            });
        });

        let checkboxes = document.querySelectorAll('[name="language"]');
        // 为所有的复选框绑定change事件,触发时打印元素id
        checkboxes.forEach(box => {
            box.addEventListener('change', () => {
                // 复选框checked属性(布尔值)记录当前元素的选中状态
                console.log(`${box.id}状态为:${box.checked}`);
            })
        });
    </script>
</body>
</html>

下拉列表事件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>下拉列表事件</title>
</head>
<body>
    <select name="hobby" id="hobby">
        <option value="sing"></option>
        <option value="dance"></option>
        <option value="rap">Rap</option>
    </select>
    <script>
        let select = document.querySelector('select');
        // selected属性:默认选中
        select.querySelector('option:last-child').selected = true;

        // 为select添加change事件,当用户操作option选项时,会将这个option的value值更新到select的value属性上
        select.addEventListener('change', () => {
            console.log(select.value);
        })
    </script>
</body>
</html>

框架事件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>框架事件</title>
</head>
<body>
    <h2>框架事件</h2>
    <img src="" alt="花千骨" width="300">
    <script>
        /*  
            load事件:页面全部加载完成时触发(包括CSS,图片等资源)
            语法: 为window对象添加load事件

            DOMContentloaded事件: 页面HTML全部解析加载完成
            语法:为document对象添加DOMContentloaded事件
        */
        window.addEventListener('load', () => {
            console.log('此处的代码在整个页面加载完成以后才会触发');
        });

        document.addEventListener('DOMContentLoaded', () => {
            console.log('此处的代码在DOM加载完成后就会执行');
        })

        // 之后为了避免因DOM加载问题,网络问题,或者资源问题导致代码执行出错,将页面初始化相关代码放到load或者
        // DOMContentLoaded事件处理函数中
        window.onload = () => {
            // render();
        }

        document.addEventListener('DOMContentLoaded', () => {
            // querySelector;
            // render();
        });

        // resize事件:在窗口大小发生变化时触发,能够通过window.innerWidth和window.innerHeight获取窗口可视区域的大小
        // 语法:为window添加resize事件
        window.addEventListener('resize', () => {
            console.log(`宽度${window.innerWidth},高度${window.innerHeight}`);
        })
    </script>
</body>
</html>

鼠标事件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>鼠标事件</title>
</head>
<body>
    <div style="width: 300px; height: 200px; border: 1px solid black;">
        <button>点我</button>
    </div>
    <script>
        let btn = document.querySelector('button');
        // click 单击事件
        btn.onclick = () => {
            console.log('我被点了');
        }

        // mousedown 鼠标按下
        btn.onmousedown = () => {
            console.log('这个人开始点了');
        }

        // mouseup 鼠标松开
        btn.onmouseup = () => {
            console.log('这个人松开了');
        }

        // dblclick 双击事件
        btn.ondblclick = () => {
            console.log('连点两次');
        }

        // 触发顺序:mousedown-mouseup-click-dblclick

        // contextmenu 鼠标右键点击
        let div = document.querySelector('div');
        div.oncontextmenu = () => {
            console.log('用户右键查看功能列表');
        }

        // 鼠标移入移出
        // 移入:mouseover mouseenter
        // 移出: mouseout mouseleave
        // 注意: mouseover和mouseout在进入子元素时,也会触发;mouseenter和mouseleave不会
        div.onmouseover = () => {
            console.log('over进入');
        }

        div.onmouseenter = () => {
            console.log('enter进入');
        }

        div.onmouseout = () => {
            console.log('out离开');
        }

        div.onmouseleave = () => {
            console.log('leave离开');
        }

    </script>
</body>
</html>

键盘事件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        // keydown 键盘按下
        // keypress 键盘按下并松开
        // keyup 键盘松开
        // 触发顺序: keydown-keypress-keyup
        
        document.addEventListener('keydown', () => {
            console.log('有一个按键被按下了');
        })

        document.addEventListener('keypress', () => {
            console.log('有一个按键被按下并松开了');
        })

        document.addEventListener('keyup', () => {
            console.log('终于松开了');
        })
    </script>
</body>
</html>

事件对象

事件对象:每次事件发生的时候,浏览器会自动创建event事件对象,把触发事件的信息放到event对象上,将这个对象作为参数传递给事件处理函数,供函数使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>事件对象</title>
</head>
<body>
    <button style="height: 2000px;">点我</button>
    <script>
        let btn = document.querySelector('button');
        btn.addEventListener('click', (e) => {
            // type属性:触发实现的类型
            console.log(e.type);
            // currentTarget属性 正在处理事件的元素
            console.log(e.currentTarget);
            // clientX, clientY 触发事件时鼠标相对于窗口的位置
            console.log(e.clientX, e.clientY);
            // pageX, pageY 触发事件时鼠标相对于整个页面的位置
            console.log(e.pageX, e.pageY);
        });

        document.addEventListener('keyup', (e) => {
            // key属性:保存触发键盘事件的按键字符
            console.log(e.key);
            // keyCode: 键盘事件的按键码
            console.log(e.keyCode);
        })
    </script>
</body>
</html>

事件流

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>事件流</title>
</head>
<body>
    <div style="width: 300px; height: 200px; border: 1px solid black;">
        <button>点我</button>
    </div>
    <script>
        /*  
            事件流:
            当某个事件触发时,这个事件在嵌套标签之间的触发顺序
            根据触发顺序不同分成事件冒泡和事件捕获
        */
        document.querySelector('button').onclick = () => {
            alert('按钮被点击');
        }

        document.querySelector('div').onclick = () => {
            alert('容器被点击');
        }
    </script>
</body>
</html>

事件冒泡

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>事件冒泡</title>
</head>
<body>
    <div style="width: 300px; height: 200px; border: 1px solid black;">
        <button>点我</button>
    </div>
    <script>
        /*  
            事件冒泡:
            当事件发生在一个元素上,会先执行这个元素的事件处理函数
            然后执行这个元素的父元素的事件处理函数,再找这个元素的父
            级的父级,一直执行到根元素的事件处理函数
        */
        document.querySelector('button').onclick = () => {
            alert('button触发');
        }

        document.querySelector('div').onclick = () => {
            alert('div触发');
        }

        document.body.onclick = () => {
            alert('body触发');
        }
    </script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>事件冒泡</title>
</head>
<body>
    <div style="width: 300px; height: 200px; border: 1px solid black;">
        <button>点我</button>
    </div>
    <script>
        /*  
            currentTarget属性:当前正在处理事件的元素
            target属性:始终指向嵌套最深的,引发事件的元素
        */

        // 如果点击的是button
        document.querySelector('button').onclick = (e) => {
            console.log(e.currentTarget);  // button
            console.log(e.target); // button
        }

        document.querySelector('div').onclick = (e) => {
            console.log(e.currentTarget); // div
            console.log(e.target); // button
        }

        document.body.onclick = (e) => {
            console.log(e.currentTarget); // body
            console.log(e.target); //button
        }
    </script>
</body>
</html>

阻止事件冒泡

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>阻止事件冒泡</title>
</head>
<body>
    <div style="width: 300px; height: 200px; border: 1px solid black;">
        <button>点我</button>
    </div>
    <script>
        /*  
            阻止事件冒泡
            默认事件冒泡流中,即使内层的元素可以保证事件处理完成,依旧会将事件
            传播到外层嵌套元素。如果内层处理函数可以将事件处理完成,可以使用
            event对象的stopPropagation方法阻止这个行为
            语法:
            event对象.stopPropagation();
            注意:
            - 除非在实际代码中明确需要阻止冒泡,否则不要随便这样做
        */
        document.querySelector('button').onclick = (e) => {
            e.stopPropagation(); // 阻止事件冒泡到外层元素
            alert('我自己就可以完全处理');
        }

        document.querySelector('div').onclick = (e) => {
            alert('我也能处理');
        }
    </script>
</body>
</html>

事件捕获

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>事件捕获</title>
</head>
<body>
    <div style="width: 300px; height: 200px; border: 1px solid black;">
        <button>点我</button>
    </div>
    <script>
        /*  
            事件捕获:
            与事件冒泡相反,从根元素开始出发,一直走到目标(最深层嵌套)元素
            默认事件流是事件冒泡的机制,可以在绑定事件时设置事件捕获模式
            语法:
            元素.addEventListener(事件类型,事件处理函数,是否启用捕获模式)
            注意:
            - DOM0级只支持事件冒泡,DOM2级支持冒泡和捕获两种机制
            - 移除事件处理函数时,其中的启用捕获模式值也必须完全一致
            - 事件流传播在嵌套元素中触发相同类型的事件处理函数,如果类型不同不会触发

            DOM事件传播阶段
            - 捕获阶段:从根元素走到目标元素
            - 目标阶段:到达了目标元素
            - 冒泡阶段:从目标元素走到根元素
        */
        document.querySelector('button').addEventListener('click', () => {
            console.log('button触发');
        })

        document.querySelector('div').addEventListener('click', () => {
            console.log('div触发');
        }, true)

        document.body.addEventListener('click', () => {
            console.log('body触发');
        })

        document.documentElement.addEventListener('click', () => {
            console.log('html触发');
        }, true);
    </script>
</body>
</html>

默认行为

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>默认行为</title>
</head>
<body>
    <a href="http://www.baidu.com">百度</a>
    <form action="3-事件流.html" method="get">
        <input type="text" id="username" name="username">
        <input type="submit" value="提交">
    </form>
    <input type="checkbox">看看能不能勾选
    <script>
        /*  
            不同元素触发事件会有不同的默认行为
            - 链接点击会跳转
            - 表单点击提交按钮会将数据提交到指定的位置
            - 点击复选框选中元素
            - 右键出现上下文菜单

            阻止默认行为
            1. 标准方式:event对象.preventDefault();
            2. DOM0级:return false;
        */
        
        // 阻止超链接的跳转
        document.querySelector('a').addEventListener('click', (e) => {
            e.preventDefault(); // 可以阻止
            return false; // 不能阻止,只有DOM0级可以这么用
        })
        document.querySelector('a').onclick = (e) => {
            return false;
        }
        // 阻止表单提交
        document.querySelector('[type="submit"]').addEventListener('click', (e) => {
            e.preventDefault();
        })
        // 阻止复选框选中
        document.querySelector('[type="checkbox"]').addEventListener('click', (e) => {
            e.preventDefault();
        })
    </script>
</body>
</html>

事件委托

 <!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>事件委托</title>
 </head>
 <body>
     <ul>
         <li>天王盖地虎</li>
         <li>宝塔镇河妖</li>
         <li>谁知盘中餐</li>
         <li>粒粒皆辛苦</li>
     </ul>
     <script>
         /*  
             事件委托:
             利用事件冒泡机制和event对象,将原本需要添加到每个子元素的事件,
             交给父元素执行,这样可以减少事件处理程序,提高代码可维护性
             步骤:
             1. 给父元素绑定事件处理程序
             2. 触发事件时,通过event对象的target属性可以获得触发事件的目标元素
             3. 根据目标元素的属性和内容执行相应的代码
         */
         let list = document.querySelectorAll('li');
         list.forEach(li => {
             li.addEventListener('click', (e) => {
                 alert(e.target.innerHTML);
             })
         })
 ​
         // 事件委托
         let ul = document.querySelector('ul');
         ul.addEventListener('click', (e) => {
             // 元素nodeName属性记录当前元素的标签名(大写)
             console.log(e.target.nodeName);
             // 如果触发事件的是li,在执行对应的代码;否则不执行
             if (e.target.nodeName === 'LI') {
                 alert(e.target.innerHTML);
             }
         })
     </script>
 </body>
 </html>