DOM

180 阅读16分钟

参考哔哩哔哩pink老师课程

1. DOM 简介

1.1 什么是 DOM

文档对象模型(Document Object Model,简称DOM ),是W3C组织推荐的处理可扩展标记语言( HTML或者XML )的标准编程接口。 W3C已经定义了一系列的DOM接口,通过这些DOM接口可以改变网页的内容、结构和样式。

1.2 DOM 树

在这里插入图片描述

  • 文档:一个页面就是一个文档,DOM中使用document表示
  • 元素:页面中的所有标签都是元素,DOM中使用element表示
  • 节点:网页中的所有内容都是节点(标签、属性、文本、注释等),DOM中使用node表示

DOM把以上内容都称之为对象

2. 获取元素

2.1 如何获取页面元素

DOM 在我们实际开发中主要用来操作元素。我们如何来获取页面中的元素呢?

获取页面中的元素可以使用以下几种方式:

  • 根据ID获取
  • 根据标签名获取
  • 通过HTML5新增的方法获取
  • 特殊元素获取

2.2 根据 ID 获取

使用 getElementByld() 方法可以获取带有 ID 的元素对象,并返回一个 Element 对象。 getElementById | MDN console.dir() 可打印元素的属性和方法

<body>
    <div id="time">2022-8-21</div>
    <script>
        // 1. 因为我们文档页面从上往下加载,所以先得有标签
        //    所以我们script写到标签的下面
        // 2. get 获得 element 元素 by 通过 驼峰命名法 
        // 3. 参数 id是大小写敏感的字符串
        // 4. 返回的是一个元素对象
        var timer = document.getElementById('time'); 
        console.log(timer);
        console.log(typeof timer); // 返回当前数据类型
        // 5. console.dir 打印我们返回的元素对象 更好的查看里面的属性和方法
        console.dir(timer);
    </script>
</body>

2.3 根据标签名获取

使用 getElementsByTagName()方法可以返回带有指定标签名的对象的集合。

  • 返回的是元素对象的集合 ,伪数组形式表示。
  • 可以遍历返回的伪数组。
  • 得到的元素对象是动态的。
  • 若没有元素,则返回空的伪数组[]
<body>
    <ul>
        <li>xky</li>
        <li>xky</li>
        <li>xky</li>
        <li>xky</li>

    </ul>
    <script>
        // 1.返回的是 获取过来元素对象的集合 以伪数组的形式存储的
        var lis = document.getElementsByTagName('li');
        console.log(lis);
        console.log(lis[0]);
        // 2. 我们想要依次打印里面的元素对象我们可以采取遍历的方式
        for (var i = 0; i < lis.length; i++) {
            console.log(lis[i]);

        }
        // 3. 如果页面中只有一个li 返回的还是伪数组的形式 
        // 4. 如果页面中没有这个元素 返回的是空的伪数组的形式
    </script>
</body>

还可以获取某个元素(父元素)内部所有指定标签名的子元素 element.getElementsByTagName('标签名');

注意:父元素必须是单个对象(必须指明是哪一个元素对象),获取的时候不包括父元素自己。

    <body>
    <ol id="ol">
        <li>xky</li>
        <li>xky</li>
        <li>xky</li>
        <li>xky</li>
    </ol>
    <script>
        // var ol = document.getElementsByTagName('ol'); // [ol]
        // console.log(ol[0].getElementsByTagName('li')); // 指定单个对象
        // 但是这样写有点麻烦
        var ol = document.getElementById('ol');
        console.log(ol.getElementsByTagName('li'));
    </script>

2.4 通过 HTML5 新增方法获取

  1. 根据类名返回元素对象集合。document.getElementsByClassName('类名');
  2. 根据指定选择器返回第一个元素对象。document.querySelector('选择器');
  3. 返回指定选择器的所有元素集合。document.querySelectorAll('选择器')
<body>
    <div class="box">盒子1</div>
    <div class="box">盒子2</div>
    <div id="nav">
        <ul>
            <li>首页</li>
            <li>产品</li>
        </ul>
    </div>
    <script>
        // 1. getElementsByClassName 根据类名获得某些元素集合
        var boxs = document.getElementsByClassName('box');
        console.log(boxs);
        // 2. querySelector 返回指定选择器的第一个元素对象
        // 切记 里面的选择器需要加符号 .box  #nav
        var firstBox = document.querySelector('.box');
        console.log(firstBox);
        var nav = document.querySelector('#nav');
        console.log(nav);
        var li = document.querySelector('li');
        console.log(li);
        // 3. querySelectorAll()返回指定选择器的所有元素对象集合
        var allBox = document.querySelectorAll('.box');
        console.log(allBox);
        var lis = document.querySelectorAll('li');
        console.log(lis);
    </script>
</body>

对于这些获取事件,要不就是返回一个元素对象,要不就是返回一个对象集合。对于返回的对象集合,都可以是作为一种伪数组,可以通过 elems.length 来获取元素对象个数。

2.5 获取特殊元素

  1. 获取body元素
document.body;
  1. 获取html元素
document.documentElement;

<body>
    <script>
        // 1.获取body 元素
        var bodyEle = document.body;
        console.log(bodyEle);
        console.dir(bodyEle);
        // 2.获取html 元素
        // var htmlEle = document.html;
        var htmlEle = document.documentElement;
        console.log(htmlEle);
    </script>
</body>

3. 事件基础

3.1 事件概述

JavaScript 使我们有能力创建动态页面,而事件是可以被 JavaScript 侦测到的行为。

简单理解︰触发--响应机制。

网页中的每个元素都可以产生某些可以触发 JavaScript 的事件,例如,我们可以在用户点击某按钮时产生一个事件,然后去执行某些操作。

3.2 事件三要素

  • 事件源:事件被触发的对象(按钮)。
  • 事件类型:如何触发、什么事件(点击按钮)。
  • 事件处理程序:可通过一个函数赋值的方式实现。
<body>
    <button id="btn">唐伯虎</button>
    <script>
        // 点击一个按钮,弹出对话框
        var btn = document.getElementById('btn');
        btn.onclick = function() {
            alert('点秋香');
        }
    </script>
</body>

3.3 执行事件的步骤

  1. 获取事件源
  2. 注册事件(绑定事件)
  3. 添加事件处理程序(函数赋值)
<body>
    <div>123</div>
    <script>
        // 执行事件步骤
        // 点击div 控制台输出 我被选中了
        // 1. 获取事件源
        var div = document.querySelector('div');
        // 2.绑定事件 注册事件
        // div.onclick 
        // 3.添加事件处理程序 
        div.onclick = function() {
            console.log('我被选中了');

        }
    </script>
</body>

3.4 常见鼠标事件

鼠标事件触发条件
onclick鼠标点击左键触发
onmouseover鼠标经过触发
onmouseout鼠标离开触发
onfocus获得鼠标焦点触发
onblur失去鼠标焦点触发
onmousemove鼠标移动触发
onmouseup鼠标弹起触发
onmousedown鼠标按下触发

4. 操作元素

JavaScript 的 DOM 操作可以改变网页内容、结构和样式,我们可以利用 DOM 操作元素来改变元素里面的内容、属性等。

注意以下都是属性:

4.1 改变元素的内容

element.innerText element是对应的元素

从起始位置到终止位置的内容,但它去除html标签,同时空格和换行也会去掉

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        div,
        p {
            width: 300px;
            height: 30px;
            line-height: 30px;
            color: #fff;
            background-color: pink;
        }
    </style>
</head>

<body>
    <button>显示当前系统时间</button>
    <div>某个时间</div>
    <p>1123</p>  
    <script>
        // 当我们点击了按钮,  div里面的文字会发生变化
        // 1. 获取元素 
        var btn = document.querySelector('button');
        var div = document.querySelector('div');
        // 2.注册事件
        btn.onclick = function() { // 点击后显示现在时间
            div.innerHTML = getDate();
        }

        function getDate() {
            var date = new Date();
            // 自动生成时间
            var year = date.getFullYear();
            var month = date.getMonth() + 1;
            var dates = date.getDate();
            var arr = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
            var day = date.getDay();
            return '今天是:' + year + '年' + month + '月' + dates + '日 ' + arr[day];
        }
        // 我们元素可以不用添加事件 直接显示现在时间
        var p = document.querySelector('p'); 
        p.innerHTML = getDate();
    </script>
</body>

</html>

element.innerHTML 使用最多 W3C标准

起始位置到终止位置的全部内容,包括html标签,同时保留空格和换行

两者的区别:

  • innerText不识别html标签,innerHTML 识别HTML标签
  • innerText非标准,innerHTML符合 W3C 标准
  • innerText去除空格和换行,innerHTML保留空格和换行
<body>
    <div></div>
    <p>
        我是文字
        <span>123</span>
    </p>
    <script>
        // innerText 和 innerHTML的区别 
        // 1. innerText 不识别html标签 非标准  去除空格和换行
        var div = document.querySelector('div');
        // div.innerText = '<strong>今天是:</strong> 2022';
        // 2. innerHTML 识别html标签 W3C标准 保留空格和换行的
        div.innerHTML = '<strong>今天是:</strong> 2022';
        // 这两个属性是可读写的  可以获取元素里面的内容
        var p = document.querySelector('p');
        console.log(p.innerText);
        console.log(p.innerHTML);
    </script>
</body>

4.2 常用元素的属性操作

  1. innerTextinnerHTML 改变元素内容
  2. srchref
  3. idalttitle

示例:

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        img {
            width: 300px;
        }
    </style>
</head>

<body>
    <button id="ldh">刘德华</button>
    <button id="zxy">张学友</button> <br>
    <img src="images/ldh.jpg" alt="" title="刘德华">

    <script>
        // 修改元素属性  src
        // 1. 获取元素
        var ldh = document.getElementById('ldh');
        var zxy = document.getElementById('zxy');
        var img = document.querySelector('img');
        // 2. 注册事件  处理程序
        zxy.onclick = function() {
            img.src = 'images/zxy.jpg';
            img.title = '张学友';
        }
        ldh.onclick = function() {
            img.src = 'images/ldh.jpg';
            img.title = '刘德华';
        }
    </script>
</body>

</html>

案例:分时显示不同图片,显示不同问候语

思路:

  1. 根据系统不同时间来判断,所以需要用到日期内置对象
  2. 利用多分支语句来设置不同的图片
  3. 需要一个图片,并且根据时间修改图片,就需要用到操作元素src属性
  4. 需要一个div元素,显示不同问候语,修改元素内容即可
<body>
    <img src="images/s.gif" alt="">
    <div>上午好</div>
    <script>
        根据系统不同时间来判断,所以需要用到日期内置对象
        利用多分支语句来设置不同的图片
        需要一个图片,并且根据时间修改图片,就需要用到操作元素src属性
        需要一个div元素,显示不同问候语,修改元素内容即可
        // 1.获取元素
        var img = document.querySelector('img');
        var div = document.querySelector('div');
        // 2. 得到当前的小时数
        var date = new Date();
        var h = date.getHours();
        // 3. 判断小时数改变图片和文字信息
        if (h < 12) {
            img.src = 'images/s.gif';
            div.innerHTML = '上午好';
        } else if (h < 18) {
            img.src = 'images/x.gif';
            div.innerHTML = '下午好';
        } else {
            img.src = 'images/w.gif';
            div.innerHTML = '晚上好';

        }
    </script>
</body>

4.3 表单元素的属性操作

利用 DOM 可以操作如下表单元素的属性: type、 value、 checked、 selected、 disabled

示例:

<body>
    <button>按钮</button>
    <input type="text" value="输入内容">
    <script>
        // 1. 获取元素
        var btn = document.querySelector('button');
        var input = document.querySelector('input');
        // 2. 注册事件 处理程序
        btn.onclick = function() {
            // input.innerHTML = '点击了';  这个是 普通盒子 比如 div 标签里面的内容
            // 表单里面的值 文字内容是通过 value 来修改的
            input.value = '被点击了';
            // 如果想要某个表单被禁用 不能再点击 disabled  我们想要这个按钮 button禁用
            // btn.disabled = true;
            this.disabled = true;
            // this 指向的是事件函数的调用者 btn
        }
    </script>
</body>

案例:仿京东显示隐藏密码

  1. 核心思路:点击眼睛按钮,把密码框类型改为文本框就可以看见里面的密码
  2. 一个按钮两个状态,点击一次,切换为文本框,继续点击一次切换为密码框
  3. 算法:利用一个 flag 变量,来判断 flag 的值,如果是1就切换为文本框,flag 设置为0,如果是0 就切换为密码框,flag 设置为1
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .box {
            position: relative;
            width: 400px;
            border-bottom: 1px solid #ccc;
            margin: 100px auto;
        }
        
        .box input {
            width: 370px;
            height: 30px;
            border: 0;
            outline: none;
        }
        
        .box img {
            position: absolute;
            top: 2px;
            right: 2px;
            width: 24px;
        }
        /* 因为密码框自带小眼睛,所以取消小眼睛 */
        input[type="password"]::-ms-reveal {
            display: none;
        }
    </style>
</head>

<body>
    <div class="box">
        <label for="">
            <img src="images/close.png" alt="" id="eye">
        </label>
        <input type="password" name="" id="pwd">
    </div>
    <script>
        // 1. 获取元素
        var eye = document.getElementById('eye');
        var pwd = document.getElementById('pwd');
        // 2. 注册事件 处理程序
        var flag = 0;
        eye.onclick = function() {
            // 点击一次之后, flag 一定要变化
            if (flag == 0) {
                pwd.type = 'text';
                eye.src = 'images/open.png';
                flag = 1; // 赋值操作
            } else {
                pwd.type = 'password';
                eye.src = 'images/close.png';
                flag = 0;
            }

        }
    </script>
</body>

</html>

4.4 样式属性操作

我们可以通过 JS 修改元素的大小、颜色、位置等样式

  1. 行内样式操作 element.style
  2. 类名样式操作 element.className

注意:

  1. JS 里面的样式采取驼峰命名法 比如 fontSizebackgroundColor
  2. JS 修改 style 样式操作,产生的是行内样式,CSS 权重比较高

element.style 的使用案例:

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

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        div {
            width: 200px;
            height: 200px;
            background-color: #ccc;
        }
    </style>
</head>

<body>
    <div></div>
    <script>
        // 1. 获取元素
        var div = document.querySelector('div');
        // 2. 注册事件 处理程序
        var flag = 0;
        div.onclick = function () {
            // div.style里面的属性 采取驼峰命名法 
            if (flag == 0) {
                this.style.backgroundColor = '#949494';
                this.style.width = '250px';

                flag = 1;
            } else {
                this.style.backgroundColor = '#ccc';
                this.style.width = '200px';
                flag = 0;
            }
        }
    </script>
</body>

</html>

注意:

  1. 如果样式修改较多,可以采用操作类名方式更改元素样式
  2. class 因为是个保留字,因此使用className来操作元素类名属性
  3. className会直接更改元素的类名,会覆盖原先的类名 element.className 的使用案例:
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        div {
            width: 100px;
            height: 100px;
            background-color: pink;
        }
        
        .change {
            background-color: purple;
            color: #fff;
            font-size: 25px;
            margin-top: 100px;
        }
    </style>
</head>


<body>
    <div class="first">文本</div>
    <script>
        // 1. 使用 element.style 获得修改元素样式  如果样式比较少 或者 功能简单的情况下使用
        var test = document.querySelector('div');
        test.onclick = function() {
            // this.style.backgroundColor = 'purple';
            // this.style.color = '#fff';
            // this.style.fontSize = '25px';
            // this.style.marginTop = '100px';
            // 让我们当前元素的类名改为了 change

            // 2. 我们可以通过 修改元素的className更改元素的样式 适合于样式较多或者功能复杂的情况
            // 3. 如果想要保留原先的类名,我们可以这么做 多类名选择器
            // this.className = 'change';
            this.className = 'first change';
        }
    </script>
</body>

</html>

4.5 案例

案例:仿淘宝关闭二维码

  1. 核心思路:利用样式的显示和隐藏完成,display: none; 隐藏元素,display: block显示元素(display: none;隐藏后不占位,visibility: hidden;占位)
  2. 点击按钮,就让这个二维码盒子隐藏起来即可

示例:

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .box {
            position: relative;
            width: 74px;
            height: 88px;
            border: 1px solid #ccc;
            margin: 100px auto;
            font-size: 12px;
            text-align: center;
            color: #f40;
            /* display: block; */
        }
        
        .box img {
            width: 60px;
            margin-top: 5px;
        }
        
        .close-btn {
            position: absolute;
            top: -1px;
            left: -16px;
            width: 14px;
            height: 14px;
            border: 1px solid #ccc;
            line-height: 14px;
            font-family: Arial, Helvetica, sans-serif;
            cursor: pointer;
        }
    </style>
</head>

<body>
    <div class="box">
        淘宝二维码
        <img src="images/tao.png" alt="">
        <i class="close-btn">×</i>
    </div>
    <script>
        // 1. 获取元素 
        var btn = document.querySelector('.close-btn');
        var box = document.querySelector('.box');
        // 2.注册事件 程序处理
        btn.onclick = function() {
            // 这里不能用 this this 指定的是事件函数的调用者
            // 在这里我们关闭的不是 btn 这个按钮 而是这个 div
            box.style.display = 'none';
        }
    </script>
</body>

</html>

案例:循环精灵图背景

可以利用 for 循环设置一组元素的精灵图背景

  1. 首先 精灵图的图片排列是有规律的
  2. 核心思路:利用 for 循环,修改精灵图图片的背景位置 backgroundPosition
  3. 数学功底😜
  4. 本题规律:让循环里面的 i 索引号 * 44 就是每个图片的 y 坐标
    <script>
        // 1. 获取元素 所有的小li 
        var lis = document.querySelectorAll('li');
        for (var i = 0; i < lis.length; i++) {
            // 让索引号 乘以 44 就是每个li 的背景y坐标  index就是我们的y坐标
            var index = i * 44;
            // 因为图标是网上走的 所以index 是负值
            lis[i].style.backgroundPosition = '0 -' + index + 'px';
        }
    </script>

案例:显示隐藏文本框内容

案例分析:

  1. 2个新事件 获得焦点事件 onfocus 失去焦点事件onblur
  2. 如果获得焦点,判断表单里面的文字是否为默认文字,如果是默认文字,就清空表单内容
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        input {
            color: #999;
        }
    </style>
</head>

<body>
    <input type="text" value="手机">
    <script>
        // 1.获取元素
        var text = document.querySelector('input');
        // 2.注册事件 获得焦点事件 onfocus 
        text.onfocus = function() {
                // console.log('得到了焦点');
                if (this.value === '手机') {
                    this.value = '';
                }
                // 获得焦点需要把文本框里面的文字颜色变黑
                this.style.color = '#333';
            }
            // 3. 注册事件 失去焦点事件 onblur
        text.onblur = function() {
            // console.log('失去了焦点');
            if (this.value === '') {
                this.value = '手机';
            }
            // 失去焦点需要把文本框里面的文字颜色变浅色
            this.style.color = '#999';
        }
    </script>

</body>

</html>

案例:密码框格式提示错误信息

如果用户离开密码框,里面输入个数不是6~16,则提示错误信息,否则提示输入正确信息

案例分析:

  1. 首先判断的事件是表单失去焦点 onblur
  2. 如果输入正确则提示正确的信息颜色为绿色的小图标
  3. 如果输入的不是6~16位,则提示错误信息颜色为红色 小图标变化
  4. 因为里面变化样式较多,我们采取className修改样式
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        div {
            width: 600px;
            margin: 100px auto;
        }
        
        .message {
            display: inline-block;
            font-size: 12px;
            color: #999;
            background: url(images/mess.png) no-repeat left center;
            padding-left: 20px;
        }
        
        .wrong {
            color: red;
            background-image: url(images/wrong.png);
        }
        
        .right {
            color: green;
            background-image: url(images/right.png);
        }
    </style>
</head>

<body>
    <div class="register">
        <input type="password" class="ipt">
        <p class="message">请输入6~16位密码</p>
    </div>
    <script>
        // 首先判断的事件是表单失去焦点 onblur
        // 如果输入正确则提示正确的信息颜色为绿色小图标变化
        // 如果输入不是6到16位,则提示错误信息颜色为红色 小图标变化
        // 因为里面变化样式较多,我们采取className修改样式
        // 1.获取元素
        var ipt = document.querySelector('.ipt');
        var message = document.querySelector('.message');
        //2. 注册事件 失去焦点
        ipt.onblur = function() {
            // 根据表单里面值的长度 ipt.value.length
            if (this.value.length < 6 || this.value.length > 16) {
                // console.log('错误');
                message.className = 'message wrong';
                message.innerHTML = '您输入的位数不对要求6~16位';
            } else {
                message.className = 'message right';
                message.innerHTML = '您输入的正确';
            }
        }
    </script>
</body>

</html>

4.6 操作元素总结

操作元素是 DOM 核心内容 在这里插入图片描述

4.7 排他思想

如果有同一组元素,我们想要某一个元素实现某种样式,需要用到循环的排他思想算法:

  1. 所有元素全部清除样式(干掉其他人)
  2. 给当前元素设置样式(留下我自己)
  3. 注意顺序不能颠倒,首先干掉其他人,再设置自己。
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <button>按钮1</button>
    <button>按钮2</button>
    <button>按钮3</button>
    <button>按钮4</button>
    <button>按钮5</button>
    <script>
        // 1. 获取所有按钮元素
        var btns = document.getElementsByTagName('button'); // 兼容
        // btns得到的是伪数组  里面的每一个元素 btns[i]
        for (var i = 0; i < btns.length; i++) {
            // 通过循环给每一个按钮设置 onclick 事件
            btns[i].onclick = function() {
                // (1) 我们先把所有的按钮背景颜色去掉  干掉所有人
                for (var i = 0; i < btns.length; i++) {
                    btns[i].style.backgroundColor = '';
                }
                // (2) 然后才让当前的元素背景颜色为pink 留下我自己
                this.style.backgroundColor = 'pink';

            }
        }
        //2. 首先先排除其他人,然后才设置自己的样式 这种排除其他人的思想我们成为排他思想
    </script>
</body>

</html>

根据这个算法可以用来实现鼠标经过每一行时背景变色。

案例:百度换肤效果

案例分析:

  1. 这个案例练习的是给一组元素注册事件
  2. 给 4 个小图片利用循环注册点击事件
  3. 当我们点击了这个图片,让我们页面背景改为当前的图片
  4. 核心算法:把当前图片的 src 路径取过来,给 body 作为背景即可
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
        
        body {
            background: url(images/1.jpg) no-repeat center top;
        }
        
        li {
            list-style: none;
        }
        
        .baidu {
            overflow: hidden;
            margin: 100px auto;
            background-color: #fff;
            width: 410px;
            padding-top: 3px;
        }
        
        .baidu li {
            float: left;
            margin: 0 1px;
            cursor: pointer;
        }
        
        .baidu img {
            width: 100px;
        }
    </style>
</head>

<body>
    <ul class="baidu">
        <li><img src="images/1.jpg"></li>
        <li><img src="images/2.jpg"></li>
        <li><img src="images/3.jpg"></li>
        <li><img src="images/4.jpg"></li>
    </ul>
    <script>
        // 1. 获取元素 
        var imgs = document.querySelector('.baidu').querySelectorAll('img');
        // console.log(imgs);
        // 2. 循环注册事件 
        for (var i = 0; i < imgs.length; i++) {
            imgs[i].onclick = function() {
                // this.src 就是我们点击图片的路径   images/2.jpg
                // console.log(this.src);
                // 把这个路径 this.src 给body 就可以了
                document.body.style.backgroundImage = 'url(' + this.src + ')';
            }
        }
    </script>
</body>

</html>

案例:表格隔行变色

案例分析:

  1. 用到新的鼠标事件 鼠标经过 onmouseover 鼠标离开 onmouseout
  2. 核心思路:鼠标经过 tr 行,当前的行变背景颜色,鼠标离开去掉当前的背景颜色
  3. 注意:第一行(thead里面的行)不需要变换颜色,因此我们获取的是 tbody 里面的行
    <script>
        // 1.获取元素 获取的是 tbody 里面所有的行
        var trs = document.querySelector('tbody').querySelectorAll('tr');
        // 2. 利用循环绑定注册事件
        for (var i = 0; i < trs.length; i++) {
            // 3. 鼠标经过事件 onmouseover
            trs[i].onmouseover = function() {
                    // console.log(11);
                    this.className = 'bg';
                }
                // 4. 鼠标离开事件 onmouseout
            trs[i].onmouseout = function() {
                this.className = '';
            }
        }
    </script>

案例:表单全选取消全选案例

业务需求:

  1. 点击上面全选复选框,下面所有的复选框都选中(全选)
  2. 再次带年纪全选复选框,下面所有的复选框都不中选(取消全选)
  3. 如果下面复选框全部选中,上面全选按钮就自动选中
  4. 如果下面复选框有一个没有选中,上面全选按钮就不选中
  5. 所有复选框一开始默认都没选中状态

案例分析:

  1. 全选和取消全选做法:让下面所有复选框checked属性(选中状态)跟随 全选按钮即可
  2. 下面复选框需要全部选中,上面全选才能选中做法:给下面所有复选框绑定点击事件,每次点击,都要循环查看下面所有的复选框是否有没选中的,如果有一个没选中的,上面全选就不选中
  3. 可以设置一个变量,来控制全选是否选中
    <script>
        // 1. 全选和取消全选做法:  让下面所有复选框的checked属性(选中状态) 跟随 全选按钮即可
        // 获取元素
        var j_cbAll = document.getElementById('j_cbAll'); // 全选按钮
        var j_tbs = document.getElementById('j_tb').getElementsByTagName('input'); // 下面所有的复选框
        // 注册事件
        j_cbAll.onclick = function() {
                // this.checked 它可以得到当前复选框的选中状态如果是true 就是选中,如果是false 就是未选中
                console.log(this.checked);
                for (var i = 0; i < j_tbs.length; i++) {
                    j_tbs[i].checked = this.checked;
                }
            }
            // 2. 下面复选框需要全部选中, 上面全选才能选中做法: 给下面所有复选框绑定点击事件,每次点击,都要循环查看下面所有的复选框是否有没选中的,如果有一个没选中的, 上面全选就不选中。
        for (var i = 0; i < j_tbs.length; i++) {
            j_tbs[i].onclick = function() {
                // flag 控制全选按钮是否选中
                var flag = true;
                // 每次点击下面的复选框都要循环检查这4个小按钮是否全被选中
                for (var i = 0; i < j_tbs.length; i++) {
                    if (!j_tbs[i].checked) {
                        flag = false;
                        break; // 退出for循环 这样可以提高执行效率 因为只要有一个没有选中,剩下的就无需循环判断了
                    }
                }
                j_cbAll.checked = flag;
            }
        }
    </script>

4.8 自定义属性的操作

4.8.1 获取属性值

  • element.属性 获取属性值
  • element.getAttribute('属性) get 得到 attribute 属性

区别:

  • element.属性 获取内置属性值(元素本身自带的属性)
  • element.getAttribute('属性') 主要获得自定义的属性(标准)我们程序员自定义的属性

4.8.2 设置属性值

  • element.属性 = '值' 设置内置属性值
  • element.setAtteibute('属性','值');

区别:

  • element.属性 设置内置属性值
  • element.setAttribute('属性') 主要设置自定义的属性(标准)

4.8.3 移除属性

  • element.removeAttribute('属性');
<body>
    <div id="demo" index="1" class="nav"></div>
    <script>
        var div = document.querySelector('div');
        // 1. 获取元素的属性值
        // (1) element.属性
        console.log(div.id);
        //(2) element.getAttribute('属性')  get得到获取 attribute 属性的意思 我们程序员自己添加的属性我们称为自定义属性 index
        console.log(div.getAttribute('id'));
        console.log(div.getAttribute('index'));
        // 2. 设置元素属性值
        // (1) element.属性= '值'
        div.id = 'test';
        div.className = 'navs';
        // (2) element.setAttribute('属性', '值');  主要针对于自定义属性
        div.setAttribute('index', 2);
        div.setAttribute('class', 'footer'); // class 特殊  这里面写的就是class 不是className
        // 3 移除属性 removeAttribute(属性)    
        div.removeAttribute('index');
    </script>
</body>

案例:tab 栏切换(重点案例)

案例分析:

  1. Tab 栏切换有2个大的模块
  2. 上面的模块选项卡,点击某一个,当前这一个底色会是红色,其余不变(派他思想)修改类名的方式
  3. 下面的模块内容,会跟随上面的选项卡变化。所以下面模块变化写到点击事件里面
  4. 规律:下面的模块显示内容和上面的选项卡一一对应,相匹配
  5. 核心思路:给上面的 tab_list 里面的所有小 li 添加自定义属性,属性值从0开始编号
  6. 当我们点击 tab_list 里面的某个小 li ,让 tab_con 里面对应序号的内容显示,其余隐藏(排他思想)
    <script>
        // 获取元素
        var tab_list = document.querySelector('.tab_list');
        var lis = tab_list.querySelectorAll('li');
        var items = document.querySelectorAll('.item');
        // for循环绑定点击事件
        for (var i = 0; i < lis.length; i++) {
            // 开始给5个小li 设置索引号 
            lis[i].setAttribute('index', i);
            lis[i].onclick = function() {
                // 1. 上的模块选项卡,点击某一个,当前这一个底色会是红色,其余不变(排他思想) 修改类名的方式

                // 干掉所有人 其余的li清除 class 这个类
                for (var i = 0; i < lis.length; i++) {
                    lis[i].className = '';
                }
                // 留下我自己 
                this.className = 'current';
                // 2. 下面的显示内容模块
                var index = this.getAttribute('index');
                console.log(index);
                // 干掉所有人 让其余的item 这些div 隐藏
                for (var i = 0; i < items.length; i++) {
                    items[i].style.display = 'none';
                }
                // 留下我自己 让对应的item 显示出来
                items[index].style.display = 'block';
            }
        }
    </script>

4.9 H5 自定义属性

自定义属性目的:是为了把偶才能并使用数据。有些数据可以保存到页面中而不用保存到数据库中。

自定义属性获取是通过 getAttribute('属性') 获取

但是有些自定义属性很容易引起歧义,不容易判断是元素的内置属性还是自定义属性。

H5 给我们新增了自定义属性:

4.9.1 设置 H5 自定义属性

H5 规定自定义属性data- 开头做为属性名并且赋值

比如<div data-index = "1"></div>

或者使用 JS 设置

element.setAttribute( 'data-index' , 2)

4.9.2 获取 H5 自定义属性

  1. 兼容性获取 element.getAttribute( 'data-index' );
  2. H5 新增 element.dataset.index 或者 element.dataset[ 'index' ] ie 11才开始支持 在这里插入图片描述

5. 节点操作

5.1 为什么学节点操作

获取元素通常使用的两种方式:

1. 利用 DOM 提供的方法获取元素

  • document,getElementById()
  • document,getElementsByTagName()
  • document.querySelector
  • 逻辑性不强、繁琐

2. 利用节点层级关系获取元素

  • 利用父子兄节点关系获取元素
  • 逻辑性强,但是兼容性较差

这两种方式都可以获取元素节点,我们后面都会用,但是节点操作更简单

5.2 节点概述

网页中的所有内容都是节点(标签、属性、文本、注释等),在DOM中,节点使用 node 来表示。

HTML DOM 树中的所有节点均可通过 JavaScript 进行访问,所有 HTML 元素(节点)均可被修改,也可以创建或删除。

一般地,节点至少拥有 nodeType (节点类型)、nodeName (节点名称)和 nodeValue (节点值)这三个基本属性。

三种基本节点类型:

  • 元素节点 nodeType 为 1
  • 属性节点 nodeType 为 2
  • 文本节点 nodeType 为 3(文字、空格、换行)

实际开发中,主要操作的还是元素节点

5.3 节点层级

利用 DOM 树可以把节点划分为不同的层级关系,常见的是父子兄层级关系

5.3.1 父级节点

node.parentNode

获取离元素最近的父节点,若找不到则返回 null

5.3.2 子节点

parentNode.childNodes 返回包含指定节点的子节点的集合,该集合为即时更新的集合。

注意:parentNode.childNodes 包含了所有的子节点,包括元素节点,文本节点等。 如果只想要获得里面的元素节点,则需要专门处理。所以我们一般不提倡使用 childNodes

// 筛选元素节点
var ul = document.querySelector('ul');
for (var i = 0; i < ul.childNodes.length; i++) {
            if (ul.childNodes[i].nodeType == 1) {
            // ul.childNodes[i]是元素节点
                console.log(ul.childNodes[i]);
            }
        }

更加好的方法:获取子元素节点

parentNode.children  (非标准)

parentNode.children是一个只读属性,返回所有的子元素节点。它只返回子元素节点,其余节点不反悔(重点掌握

虽然 children 是一个非标准,但是得到了各个浏览器的支持,因此我们可以放心使用

子节点方法:

① 获取 所有结点 中的第一个和最后一个:(包括文本节点)

  • parentNode.firstChild
  • parentNode.lastChild

② 获取 元素节点 中第一个和最后一个

注意:此方法具有兼容性问题 IE9 以上才支持

  • parentNode.firstElementChild
  • parentNode.lastElementChild

③ 实际开发:

  • parentNode.children[0] 获取第一个
  • parentNode.children[parentNode.children.length -1] 获取最后一个

案例:下拉菜单

    <script>
        // 1. 获取元素
        var nav = document.querySelector('.nav');
        var lis = nav.children; // 得到4个小li
        // 2.循环注册事件
        for (var i = 0; i < lis.length; i++) {
            lis[i].onmouseover = function() {
                this.children[1].style.display = 'block';
            }
            lis[i].onmouseout = function() {
                this.children[1].style.display = 'none';
            }
        }
    </script>

5.3.3 兄弟节点

  1. 返回下一个兄弟节点 node.nextSibling

  2. 返回上一个的兄弟节点: node.previousSibling

nextSibling / previousSibling 返回当前元素的下/上一个兄弟节点,找不到则返回 null。这个兄弟节点可能是是所有的节点之一(即元素节点、文本节点等)

  1. 返回下一个 兄弟元素节点(≥IE9) node.nextElementSibling

  2. 返回上一个 兄弟元素节点(≥IE9) node.previousElementSibling

nextElementSibling / previousElementSibling 返回当前元素下/上一个兄弟元素节点,找不到则返回 null。有兼容性问题,IE9以上支持。

解决方法:封装一个函数。

// 处理 <IE9 兼容性问题
function getNextElementSibling(node) {
    var el = node;
    while (el = el.nextSibling) {
        if (el.nodeType === 1) {
            return el;
        }
    }
    return null;
}

5.3.4 创建并添加节点

document.createElement('tagName')

document.createElement(' ')方法创建由 tagName 指定的HTML元素。因为这些元素原先不存在,是根据我们的需求动态生成的,所以我们也称为动态创建元素节点

在创建了元素节点后,还需要将节点添加到页面中。

parentnode.appendChild(child)

node.appendChild() 方法可以给元素节点添加子元素节点,若某元素已存在则重复添加,在页面已存在的元素 后面追加新节点

node.insertBefore(child, 指定元素)

也可以使用 insertBefore() 方法在指定元素的前面插入节点。

在一个页面中要添加元素节点,先创建节点,然后添加节点

<body>
    <ul>
        <li>123</li>
    </ul>
    <script>
        // 1. 创建节点元素节点
        var li = document.createElement('li');
        // 2. 添加节点 node.appendChild(child)  node 父级  child 是子级 
        // 后面追加元素  类似于数组中的push
        var ul = document.querySelector('ul');
        ul.appendChild(li);
        // 3. 添加节点 node.insertBefore(child, 指定元素);
        var lili = document.createElement('li');
        ul.insertBefore(lili, ul.children[0]);
        // 4. 我们想要页面添加一个新的元素 : 1. 创建元素 2. 添加元素
    </script>
</body>

案例:简单版发布留言案例

案例分析:

  1. 核心思路:点击按钮之后,就动态创建一个 li ,添加到 ul 里面
  2. 创建 li 的同时,把文本域里面的值通过 li.innerHTML赋值给 li
  3. 如果想要新的留言后面显示就用 appendChild 如果想要前面显示就用 insertBefore
<body>
    <textarea name="" id=""></textarea>
    <button>发布</button>
    <ul>

    </ul>
    <script>
        // 1. 获取元素
        var btn = document.querySelector('button');
        var text = document.querySelector('textarea');
        var ul = document.querySelector('ul');
        // 2. 注册事件
        btn.onclick = function() {
            if (text.value == '') {
                alert('您没有输入内容');
                return false;
            } else {
                // console.log(text.value);
                // (1) 创建元素
                var li = document.createElement('li');
                // 先有li 才能赋值
                li.innerHTML = text.value;
                // (2) 添加元素
                // ul.appendChild(li);
                ul.insertBefore(li, ul.children[0]);
                text.value = '';  //发布后清空留言板
            }
        }
    </script>
</body>

5.3.5 删除节点

node.removeChild(child)

DOM 提供了 removeChild(childNode) 方法,删除一个子节点,并返回删除的节点。其中 childNode 为待删除的子节点。

案例:删除留言案例

案例分析:

  1. 当我们把文本域里面的值赋值给 li 的时候,多添加一个删除的链接
  2. 需要把所有的链接获取过来,当我们点击当前的链接的时候,删除当前连接所在的 li
  3. 阻止连接跳转需要添加 javascript: void(0);或者 javascript:;
    <script>
        // 1. 获取元素
        var btn = document.querySelector('button');
        var text = document.querySelector('textarea');
        var ul = document.querySelector('ul');
        // 2. 注册事件
        btn.onclick = function() {
            if (text.value == '') {
                alert('您没有输入内容');
                return false;
            } else {
                // console.log(text.value);
                // (1) 创建元素
                var li = document.createElement('li');
                // 先有li 才能赋值
                li.innerHTML = text.value + "<a href='javascript:;'>删除</a>";
                // (2) 添加元素
                // ul.appendChild(li);
                ul.insertBefore(li, ul.children[0]);
                // (3) 删除元素 删除的是当前链接的li  它的父亲
                var as = document.querySelectorAll('a');
                for (var i = 0; i < as.length; i++) {
                    as[i].onclick = function() {
                        // node.removeChild(child); 删除的是 li 当前a所在的li  this.parentNode;
                        ul.removeChild(this.parentNode);
                    }
                }
            }
        }
    </script>

5.3.6 复制节点(克隆节点)

node.cloneNode()

node.cloneNode()方法返回调用该方法的节点的一个副本。也称为克隆节点/拷贝节点

注意: 1.`如果括号参数为空或者为false,则是浅拷贝,即只克隆复制节点本身,不克隆里面的子节点。(只复制标签不复制内容) 2. 如果括号参数为true,则是深度拷贝,恢会复制节点本身以及里面所有的子节点。(复制标签也复制内容)

<body>
    <ul>
        <li>1111</li>
        <li>2</li>
        <li>3</li>
    </ul>
    <script>
        var ul = document.querySelector('ul');
        // 1. node.cloneNode(); 括号为空或者里面是false 浅拷贝 只复制标签不复制里面的内容
        // 2. node.cloneNode(true); 括号为true 深拷贝 复制标签复制里面的内容
        var lili = ul.children[0].cloneNode(true);
        ul.appendChild(lili);
    </script>
</body>

5.4 三种动态创建元素的区别

  • document.write()
  • element.innerHTML
  • document.createElement()

区别:

  1. document.wirte是直接将内容写入页面的内容流,但是文档流执行完毕,则它会导致页面全部重绘
  2. innerHTML 是将内容写入某个 DOM 节点,不会导致页面全部重绘
  3. innerHTML 创建多个元素效率更高(不要拼接字符串,采取数组形式拼接),结构稍微复杂
  4. createElement()创建多个元素效率低一点点,但是结构更清晰

总结:不同浏览器下,innerHTML效率要比createElement

6. DOM 重点核心

文档对象模型(Document Object Model ,简称DOM),是W3C组织推荐的处理可扩展标记语言(HTML或者XML)的标准编程接口

W3C已经定义了一系列的DOM接口,通过这些DOM接口可以改变网页的内容、结构和样式

  1. 对于JavaScript,为了能够使JavaScript操作HTML,JavaScript就有了一套自己的dom编程接口
  2. 对于HTML,dom使得html形成一棵dom树,包含 文档、元素、节点

我们获取过来的DOM元素是一个对象(object),所以称为文档对象模型

6.1 创

  1. document.write
  2. innerHTML
  3. createElement

6.2 增

  1. appendChild
  2. insertBefore

6.3 删

  1. removerChild

6.4 改

主要修改 dom 的元素属性,dom元素的内容、属性,表单的值等

  1. 修改元素属性:srchreftiele
  2. 修改普通元素内容:innerHtmlinnerText
  3. 修改表单元素:valuetypedisabled
  4. 修改元素样式:styleclassName

6.5 查

主要获取查询dom的元素

  1. DOM提供的API方法:getElementByIdgetElementsByTagName 古老用法 不太推荐
  2. H5提供的新方法:querySelectorquerySelectorAll 提倡
  3. 利用节点操作控制元素:父(parentNode) 、子(children)、兄(previousElementSiblingnextElementSibling提倡

6.6 属性操作

主要针对于自定义属性

  1. setAttribute:设置dom的属性值
  2. getAttribute:得到dom的属性值
  3. removeAttribute:移除属性

6.7 事件操作

给元素注册事件,采取 事件源.事件类型 = 事件处理程序