前端JavaScript DOM BOM 自学复盘 D3

DOM- 节点操作

1.DOM节点的基本概念

1.1.DOM节点的概念

DOM树

DOM树

DOM树里每一个内容(标签、注释、文本等)都称之为节点。

1.2.节点类型

1.元素节点

所有的标签 比如 body、 div、script、style等

html 是根节点

2.属性节点

所有的属性 比如 链接路径href、类(class)、id

3.文本节点

所有的文本,如 标签< button >按钮3< /button >里面的‘按钮3’就是文本节点

4. 其他

重点记住元素节点

2.查找节点

节点关系:

父节点

子节点

兄弟节点

2.1.父节点查找:

parentNode 属性:返回最近一级的父节点 ,找不到返回为null

2.1.1.父节点查找语法:

子元素.parentNode

例:通过节点查找,父级div.father将本身和子级div.son隐藏

父级和子级html

 <div class="father">
        我爱罗
        <div class="son">Tom</div>
    </div>

思路:

1.获取子级标签div.son;
2.查找结点,父级div.father将本身和子级div.son隐藏

代码:

<!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>
        /* .father{
            display:  none;
        } */
    </style>
</head>
<body>
    <div class="father">
        我爱罗
        <div class="son">Tom</div>
    </div>
    <script>
        // 父节点查找语法:
        //子元素.parentNode
        //高级父级查找语法:
        //注意:parentNode只能查找最近一级的父级节点,即当前子节点的直接父级节点
        //更高级别的父级需要编写多个parentNode
        //爷爷节点查找语法:
        //  子元素.parentNode.parentNode
        //例:通过节点查找,父级div.father将本身和子级div.son隐藏
        //1.获取子级标签div.son
        let son = document.querySelector('.son')
        //测试:找爸爸,观察是否获取到直接父级
        // console.log(son.parentNode)
        //2.查找节点,父级div.father将本身和子级div.son隐藏
        son.parentNode.style.display = 'none'

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

运行结果:
运行前:
在这里插入图片描述
运行后:
在这里插入图片描述
因此这个运行结果相当于css中的给父级div.father添加display:none样式

.father{
            display:  none;
        }

2.1.2.高级父级查找语法:

注意:parentNode只能查找最近一级的父级节点,即当前子节点的直接父级节点,更高级别的父级需要编写多个parentNode

爷爷节点查找语法:

   子元素.parentNode.parentNode

2.1.3.关闭二维码案例

需求:关闭二维码案例
在这里插入图片描述
素材

<!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>
        * {
            margin: 0;
            padding: 0;
        }

        .erweima {
            position: relative;
            width: 160px;
            height: 160px;
            margin: 100px auto;
            border: 1px solid #ccc;
        }

        .erweima i {
            position: absolute;
            left: -13px;
            top: 0;
            width: 10px;
            height: 10px;
            border: 1px solid #ccc;
            font-size: 12px;
            line-height: 10px;
            color: #ccc;
            font-style: normal;
            cursor: pointer;
        }
    </style>
</head>

<body>
    <div class="erweima">
        <img src="./images/code.png" alt="">
        <i class="close_btn">x</i>
    </div>
    <script>
      
    </script>
</body>

</html>

HTML+CSS素材思路分析:
在这里插入图片描述
通过子绝父相定位,将关闭按钮定位在二维码图片最左上方

JavaScript 部分

思路:

1.获取子级标签 close_btn;
2.查找父级节点,点击close_btn,,通过父级节点将父级整体(或二维码)隐藏。
       

代码:

 <script>
        //1.获取子级标签 close_btn
        let btn = document.querySelector('.close_btn')
        //2.查找父级节点,点击close_btn,,通过父级节点将父级整体(或二维码)隐藏
        btn.addEventListener('click',function(){
            btn.parentNode.style.display = 'none'
        })
    </script>

运行结果:
在这里插入图片描述

在这里插入图片描述

2.1.4.关闭多个二维码案例

素材
在这里插入图片描述

<!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>
        * {
            margin: 0;
            padding: 0;
        }

        .erweima {
            position: relative;
            width: 160px;
            height: 160px;
            margin: 100px auto;
            border: 1px solid #ccc;
        }

        .erweima i {
            position: absolute;
            left: -13px;
            top: 0;
            width: 10px;
            height: 10px;
            border: 1px solid #ccc;
            font-size: 12px;
            line-height: 10px;
            color: #ccc;
            font-style: normal;
            cursor: pointer;
        }
    </style>
</head>

<body>
    <div class="erweima">
        <img src="./images/code.png" alt="">
        <i class="close_btn">x</i>
    </div>
    <script>
        //1.获取子级标签 close_btn
        let btn = document.querySelector('.close_btn')
        //2.查找父级节点,点击close_btn,,通过父级节点将父级整体(或二维码)隐藏
        btn.addEventListener('click',function(){
            btn.parentNode.style.display = 'none'
        })
    </script>
</body>

</html>

素材分析

erweima为父级盒子,close为子级盒子,erweima设置二维码图片背景,close设置关闭按钮背景,通过子绝父相定位,来将子级盒子close定位在父级盒子erweima最右侧

在这里插入图片描述JavaScript部分

思路分析

总体思路:

查找父节点,隐藏父级
 
详细思路:

由于每个盒子样式相同且都要逐个关闭,所以采用遍历获取子级元素
 1.获取子级元素
 2.事件监听:点击close,关闭或隐藏当前的父级
 3.查找父级节点,隐藏父级,关闭二维码
       

我的代码

 //思路:
    //查找父节点,隐藏父级
    //由于每个盒子样式相同且都要逐个关闭,所以采用遍历获取子级元素
    //1.获取子级元素
    let close = document.querySelectorAll('.close')
    //2.事件监听:点击close,关闭或隐藏当前的父级
    for(let i = 0;i < close.length;i++){
      close[i].addEventListener('click',function(){
        //3.查找父级节点,隐藏父级,关闭二维码
        this.parentNode.style.display = 'none'
      })
    }

我的代码运行效果:
点击关闭按钮,从底部向上(或自底向上)关闭二维码,没有实现点击当前二维码实现关闭效果

参考代码

script>
    // 1. 获取元素  关闭按钮
    let close_btn = document.querySelectorAll('.close')
    // 2. 绑定多个点击事件给close
    for (let i = 0; i < close_btn.length; i++) {
      close_btn[i].addEventListener('click', function () {
        // 3. 关闭当前的那个二维码  点击谁,就关闭谁的爸爸
        this.parentNode.style.visibility = 'hidden'
      })
    }
  </script>

优化:
将this.parentNode.style.display = 'none’修改为this.parentNode.style.visibility = ‘hidden’,实现了期望效果

 <script>
 
    //思路:
    //查找父节点,隐藏父级
    //由于每个盒子样式相同且都要逐个关闭,所以采用遍历获取子级元素
    //1.获取子级元素
    let close = document.querySelectorAll('.close')
    //2.事件监听:点击close,关闭或隐藏当前的父级
    for(let i = 0;i < close.length;i++){
      close[i].addEventListener('click',function(){
        //3.查找父级节点,隐藏父级,关闭二维码
        // this.parentNode.style.display = 'none'
        this.parentNode.style.visibility = 'hidden'
      })
    }

  </script>

运行结果
在这里插入图片描述

拓展:visibility:hidden和display:none的区别

参考文章链接

1.Mr, Jia的csdn博客:visibility:hidden和display:none的区别
https://blog.csdn.net/SoLoJia/article/details/123515533

2.你好张淑芬的csdn博客:display:none和visibility:hidden的区别
https://blog.csdn.net/flower521528/article/details/123521504
visibility:hiddendisplay:none
(1) 作用不同(1)作用不同
可以让元素消失,属于css样式,它只是简单的让元素看不见,但本身的位置还在,如果对div进行hidden,那么div除了看不见,其他所有的样式都在让这个元素失去块元素的效果,其本身这个元素也是直接消失,会影响到布局问题。
(2)使用后HTML元素有所不同(2)使用后HTML元素有所不同
使用该属性后,HTML元素(对象)仅仅是在视觉上看不见(完全透明),而它所占据的空间位置仍然存在,也即是说它仍具有高度、宽度等属性值。使用该属性后,HTML元素(对象)的宽度、高度等各种属性值都将“丢失”。
(3)定义不同(3)定义不同
定义不同,visibility属性指定一个元素是否是可见的,display这个属性用于定义建立布局时元素生成的显示框类型。

在本例中,由于使用display:none失去块元素的效果,让这个元素直接消失,影响到布局问题,所以引起从底部向上(或自底向上)关闭二维码,没有实现点击当前二维码实现关闭效果。因此本例使visibility:hidden实现隐藏能够更好实现效果。

2.2.子节点查找:

2.2.1.childNodes

获得所有子节点、包括文本节点(空格、换行)、注释节点等

2.2.2.children (重点)

获得所有元素节点,返回的还是一个伪数组

语法:
 子节点查找语法:
  父元素.children

实例

查找ul中的子级让第一个元素的文字变绿色,再点击button,通过查找子级让父级中的所有子级li文字变红色。

html部分

<!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>
</head>
<body>
    <button>来点我啊!</button>
    <ul>
        <li>我是孩子</li>
        <li>我是孩子</li>
        <li>我是孩子</li>
        <li>我是孩子</li>
        <li>我是孩子</li>
        <li>我是孩子</li>
    </ul>
   
</body>
</html>

思路

//思路:
        1.获取button按钮和li的父级ul
        2.查找ul中的子级让第一个元素变绿色
        3.注册触发点击事件,通过查找子级让父级中的所有子级li变红色
        3.1.查找子级,让所有子级变红色
               

代码

//思路:
        //1.获取button按钮和li的父级ul
        let btn = document.querySelector('button')
        let ul = document.querySelector('ul')
        //2.查找ul中的子级让第一个元素变绿色
        ul.children[0].style.color = 'green'
        //3.注册触发点击事件,通过查找子级让父级中的所有子级li变红色,
        btn.addEventListener('click',function(){
            //观察ul的子级返回的是什么
            console.log(ul.children)
            for( let i = 0;i < ul.children.length; i++){
            	//3.1.让所有子级变红色
                ul.children[i].style.color = 'red'
            }
           
        })
        //观察使用获取ul子级childNodes效果
        // console.log(ul.childNodes)

在这里插入图片描述
在这里插入图片描述

2.3.兄弟关系查找:

2.3.1. 下一个兄弟节点

nextElementSibling 属性

2.3.2. 上一个兄弟节点

previousElementSibling 属性

实例

点击button,让ul中的three类的li标签变蓝色和它的前一个兄弟变红及后一个兄弟的li标签变天蓝色

HTML部分

<!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>
</head>
<body>
    <button>点击</button>
    <ul>
        <li>第1个</li>
        <li>第2个</li>
        <li class="three">第3个</li>
        <li>第4个</li>
        <li>第5个</li>
        <li>第6个</li>
        <li>第7个</li>
        <li>第8个</li>
    </ul>  
</body>
</html>

思路:

       1.获取button和three类标签     
       2,注册事件触发btn,让ul中的three类的li标签文字变蓝色和它的前一个兄弟文字变红及后一个兄弟的li标签文字变天蓝色;
        2.1.让ul中的three类的li标签变蓝色
        2.2.查找下一个兄弟节点,three前一个兄弟(第2个)文字变红
        2.3.查找后一个兄弟节点,three下一个兄弟的(第4个)li标签文字变天蓝色

代码:

<!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>
</head>
<body>
    <button>点击</button>
    <ul>
        <li>1</li>
        <li>2</li>
        <li class="three">3</li>
        <li>4</li>
        <li>5</li>
        <li>6</li>
        <li>7</li>
        <li>8</li>
    </ul>
    <script>
        //点击button,让ul中的three类的li标签和它的前一个兄弟变红及后一个兄弟的li标签变天蓝色
        //   1.获取button和three类标签     
        let btn = document.querySelector('button')
        let three =document.querySelector('.three')
        //2,注册事件触发btn,让ul中的three类的li标签文字变蓝色和它的前一个兄弟文字变红及后一个兄弟的li标签文字变天蓝色
        btn.addEventListener('click',function(){
            //2.1.让ul中的three类的li标签变蓝色
            three.style.color = 'blue'
            //2.2.查找下一个兄弟节点,three前一个兄弟(第2个)文字变红
            three.nextElementSibling.style.color = 'red'
            //2.3.查找后一个兄弟节点,three下一个兄弟(第4个)的li标签文字变天蓝色
            three.previousElementSibling.style.color = 'skyblue'
        })
    </script>
       
    
    
</body>
</html>

运行结果:
运行前:
在这里插入图片描述
运行后:
在这里插入图片描述

3.增加节点

很多情况下,我们需要在页面中增加元素,比如,点击发布按钮,可以新增一条信息

一般情况下,我们新增节点,按照如下操作:

(1).创建一个新的节点

(2). 把创建的新的节点放入到指定的元素内部

3.1.创建节点

即创造出一个新的网页元素,再添加到网页内,一般先创建节点,然后插入节点

创建元素节点方法:

document.createElement('要创建节点的标签名')  

3.2.追加节点

要想在界面看到,还得插入到某个父元素中

3.2.1.插入到父元素的最后一个子元素:

插入到父元素的最后一个子元素,从最后一个子元素后面开始追加新节点,(或当子元素为空时的,追加第一个元素)

语法:
父元素.appendChild(要插入的子元素)

3.2.2.插入到父元素中某个子元素的前面:

语法:
父元素.insertBefore(要追加的子元素,放到的前面哪个子元素)

注意
1.括号内写的是变量,不要写 ’ ‘,创建元素时要加入’‘,此时该元素不是变量,追加元素不要加’',此时元是变量;
2.插入到父元素中某个子元素的前面中追加第一个元素语法

父元素.insertBefore(要追加的子元素,父级.children[0])

实例1

在父级ul中创建并分别在最后一个元素后面追加类名为one以及文本内容为’我是Tuffy’的新的节点为和第二个元素前面追加类名为two,文本内容为’我是尼格买提’的新的节点

<!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>
</head>
<body>
    <ul>
        <li>我是Tom</li>
        <li>我是Jerry</li>
        <li>我是Spike</li>
    </ul>
   
</body>
</html>

思路:

1.获取父级标签,创建要追加的子级节点
2.添加新节点的类名属性和文本
3.追加节点, 在最后一个元素后面追加类名为one以及文本内容为'我是Tuffy'的新的节点
4.追加节点,第二个元素前面追加新的节点,第二个元素前面追加类名为two,文本内容为'我是尼格买提'的新的节点

代码:

<!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>
</head>
<body>
    <ul>
        <li>我是Tom</li>
        <li>我是Jerry</li>
        <li>我是Spike</li>
    </ul>
    <script>
        //   1.创建节点
        // document.createElement('要创建节点的标签名')     
        // let div = document.createElement('div')
        // //创建divclass类名属性节点
        // div.className = 'current'
        //在父级ul中创建并分别在最后一个元素后面追加类名为one以及文本内容为'我是Tuffy'的新的节点为和第二个元素前面追加类名为two,文本内容为'我是尼格买提'的新的节点
        //1.获取父级标签,创建要追加的子级节点
        let ul = document.querySelector('ul')
        let li = document.createElement('li')
        let lis = document.createElement('li')
        //2.添加新节点的类名属性和文本
        li .className = 'one'
        li.innerHTML = '我是Tuffy'
        lis.className = 'two'
        lis.innerHTML= '我是尼格买提'
        
        //3.追加节点, 在最后一个元素后面追加类名为one以及文本内容为'我是Tuffy'的新的节点
        //插入到父元素的最后一个子元素,从最后一个子元素后面开始追加新节点,(或当子元素为空时的,追加第一个元素)
        //父元素.appendChild(要插入的子元素),
        // 注意:括号内写的是变量,不要写'',创建元素时要加入'',此时该元素不是变量,追加元素不要加'',此时元素是变量
        // 如: ul.appendChild('li') 不能顺利追加li
        ul.appendChild(li)
        //4.追加节点,第二个元素前面追加新的节点,第二个元素前面追加类名为two,文本内容为'我是尼格买提'的新的节点
        //插入到父元素中某个子元素的前面
        //语法:父元素.insertBefore(要追加的子元素,放到的前面哪个子元素)
        ul.insertBefore(lis,ul.children[1])
        

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

运行结果:
在这里插入图片描述
实例2:从后面创建和追加第一个元素’我是Tuffy’的新的节点,从前面追加第一个元素’我是尼格买提’

<!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>
</head>
<body>
    <ul>
       
    </ul>
   
</body>
</html>

思路:


1.获取父级标签,创建要追加的子级节点
2.添加新节点的文本
3.追加节点, 在最后一个元素后面追加文本内容为'我是Tuffy'的新的节点
4.追加节点,当前第一个元素前面追加文本内容为'我是尼格买提'的新的节点

代码:

<!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>
</head>
<body>
    <ul>
        <!-- <li>我是Tom</li>
        <li>我是Jerry</li>
        <li>我是Spike</li> -->
    </ul>
    <script>
        
        //1.获取父级标签,创建要追加的子级节点
        let ul = document.querySelector('ul')
        let li = document.createElement('li')
        let lis = document.createElement('li')
        //2.添加新节点的类名文本
        li.innerHTML = '我是Tuffy'
        lis.innerHTML= '我是尼格买提'
        
        //3.追加节点, 在最后一个元素后面追加文本内容为'我是Tuffy'的新的节点
        //插入到父元素的最后一个子元素,从最后一个子元素后面开始追加新节点,(或当子元素为空时的,追加第一个元素)
        //父元素.appendChild(要插入的子元素),
        // 注意:括号内写的是变量,不要写'',创建元素时要加入'',此时该元素不是变量,追加元素不要加'',此时元素是变量
        // 如: ul.appendChild('li') 不能顺利追加li
        ul.appendChild(li)
        // //4.追加节点,当前第一个元素前面追加文本内容为'我是尼格买提'的新的节点
        // //插入到父元素中某个子元素的前面
        // //语法:父元素.insertBefore(要追加的子元素,放到的前面哪个子元素)
        //插入到父元素中某个子元素的前面中追加第一个元素语法:
        // 父元素.insertBefore(要追加的子元素,父级.children[0])
        // ul.insertBefore(lis,ul.children[1])
        ul.insertBefore(lis,ul.children[0])
        

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

运行结果:

将ul.insertBefore(lis,ul.children[0])注释,观察运行结果

在这里插入图片描述
将ul.insertBefore(lis,ul.children[0])取消注释,观察运行结果
在这里插入图片描述

3.2.3.综合案例-学成在线

案例渲染

需求:按照数据渲染页面
在这里插入图片描述

项目准备

在这里插入图片描述

静态页面代码

(代码已在素材中给出)

盒子模型简要分析

在这里插入图片描述

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>学成在线首页</title>
    <link rel="stylesheet" href="style.css">
    <style>

    </style>
</head>

<body>

    <!-- 4. box核心内容区域开始 -->
    <div class="box w">
        <div class="box-hd">
            <h3>精品推荐</h3>
            <a href="#">查看全部</a>
        </div>
        <div class="box-bd">
            <ul class="clearfix">
                <li>
                    <img src="images/course01.png" alt="">
                    <h4>
                        Think PHP 5.0 博客系统实战项目演练
                    </h4>
                    <div class="info">
                        <span>高级</span><span>1125</span>人在学习
                    </div>
                </li>
                <li>
                    <img src="images/course01.png" alt="">
                    <h4>
                        Think PHP 5.0 博客系统实战项目演练
                    </h4>
                    <div class="info">
                        <span>高级</span><span>1125</span>人在学习
                    </div>
                </li>
                <li>
                    <img src="images/course01.png" alt="">
                    <h4>
                        Think PHP 5.0 博客系统实战项目演练
                    </h4>
                    <div class="info">
                        <span>高级</span><span>1125</span>人在学习
                    </div>
                </li>
                <li>
                    <img src="images/course01.png" alt="">
                    <h4>
                        Think PHP 5.0 博客系统实战项目演练
                    </h4>
                    <div class="info">
                        <span>高级</span><span>1125</span>人在学习
                    </div>
                </li>
                <li>
                    <img src="images/course01.png" alt="">
                    <h4>
                        Think PHP 5.0 博客系统实战项目演练
                    </h4>
                    <div class="info">
                        <span>高级</span><span>1125</span>人在学习
                    </div>
                </li>
                <li>
                    <img src="images/course01.png" alt="">
                    <h4>
                        Think PHP 5.0 博客系统实战项目演练
                    </h4>
                    <div class="info">
                        <span>高级</span><span>1125</span>人在学习
                    </div>
                </li>
                <li>
                    <img src="images/course01.png" alt="">
                    <h4>
                        Think PHP 5.0 博客系统实战项目演练
                    </h4>
                    <div class="info">
                        <span>高级</span><span>1125</span>人在学习
                    </div>
                </li>
                <li>
                    <img src="images/course01.png" alt="">
                    <h4>
                        Think PHP 5.0 博客系统实战项目演练
                    </h4>
                    <div class="info">
                        <span>高级</span><span>1125</span>人在学习
                    </div>
                </li>


            </ul>
        </div>
    </div>

</body>

</html>
css部分
* {
    margin: 0;
    padding: 0;
}
.w {
    width: 1200px;
    margin: auto;
}
body {
    background-color: #f3f5f7;
}
li {
    list-style: none;
}
a {
    text-decoration: none;
}
.clearfix:before,.clearfix:after {
    content:"";
    display:table; 
  }
  .clearfix:after {
    clear:both;
  }
  .clearfix {
     *zoom:1;
  }   
 
.header {
    height: 42px;
    /* background-color: pink; */
    /* 注意此地方会层叠 w 里面的margin */
    margin: 30px auto;
}
.logo {
    float: left;
    width: 198px;
    height: 42px;
}
.nav {
    float: left;
    margin-left: 60px;
}
.nav ul li {
    float: left;
    margin: 0 15px;
}
.nav ul li a {
    display: block;
    height: 42px;
    padding: 0 10px;
    line-height: 42px;
    font-size: 18px;
    color: #050505;   
}
.nav ul li a:hover {
    border-bottom: 2px solid #00a4ff;
    color: #00a4ff;
}
/* search搜索模块 */
.search {
    float: left;
    width: 412px;
    height: 42px;
    margin-left: 70px;
}
.search input {
    float: left;
    width: 345px;
    height: 40px;
    border: 1px solid #00a4ff;
    border-right: 0;
    color: #bfbfbf;
    font-size: 14px;
    padding-left: 15px;
}
.search button {
    float: left;
    width: 50px;
    height: 42px;
    /* 按钮button默认有个边框需要我们手动去掉 */
    border: 0;
    background: url(images/btn.png);
}
.user {
    float: right;
    line-height: 42px;
    margin-right: 30px;
    font-size: 14px;
    color: #666;
}
/* banner区域 */
.banner {
    height: 421px;
    background-color: #1c036c;
}
.banner .w {
    height: 421px;
    background: url(images/banner2.png) no-repeat top center;
}
.subnav {
    float: left;
    width: 190px;
    height: 421px;
    background: rgba(0,0,0, 0.3);
}
.subnav ul li {
    height: 45px;
    line-height: 45px;
    padding: 0 20px;
}
.subnav ul li a {
    font-size: 14px;
    color: #fff;
}
.subnav ul li a span {
    float: right;
}
.subnav ul li a:hover {
    color: #00a4ff;
}
.course {
    float: right;
    width: 230px;
    height: 300px;
    background-color: #fff;
    /* 浮动的盒子不会有外边距合并的问题 */
    margin-top: 50px;
}
.course h2 {
    height: 48px;
    background-color: #9bceea;
    text-align: center;
    line-height: 48px;
    font-size: 18px;
    color: #fff;
}
.bd {
    padding: 0 20px;
}
.bd ul li {
    padding: 14px 0;
    border-bottom: 1px solid #ccc;
}
.bd ul li h4 {
    font-size: 16px;
    color: #4e4e4e;
}
.bd ul li p {
    font-size: 12px;
    color: #a5a5a5;
}
.bd .more {
    display: block;
    height: 38px;
    border: 1px solid #00a4ff;
    margin-top: 5px;
    text-align: center;
    line-height: 38px;
    color: #00a4ff;
    font-size: 16px;
    font-weight: 700;
}
/* 精品推荐模块 */
.goods {
    height: 60px;
    background-color: #fff;
    margin-top: 10px;
    box-shadow: 0 2px 3px 3px rgba(0,0,0, 0.1);
    /* 行高会继承, 会继承给3个孩子 */
    line-height: 60px;
}
.goods h3 {
    float: left;
    margin-left: 30px;
    font-size: 16px;
    color: #00a4ff;
}
.goods ul {
    float: left;
    margin-left: 30px;
}
.goods ul li {
    float: left;
}
.goods ul li a {
    padding: 0 30px;
    font-size: 16px;
    color: #050505;
    border-left: 1px solid #ccc;
}
.mod {
    float: right;
    margin-right: 30px;
    font-size: 14px;
    color: #00a4ff;
}
.box {
    margin-top: 30px;
}
.box-hd {
    height: 45px;
}
.box-hd h3 {
    float: left;
    font-size: 20px;
    color: #494949;
}
.box-hd a {
    float: right;
    font-size: 12px;
    color: #a5a5a5;
    margin-top: 10px;
    margin-right: 30px;
}
/* 把li 的父亲ul 修改的足够宽一行能装开5个盒子就不会换行了 */
.box-bd ul {
    width: 1225px;
}
.box-bd ul li {
    position: relative;
    top: 0;
    float: left;
    width: 228px;
    height: 270px;
    background-color: #fff;
    margin-right: 15px;
    margin-bottom: 15px;
    transition: all .3s;
   
}
.box-bd ul li:hover {
    top: -8px;
    box-shadow: 2px 2px 2px 2px rgba(0,0,0,.3);
}
.box-bd ul li img {
    width: 100%;
}
.box-bd ul li h4 {
    margin: 20px 20px 20px 25px;
    font-size: 14px;
    color: #050505;
    font-weight: 400;
}
.box-bd .info {
    margin: 0 20px 0 25px;
    font-size: 12px;
    color: #999;
}
.box-bd .info span {
    color: #ff7c2d;
}
/* footer 模块 */
.footer {
    height: 415px;
    background-color: #fff;
}
.footer .w {
    padding-top: 35px;
}
.copyright {
    float: left;
}
.copyright p {
    font-size: 12px;
    color: #666;
    margin: 20px 0 15px 0;
}
.copyright .app {
    display: block;
    width: 118px;
    height: 33px;
    border: 1px solid #00a4ff;
    text-align: center;
    line-height: 33px;
    color: #00a4ff;
    font-size: 16px;
}
.links {
    float: right;
}
.links dl {
    float: left;
    margin-left: 100px;
}
.links dl dt {
    font-size: 16px;
    color: #333;
    margin-bottom: 5px;
}
.links dl dd a {
    color: #333;
    font-size: 12px;
}

在这里插入图片描述

JavaScript部分准备

data是一个数组对象里面存放数据

let data = [
                        {
                            src: 'images/course01.png',
                            title: 'Think PHP 5.0 博客系统实战项目演练',
                            num: 1125
                        },
                        {
                            src: 'images/course02.png',
                            title: 'Android 网络动态图片加载实战',
                            num: 357
                        },
                        {
                            src: 'images/course03.png',
                            title: 'Angular2 大前端商城实战项目演练',
                            num: 22250
                        },
                        {
                            src: 'images/course04.png',
                            title: 'Android APP 实战项目演练',
                            num: 389
                        },
                        {
                            src: 'images/course05.png',
                            title: 'UGUI 源码深度分析案例',
                            num: 124
                        },
                        {
                            src: 'images/course06.png',
                            title: 'Kami2首页界面切换效果实战演练',
                            num: 432
                        },
                        {
                            src: 'images/course07.png',
                            title: 'UNITY 从入门到精通实战案例',
                            num: 888
                        },
                        {
                            src: 'images/course08.png',
                            title: 'Cocos 深度学习你不会错过的实战',
                            num: 590
                        },
                    ]
JavaScript部分需求实现代码
思路分析
1.创建子级节点,并在页面追加渲染出若干个li 
2.通过js数组对象操作,逐一修改li里面的内容
我的代码

没有实现需求,写了for循环,但是只追加渲染了一个li
在这里插入图片描述


    <script>
        let data = [
            {
                src: 'images/course01.png',
                title: 'Think PHP 5.0 博客系统实战项目演练',
                num: 1125
            },
            {
                src: 'images/course02.png',
                title: 'Android 网络动态图片加载实战',
                num: 357
            },
            {
                src: 'images/course03.png',
                title: 'Angular2 大前端商城实战项目演练',
                num: 22250
            },
            {
                src: 'images/course04.png',
                title: 'Android APP 实战项目演练',
                num: 389
            },
            {
                src: 'images/course05.png',
                title: 'UGUI 源码深度分析案例',
                num: 124
            },
            {
                src: 'images/course06.png',
                title: 'Kami2首页界面切换效果实战演练',
                num: 432
            },
            {
                src: 'images/course07.png',
                title: 'UNITY 从入门到精通实战案例',
                num: 888
            },
            {
                src: 'images/course08.png',
                title: '我会变,你呢?',
                num: 590
            },
            {
                src: 'images/course08.png',
                title: '我会变,你呢?',
                num: 590
            }
        ]
        //创建子级节点,并在页面追加渲染出若干个li
        //通过js数组对象操作,逐一修改li里面的内容
        //1.获取父级元素,创建子级节点
        let ul = document.querySelector('ul')
        let li = document.createElement('li')
        for(let i = 0 ; i < data.length ; i++){
            ul.appendChild(li)
        }
        


    </script>

参考代码
<script>
        let data = [
            {
                src: 'images/course01.png',
                title: 'Think PHP 5.0 博客系统实战项目演练',
                num: 1125
            },
            {
                src: 'images/course02.png',
                title: 'Android 网络动态图片加载实战',
                num: 357
            },
            {
                src: 'images/course03.png',
                title: 'Angular2 大前端商城实战项目演练',
                num: 22250
            },
            {
                src: 'images/course04.png',
                title: 'Android APP 实战项目演练',
                num: 389
            },
            {
                src: 'images/course05.png',
                title: 'UGUI 源码深度分析案例',
                num: 124
            },
            {
                src: 'images/course06.png',
                title: 'Kami2首页界面切换效果实战演练',
                num: 432
            },
            {
                src: 'images/course07.png',
                title: 'UNITY 从入门到精通实战案例',
                num: 888
            },
            {
                src: 'images/course08.png',
                title: '我会变,你呢?',
                num: 590
            },
            {
                src: 'images/course08.png',
                title: '我会变,你呢?',
                num: 590
            }
        ]
        let ul = document.querySelector('ul')
        // 1. 根据数据的个数,决定这小li的个数
        for (let i = 0; i < data.length; i++) {
            // 2. 创建小li
            let li = document.createElement('li')
            // console.log(li)

            // 4. 先准备好内容,再追加 
            li.innerHTML = `
            <img src=${data[i].src} alt="">
            <h4>
                ${data[i].title}
            </h4>
            <div class="info">
                <span>高级</span> • <span> ${data[i].num}</span>人在学习
            </div>
            `
            // 3. 追加给ul   父元素.appendChild(子元素)
            ul.appendChild(li)

        }
    </script>
我的代码bug分析

我的创建节点和追加节点操作没有同时和在一个地方进行,应当是在哪创建节点,就在哪追加节点,创建多少个子节点,就追加多少个子节点,而我的代码中创建了一个节点,却追加了data.length个节点,即创建节点应当写在for循环内部,也要创建data.length个节点

我的代码优化和完善
思路:
总体思路:

创建子级节点,并在页面追加渲染出若干个li,通过innerHTML以及js数组对象逐一修改li里面的内容

详细思路:
        1.在html将父级ul置空,获取父级元素ul
        2.根据数据数组data里面元素个数,循环创建和追加子级元素li,并先修改子级li里面的内容再追加给父级ul
          2.1.创建子级元素li
          2.2.通过innerHTML修改子级li里面的内容,使用反引号``包裹标签,用数组对象的属性填充数据
          2.3.将li追加给ul,从后和前面追加均可
            
代码:
<script>
        let data = [
            {
                src: 'images/course01.png',
                title: 'Think PHP 5.0 博客系统实战项目演练',
                num: 1125
            },
            {
                src: 'images/course02.png',
                title: 'Android 网络动态图片加载实战',
                num: 357
            },
            {
                src: 'images/course03.png',
                title: 'Angular2 大前端商城实战项目演练',
                num: 22250
            },
            {
                src: 'images/course04.png',
                title: 'Android APP 实战项目演练',
                num: 389
            },
            {
                src: 'images/course05.png',
                title: 'UGUI 源码深度分析案例',
                num: 124
            },
            {
                src: 'images/course06.png',
                title: 'Kami2首页界面切换效果实战演练',
                num: 432
            },
            {
                src: 'images/course07.png',
                title: 'UNITY 从入门到精通实战案例',
                num: 888
            },
            {
                src: 'images/course08.png',
                title: '我会变,你呢?',
                num: 590
            },
            {
                src: 'images/course08.png',
                title: '我会变,你呢?',
                num: 590
            }
        ]
        //创建子级节点,并在页面追加渲染出若干个li
        //通过innerHTML以及js数组对象逐一修改li里面的内容
        //1.在html将父级ul置空,获取父级元素ul
        let ul = document.querySelector('ul')
        //2.根据数据数组data里面元素个数,循环创建和追加子级元素li,并先修改子级li里面的内容再追加给父级ul
        for(let i = 0 ; i < data.length ; i++){
            //2.1.创建子级元素li
            let li = document.createElement('li')
            //2.2.通过innerHTML修改子级li里面的内容,使用反引号``包裹标签,用数组对象的属性填充数据
            li.innerHTML = `<img src="${data[i].src}" alt="">
                    <h4>
                        ${data[i].title}
                    </h4>
                    <div class="info">
                        <span>高级</span> • <span> ${data[i].num}</span>人在学习
                    </div>`
            //2.3.将li追加给ul,从后和前面追加均可
            //从后面正序追加
            ul.appendChild(li)
            // //从前面倒序追加
            // ul.insertBefore(li,ul.children[0])
        }
        


    </script>
我的代码优化和完善后的运行结果

从后面正序追加
在这里插入图片描述
从前面倒序追加
在这里插入图片描述

3.2.4.克隆节点

克隆节点概念

特殊情况下(如:淘宝轮播图无缝滚动),我们新增节点,按照如下操作:

(1)复制一个原有的节点

(2) 把复制的节点放入到指定的元素内部

克隆节点语法:
元素.cloneNode(布尔值)

cloneNode,会克隆出一个跟原标签一样的元素,括号内传入布尔值:

(1)若为true,则代表克隆时会包含后代节点一起克隆

(2)若为false,则代表克隆时不包含后代节点

默认为false

实例
在body标签中克隆ul带有后代节点以及不带有后代节点

<!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>
</head>
<body>
    <ul>
        <li>内容内容</li>
    </ul>

</body>
</html>

代码与思路:

<!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>
</head>
<body>
    <ul>
       
        <li>内容内容</li>
    </ul>
    <script>
       
        //克隆节点
        //语法:元素.cloneNode(布尔值)
        //如果括号为空则默认为false,若为false,则不克隆后代节点
        //在body标签中克隆ul带有后代节点以及不带有后代节点
        //1.获取ul标签,
        let ul = document.querySelector('ul')
        //2,克隆ul,且ul带有后代节点
        let newUL = ul.cloneNode(true)
        //2.1.在body追加克隆的ul
        document.body.appendChild(newUL)
        //3,克隆ul,且ul不带有后代节点
        let newUl = ul.cloneNode(false)
        //3.1.在body追加克隆的ul
        document.body.appendChild(newUl)
    </script>
</body>
</html>

运行结果:

在这里插入图片描述

4.删除节点

4.1.删除节点的概念

若一个节点在页面中已不需要时,可以删除它

在 JavaScript 原生DOM操作中,要删除元素必须通过父元素删除

4.2.删除节点语法

父元素.removeChild(子元素)

注:

(1).如不存在父子关系则删除不成功

(2).删除节点和隐藏节点(display:none) 有区别的: 隐藏节点还是存在的,但是删除,则从html中删除节点

实例:

需求:点击按钮,删除li

<!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>
</head>
<body>
    <button>点击</button>
    <ul>
        <li>我是内容11111</li>
    </ul>
    
</body>
</html>

代码与思路

<!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>
</head>
<body>
    <button>点击</button>
    <ul>
        <li>我是内容11111</li>
    </ul>
    <script>
        // 需求:点击按钮,删除li
        //1.获取标签 按钮button和父级ul
        let btn = document.querySelector('button')
        let ul = document.querySelector('ul')
        //2.注册事件,删除子级节点
        btn.addEventListener('click',function(){
            //删除节点的语法 父元素.removeChild(子元素)
            ul.removeChild(ul.children[0])
        })
    </script>
</body>
</html>

运行结果:

点击前:
在这里插入图片描述
点击后:

在这里插入图片描述

5.时间对象

  • 实例化

  • 时间对象方法

  • 时间戳

5.1.时间对象的概念和作用

时间对象:用来表示时间的对象

作用:可以得到当前系统时间

5.2.实例化

在代码中发现了 new 关键字时,一般将这个操作称为实例化

创建一个时间对象并获取时间

5.2.1.获得当前时间

语法:

 //获得当前时间
 let 变量名 = new Date()
//小括号为空可以得到当前的时间

5.2.2.获得指定时间

使用场景:指定时间为了在某年某月某日做一件事而做准备,比如:倒计时
语法:

  //小括号里面写上时间,可以返回指定的时间
  let 变量名 = new Date('年-月-日 时:分:秒')

例:

<script>
        //实例化
        //声明数组,写法一
        let arr = []
        //观察获取数组
        console.log(arr)
        //声明数组,写法二,实例化声明数组
        let arr1 = new Array()
        //观察获取数组
        console.log(arr1)
        //声明对象,写法一
        let obj = {}
        //观察获取对象
        console.log(obj)
        //声明对象,写法二,实例化声明对象
        let obj1 = new Object()
        //观察获取对象
        console.log(obj1)
        //new 实例化 时间对象
        //1.获得当前时间
        //语法:let 变量名 = new Date()
        //小括号为空可以得到当前的时间
        let date = new Date()
        //观察获取的当前时间
        console.log(date)
        //2.获得指定时间
        //小括号里面写上时间,可以返回指定的时间
        //语法:
        // let 变量名 = new Date('年-月-日 时:分:秒')
        let date1 = new Date('2022-7-20 15:00:30')
        //观察获取指定的时间
        console.log(date1)
    </script>

在这里插入图片描述

5.3.时间对象方法

5.3.1.时间对象方法与作用


方 法             作 用               说 明

getFullYear() 获得年份             获取四位年份

getMonth()    获得月份             取值为 0 ~ 11 (实际开发中需要+1,写成getMonth()+1)

getDate()     获取月份中的每一天    不同月份取值也不相同

getDay()      获取星期             取值为 0 ~ 6

getHours()    获取小时             取值为 0 ~ 23

getMinutes()  获取分钟             取值为 0 ~ 59

getSeconds()  获取秒               取值为 0 ~ 59

5.3.2.时间对象方法与作用案例

例1:实例化后时间对象,观察时间对象常用方法

代码与思路:

 //例:实例化后时间对象,观察时间对象常用方法
        // new 实例化 时间对象
        //小括号为空可以得到当前的时间
        //1.实例化时间对象
        //let 变量名 = new Date()
        let date = new Date()
        //2.观察时间对象常用方法
        //获得年份 
        console.log(date.getFullYear())
        //获得月份  实际开发中需要+1,写成getMonth()+1
        console.log(date.getMonth()+1)
        // 获取月份中的每一天 
        console.log(date.getDate())
        //获取小时 
        console.log(date.getHours())
        //获取分钟  
        console.log(date.getMinutes())
        //获取秒    
        console.log(date.getSeconds())
        //获取星期 
        console.log(date.getDay())

在这里插入图片描述

例2:需求:将当前时间以:YYYY-MM-DD HH:mm 形式显示在页面**
HTML+CSS部分
思路:
自定义编写html和css基础页面,
本例中,我编写一个宽度600px、高度200px、背景颜色为cyan的div盒子,并让盒子中的字体加粗和文字水平垂直居中
代码:
<!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: 600px;
            height: 200px;
            background-color: cyan;
            font-weight:700 ;
            font-size: 20px;
            margin: 100px auto;
            line-height: 200px;
            text-align: center;
        }
    </style>
</head>
<body>
    <!-- 静态初始页面 -->
    <div>2023- 1-17 15: 58 : 54 星期2</div>
    
</body>
</html>

在这里插入图片描述

JavaScript部分
我的思路与代码:

1.总体思路:

获取标签,实例化时间对象,调用时间对象的方法,修改和写入获取标签的innerHTML中

2.详细思路:

1.获取div标签
2.实例化时间对象
3.调用时间对象常用方法,并将获取的值给相应的变量
4.修改和写入获取标签的innerHTML中

3.代码:

<!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: 600px;
            height: 200px;
            background-color: cyan;
            font-weight:700 ;
            font-size: 20px;
            margin: 100px auto;
            line-height: 200px;
            text-align: center;
        }
    </style>
</head>
<body>
    <!-- 静态初始页面 -->
    <div></div>
    <!-- 2023- 1-17 15: 58 : 54 星期2 -->
    <!--JavaScript部分 -->
    <script>
        //总体思路:获取标签,实例化时间对象,调用时间对象的方法,修改和写入获取标签的innerHTML中
        //详细思路与代码:
        //1.获取div标签
        let getTime = document.querySelector('div')

        //2.实例化时间对象
        let time = new Date()

        //3.调用时间对象常用方法,并将获取的值给相应的变量
        //获得年份 
        let year = time.getFullYear()
        //获得月份  实际开发中需要+1,写成getMonth()+1
        let month = time.getMonth()+1
        // 获取月份中的每一天 
        let day = time.getDate()
        //获取小时 
        let hour = time.getHours()
        //获取分钟  
        let minutes = time.getMinutes()
        //获取秒    
        let seconds = time.getSeconds()
         //时间对象获取星期
         let week = time.getDay()
        
        // 4.修改和写入获取标签的innerHTML中
        getTime.innerHTML = `${year}-${month}-${day}&nbsp;&nbsp;
        ${hour}&nbsp;:&nbsp${minutes}&nbsp;:&nbsp${seconds}
        &nbsp&nbsp;&nbsp;星期${week}`
    </script>

</body>
</html>
参考代码:

参考代码使用定时器,将实例化时间对象 写到定时器里面,让显示的时间能够动起来,变成每时每刻的时间,并且先调用函数,就省去了1秒的空白期,同时使用数组让在调用时间对象获取星期后的数字能转换为中文字符。

<!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: 400px;
            height: 50px;
            background-color: pink;
            text-align: center;
            line-height: 50px;
        }
    </style>
</head>

<body>
    <div></div>
    <script>

        let arr = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
        let div = document.querySelector('div')
        // 先调用,就省去了1秒的空白期
        getTime()
        setInterval(getTime, 1000)
        function getTime() {
            // 1. 实例化时间对象 一定写到定时器里面才可以额
            let date = new Date()
            let year = date.getFullYear()
            let month = date.getMonth() + 1
            let date1 = date.getDate()
            let hour = date.getHours()
            let min = date.getMinutes()
            let sec = date.getSeconds()
            let day = date.getDay()
            div.innerHTML = `今天是: ${year}${month}${date1}${hour}:${min}:${sec} ${arr[day]}`
        }
    </script>
</body>

</html>

运行结果:
在这里插入图片描述

我的代码优化:

优化一:启用定时器,让时间随时动态发生过变化
思路:

1.将实例化和调用时间对象以及修改和写入获取标签的innerHTML的代码封装在函数time()中,
2.先调用函数,再把函数写入定时器中,

核心代码:

 //定义函数time存放实例化和调用时间对象,修改和写入获取标签的innerHTML
        function time(){
        //2.实例化时间对象
        let time = new Date()
        //3.调用时间对象常用方法,并将获取的值给相应的变量
        //获得年份 
        let year = time.getFullYear()
        //获得月份  实际开发中需要+1,写成getMonth()+1
        let month = time.getMonth()+1
        // 获取月份中的每一天 
        let day = time.getDate()
        //获取小时 
        let hour = time.getHours()
        //获取分钟  
        let minutes = time.getMinutes()
        //获取秒    
        let seconds = time.getSeconds()
         //时间对象获取星期
         let week = time.getDay()
        // 4.修改和写入获取标签的innerHTML中
        //星期部分:将调用时间对象获取的星期数字与存放中文星期的数组的数组序号一一对应
        //比如:对象获取的星期一中的1对应存放中文星期的数组中的weekend[1]
         getTime.innerHTML = `${year}-${month}-${day}&nbsp;&nbsp;
        ${hour}&nbsp;:&nbsp${minutes}&nbsp;:&nbsp${seconds}
        &nbsp&nbsp;&nbsp;星期${weekend[week]}`
    
        }
        //6.调用函数
        //目的:减少回调函数setInterval(函数名,毫秒数)在先调用执行1000ms再执行time函数产生的空白时间
        time()
        //5.启用定时器,让时间随时动态发生过变化
        //语法:setInterval(函数名,毫秒数)
        setInterval(time,1000)

在这里插入图片描述

细节:先调用函数,再使用定时器,避免1s空挡时间*

若不先调用函数:

time()

结果执行如下:

产生1s空挡时间
在这里插入图片描述
在这里插入图片描述
原因:
回调函数setInterval(函数名,毫秒数)在先调用执行1000ms再执行time函数

优化二:让星期中的数字中文显示

思路:

1.定时器外定义存放中文星期的数组
2.星期部分:将调用时间对象获取的星期数字与存放中文星期的数组的数组序号一一对应,
比如:对象获取的星期一中的1对应存放中文星期的数组中的weekend[1]

核心代码:

//定义存放中文星期的数组
        let weekend = ['日','一','二','三','四','五','六']
// 4.修改和写入获取标签的innerHTML中
        //星期部分:将调用时间对象获取的星期数字与存放中文星期的数组的数组序号一一对应
        //比如:对象获取的星期一中的1对应存放中文星期的数组中的weekend[1]
         getTime.innerHTML = `${year}-${month}-${day}&nbsp;&nbsp;
        ${hour}&nbsp;:&nbsp${minutes}&nbsp;:&nbsp${seconds}
        &nbsp&nbsp;&nbsp;星期${weekend[week]}`

我的优化后的完整代码:

<script>
        //总体思路:获取标签,实例化时间对象,调用时间对象的方法,修改和写入获取标签的innerHTML中
        //详细思路与代码:
        //1.获取div标签和定义存放中文星期的数组
        //获取div标签
        let getTime = document.querySelector('div')
        //定义存放中文星期的数组
        let weekend = ['日','一','二','三','四','五','六']
        //定义函数time存放实例化和调用时间对象,修改和写入获取标签的innerHTML
        function time(){
        //2.实例化时间对象
        let time = new Date()
        //3.调用时间对象常用方法,并将获取的值给相应的变量
        //获得年份 
        let year = time.getFullYear()
        //获得月份  实际开发中需要+1,写成getMonth()+1
        let month = time.getMonth()+1
        // 获取月份中的每一天 
        let day = time.getDate()
        //获取小时 
        let hour = time.getHours()
        //获取分钟  
        let minutes = time.getMinutes()
        //获取秒    
        let seconds = time.getSeconds()
         //时间对象获取星期
         let week = time.getDay()
        // 4.修改和写入获取标签的innerHTML中
        //星期部分:将调用时间对象获取的星期数字与存放中文星期的数组的数组序号一一对应
        //比如:对象获取的星期一中的1对应存放中文星期的数组中的weekend[1]
         getTime.innerHTML = `${year}-${month}-${day}&nbsp;&nbsp;
        ${hour}&nbsp;:&nbsp${minutes}&nbsp;:&nbsp${seconds}
        &nbsp&nbsp;&nbsp;星期${weekend[week]}`
    
        }
        //6.调用函数
        //目的:减少回调函数setInterval(函数名,毫秒数)在先调用执行1000ms再执行time函数产生的空白时间
        time()
        //5.启用定时器,让时间随时动态发生过变化
        //语法:setInterval(函数名,毫秒数)
        setInterval(time,1000)
    </script>

运行结果
在这里插入图片描述
补充:

注意:所有关于时间的代码包括实例化必须写在定时器里面,不能写在外面。
实例化必须写在定时器内,当我1s调用时,先得到当前最新的时间,再从最新的时间拿到年月日时分秒。
若写在外面,页面一加载,先执行let time = new Date(),页面的时间将不会变化,因为定时器1s调用的是打开页面的当前时间的时分秒。

5.4.时间戳

5.4.1.时间戳的基本概念

概念

是指1970年01月01日00时00分00秒起至现在的毫秒数,它是一种特殊的计量时间的方式

使用时间戳的目的:

计算剩余时间和倒计时
算出过去的毫秒和现在的毫秒数,两者独立,互不影响,相减即可算出剩余毫秒数,然后再将结果转换为时分秒,得到换算为时分秒的剩余时间。
即:可以利用将来的时间戳减去现在的时间戳就是剩余时间的毫秒数,再将其转换为时分秒就是剩余时间。

5.4.2.三种方式获取时间戳

方式1.使用 getTime() 方法

语法:

//1.实例化
    let 变量名 = new Date()
//2.获取时间戳
    //变量名.getTime()
    console.log(变量名.getTime())

例:

//实例化
  let date = new Date()
//获取时间戳
  console.log(date.getTime())

在这里插入图片描述

方式2.简写 +new Date()

语法:

// 1).获取当前的时间戳
+new  Date()
// 2) .获取指定时间的时间戳
+new Date('年-月-日 时:分:秒')

例:

//方式2.简写 +new Date()
        // 1).获取当前的时间戳
        let b = +new  Date()
        console.log(b)
        // 2) .获取指定时间的时间戳
        //console.log(+new Date('年-月-日 时:分:秒'))
        let c = +new  Date('2023-1-22 00:00:00')
        console.log(c)

在这里插入图片描述

方式3.使用 Date.now(),只能得到当前的

特点:

1)无需实例化

2)但是只能得到当前的时间戳, 而前面两种可以返回指定时间的时间戳

语法:

Date.now()

例:

let a = Date.now()
console.log(a)

在这里插入图片描述

5.4.3.案例-春节倒计时效果

需求:计算到春节还有多少时间

案例
在这里插入图片描述
案例总体分析与注意事项:

分析:
①:用将来时间减去现在时间就是剩余的时间
②:核心: 使用将来的时间戳减去现在的时间戳
③:把剩余的时间转换为 天 时 分 秒

注意事项:

  1. 通过时间戳得到是毫秒,需要转换为秒在计算
  2. 转换公式:

d = parseInt(总秒数/ 60/60 /24); // 计算天数

h = parseInt(总秒数/ 60/60 %24) // 计算小时

m = parseInt(总秒数 /60 %60 ); // 计算分数

s = parseInt(总秒数%60); // 计算当前秒数

素材分析:HTML+CSS部分

素材:

在这里插入图片描述

<!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>
    .countdown {
      width: 240px;
      height: 305px;
      text-align: center;
      line-height: 1;
      color: #fff;
      background-color: brown;
      /* background-size: 240px; */
      /* float: left; */
      overflow: hidden;
    }

    .countdown .next {
      font-size: 16px;
      margin: 25px 0 14px;
    }

    .countdown .title {
      font-size: 33px;
    }

    .countdown .tips {
      margin-top: 80px;
      font-size: 23px;
    }

    .countdown small {
      font-size: 17px;
    }

    .countdown .clock {
      width: 142px;
      margin: 18px auto 0;
      overflow: hidden;
    }

    .countdown .clock span,
    .countdown .clock i {
      display: block;
      text-align: center;
      line-height: 34px;
      font-size: 23px;
      float: left;
    }

    .countdown .clock span {
      width: 34px;
      height: 34px;
      border-radius: 2px;
      background-color: #303430;
    }

    .countdown .clock i {
      width: 20px;
      font-style: normal;
    }
  </style>
</head>

<body>
  <div class="countdown">
    <p class="next">今天是2021年8月28日</p>
    <p class="title">下班倒计时</p>
    <p class="clock">
      <span id="hour">00</span>
      <i>:</i>
      <span id="minutes">25</span>
      <i>:</i>
      <span id="scond">20</span>
    </p>
    <p class="tips">
      现在是18:30:00
    </p>
  </div>

</body>

</html>

盒子模型分析:
在这里插入图片描述

countdown是最外层的大盒子,里面包着存放当前日期的next盒子、存放倒计时主题:下班倒计时title盒子、存放剩余时间的clock盒子以及存放现在的时分秒的tips盒子。此外,"clock盒子内包着剩余时间时分秒的hour、minutes以及scond三个盒子和两个i标签。

JavaScript 部分
需求1:显示当前时间(日期与时分秒)
思路:

总体思路

获取标签、实例化时间对象和调用时间对象方法,修改有关标签的innerHTML属性,通过定时器动态变化时间

详细思路


1.获取存放当前日期的next盒子以及现在时分秒时间的tips盒子
2.将实例化时间对象、调用时间对象方法、修改有关标签的innerHTML属性封装在函数里面,通过定时器执行且动态变化时间
2.1.实例化时间对象
2.2.调用时间对象方法,得到现在的年月日时分秒,时分秒小于10的时候自动补零
2.2.1.调用时间对象方法,得到现在的年月日时分秒,
2.2.2.时分秒小于10的时候自动补零
2.3.修改有关标签的innerHTML属性
3.调用函数减少1s时间空挡
4.定时器回调执行,动态变化时间
 
代码:
//1.获取存放当前日期的next盒子以及现在时分秒时间的tips盒子
  let today = document.querySelector('.next')
  let time = document.querySelector(' .tips')
  //2.将实例化时间对象、调用时间对象方法、修改有关标签的innerHTML属性封装在函数里面,通过定时器执行且动态变化时间
 function timeShowing(){
    //2.1.实例化时间对象
    let date = new Date()
    //2.2.调用时间对象方法,得到现在的年月日时分秒,时分秒小于10的时候自动补零
    //2.2.1.调用时间对象方法,得到现在的年月日时分秒,
    let year = date.getFullYear() 
    let month = date.getMonth()+1 
    let day = date.getDate() 
    let hour = date.getHours() 
    let minute = date.getMinutes() 
    let second = date.getSeconds() 
    //2.2.2.时分秒小于10的时候自动补零
    hour = hour < 10 ?  '0'+ hour: hour
    minute = minute < 10 ? '0' + minute: minute
    second = second < 10 ? '0' + second: second
    //2.3.修改有关标签的innerHTML属性
    today.innerHTML = `今天是${year}${month}${day}`
    time.innerHTML = `现在是${hour}:${minute}:${second}`
 }
 //3.调用函数减少1s时间空挡
 timeShowing()
 //4.定时器回调执行,动态变化时间
 setInterval(timeShowing,1000)

运行结果:
在这里插入图片描述

需求2:实现倒计时效果
思路:

总体思路:

1.计算剩余时间的时间戳,将剩余时间的时间戳转换为对应的时分秒,修改剩余时分秒的innerHTML属性,
2.封装剩余时间计算和转换代码封装在函数内,并调用函数,启用定时器让时间动态变化

详细思路:

1.定义变量,存放现在和未来时间戳;
2.定义变量,存放计算存放现在和未来时间戳之差,即:计算将来与现在的剩余时间,注意:时间戳之差的结果单位是毫秒,要将计算结果除以10003.将剩余时间转换为时分秒,小于10的时候自动补零
4.获取剩余时间时分秒对应标签,修改剩余时分秒对应的标签的innerHTML属性
5.定义函数封装所有有关剩余时间计算、结果转换以及修改剩余时分秒的innerHTML属性的代码,调用函数,启用定时器,让剩余时间动态变化
代码:
//5.定义函数封装所有有关剩余时间计算、结果转换以及修改剩余时分秒的innerHTML属性的代码,调用函数,启用定时器,让剩余时间动态变化
//5.1.定义函数封装所有有关剩余时间变量的代码
function getRestTime (){
  //1.定义变量,存放现在和未来时间戳
let now = +new Date()
let future = +new Date('2023-1-22 00:00:00')
  //2.定义变量 ,存放计算存放现在和未来时间戳之差,即:计算将来与现在的剩余时间,注意:时间戳之差的结果单位是毫秒,要将计算结果除以1000;
let restTime = (future - now)/1000
//3.将剩余时间转换为时分秒,小于10的时候自动补零
//3.1.将剩余时间转换为时分秒
let restHour = parseInt(restTime/ 60 / 60 % 24) // 计算小时
let restMinute= parseInt(restTime /60 % 60 ); // 计算分数
let restSecond = parseInt(restTime % 60); // 计算当前秒数
//3.2.时分秒小于10的时候自动补零
restHour = restHour < 10 ?  '0'+ restHour: restHour
restMinute = restMinute < 10 ? '0' + restMinute: restMinute
restSecond = restSecond < 10 ? '0' + restSecond: restSecond
//4.获取剩余时间时分秒对应标签,修改剩余时分秒对应的标签的innerHTML属性
//4.1.获取剩余时间时分秒对应标签,
let hourRes = document.querySelector('#hour')
let minuteRes = document.querySelector('#minutes')
let scondRes = document.querySelector('#scond')
// 4.2.修改剩余时分秒对应的标签的innerHTML属性
hourRes.innerHTML = `${restHour}`
minuteRes.innerHTML = `${restMinute}`
scondRes.innerHTML = `${restSecond}`
}
//5.2.调用函数减少1s时间空挡
getRestTime()
//5.3.启用定时器,让剩余时间动态变化
setInterval(getRestTime,1000)

在这里插入图片描述
在这里插入图片描述

细节:

1.获取未来和现在的时间戳代码

let now = +new Date()
let future = +new Date('2023-1-22 00:00:00')

必须写在函数内,才会在定时器里面起作用;
若将上述代码写在外面运行结果如下:

在这里插入图片描述
在这里插入图片描述

因为若写在定时器的函数外面,得到的时间戳是浏览器启动时的时间戳,是静态的和固定不变的,并且定时器的数据自动变化只局限于自身内的函数,每隔一定时间,回调执行函数,因此不会调用外面全局变量的现在和未来的时间戳,从而让定时器内函数计算剩余时间的变量动态变化。
2.注意:时间戳之差的结果单位是毫秒,要将计算结果除以1000;
否则剩余时间结果计算出错,1分钟内剩余时间时分秒大幅度同时变化,不符合预期结果。
若不除以1000,运行结果如下:
在这里插入图片描述

在这里插入图片描述
3.本例中实际计算的剩余几天几小时几分几秒,仍需后期完善出计算和转换剩余天数的结果,由于时间有限,此处不予完善。

完整代码
<!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>
    .countdown {
      width: 240px;
      height: 305px;
      text-align: center;
      line-height: 1;
      color: #fff;
      background-color: brown;
      /* background-size: 240px; */
      /* float: left; */
      overflow: hidden;
    }

    .countdown .next {
      font-size: 16px;
      margin: 25px 0 14px;
    }

    .countdown .title {
      font-size: 33px;
    }

    .countdown .tips {
      margin-top: 80px;
      font-size: 23px;
    }

    .countdown small {
      font-size: 17px;
    }

    .countdown .clock {
      width: 142px;
      margin: 18px auto 0;
      overflow: hidden;
    }

    .countdown .clock span,
    .countdown .clock i {
      display: block;
      text-align: center;
      line-height: 34px;
      font-size: 23px;
      float: left;
    }

    .countdown .clock span {
      width: 34px;
      height: 34px;
      border-radius: 2px;
      background-color: #303430;
    }

    .countdown .clock i {
      width: 20px;
      font-style: normal;
    }
  </style>
</head>

<body>
  <div class="countdown">
    <p class="next">今天是2021年8月28日</p>
    <p class="title">春节倒计时</p>
    <p class="clock">
      <span id="hour">00</span>
      <i>:</i>
      <span id="minutes">25</span>
      <i>:</i>
      <span id="scond">20</span>
    </p>
    <p class="tips">
      现在是18:30:00
    </p>
  </div>
<script>
  // 需求1:显示当前时间(日期与时分秒)
  //总体思路:获取标签、实例化时间对象和调用时间对象方法,修改有关标签的innerHTML属性,通过定时器动态变化时间
  //1.获取存放当前日期的next盒子以及现在时分秒时间的tips盒子
  let today = document.querySelector('.next')
  let time = document.querySelector(' .tips')
  //2.将实例化时间对象、调用时间对象方法、修改有关标签的innerHTML属性封装在函数里面,通过定时器执行且动态变化时间
 function timeShowing(){
    //2.1.实例化时间对象
    let date = new Date()
    //2.2.调用时间对象方法,得到现在的年月日时分秒,时分秒小于10的时候自动补零
    //2.2.1.调用时间对象方法,得到现在的年月日时分秒,
    let year = date.getFullYear() 
    let month = date.getMonth()+1 
    let day = date.getDate() 
    let hour = date.getHours() 
    let minute = date.getMinutes() 
    let second = date.getSeconds() 
    //2.2.2.时分秒小于10的时候自动补零
    hour = hour < 10 ?  '0'+ hour: hour
    minute = minute < 10 ? '0' + minute: minute
    second = second < 10 ? '0' + second: second
    //2.3.修改有关标签的innerHTML属性
    today.innerHTML = `今天是${year}${month}${day}`
    time.innerHTML = `现在是${hour}:${minute}:${second}`
 }
 //3.调用函数减少1s时间空挡
 timeShowing()
 //4.定时器回调执行,动态变化时间
 setInterval(timeShowing,1000)
//  需求2:实现倒计时效果
//总体思路:
//1.计算剩余时间的时间戳,将剩余时间的时间戳转换为对应的时分秒,修改剩余时分秒的innerHTML属性;
// 2.封装剩余时间计算和转换代码封装在函数内,并调用函数,启用定时器让时间动态变化
//详细思路:
// //1.定义变量,存放现在和未来时间戳
// let now = +new Date()
// let future = +new Date('2023-1-22 00:00:00')
//5.定义函数封装所有有关剩余时间计算、结果转换以及修改剩余时分秒的innerHTML属性的代码,调用函数,启用定时器,让剩余时间动态变化
//5.1.定义函数封装所有有关剩余时间变量的代码
function getRestTime (){
  //1.定义变量,存放现在和未来时间戳
let now = +new Date()
let future = +new Date('2023-1-22 00:00:00')
  //2.定义变量 ,存放计算存放现在和未来时间戳之差,即:计算将来与现在的剩余时间,注意:时间戳之差的结果单位是毫秒,要将计算结果除以1000;
  // let restTime = future - now
let restTime = (future - now)/1000
//3.将剩余时间转换为时分秒,小于10的时候自动补零
//3.1.将剩余时间转换为时分秒
let restHour = parseInt(restTime/ 60 / 60 % 24) // 计算小时
let restMinute= parseInt(restTime /60 % 60 ); // 计算分数
let restSecond = parseInt(restTime % 60); // 计算当前秒数
//3.2.时分秒小于10的时候自动补零
restHour = restHour < 10 ?  '0'+ restHour: restHour
restMinute = restMinute < 10 ? '0' + restMinute: restMinute
restSecond = restSecond < 10 ? '0' + restSecond: restSecond
//4.获取剩余时间时分秒对应标签,修改剩余时分秒对应的标签的innerHTML属性
//4.1.获取剩余时间时分秒对应标签,
let hourRes = document.querySelector('#hour')
let minuteRes = document.querySelector('#minutes')
let scondRes = document.querySelector('#scond')
// 4.2.修改剩余时分秒对应的标签的innerHTML属性
hourRes.innerHTML = `${restHour}`
minuteRes.innerHTML = `${restMinute}`
scondRes.innerHTML = `${restSecond}`
}
//5.2.调用函数减少1s时间空挡
getRestTime()
//5.3.启用定时器,让剩余时间动态变化
setInterval(getRestTime,1000)

</script>
</body>

</html>

6.重绘与重排

6.1. 浏览器是如何进行界面渲染的

在这里插入图片描述

(1).浏览器解析(Parser)HTML,生成DOM树(DOM Tree)

(2).同时浏览器解析(Parser) CSS,生成样式规则 (Style Rules)

(3) .根据DOM树和样式规则,生成渲染树(Render Tree)

(4).进行布局 Layout(回流/重排):根据生成的渲染树,得到节点的几何信息(位置,大小),确定页面中的盒子布局

(5).进行绘制 Painting(重绘): 根据计算和获取的信息进行整个页面的绘制(如:给盒子上色、修改字体颜色、修改文字字体等不影响页面布局的操作)

(6).Display: 展示在页面上

6.2.2. 重绘和回流(重排)

1.回流(或称重排)

当渲染树中部分或者全部元素的尺寸、结构、布局等发生改变时,浏览器就会重新渲染部分或全部文档的过程称为 回流。
即涉及影响页面中盒子的尺寸、结构、显示隐藏、内部文字大小等页面布局信息的变化过程就是回流。

2.重绘

由于节点(元素)的样式的改变并不影响它在文档流中的位置和文档布局时(比如:color、background-color、outline等), 称为重绘。
即不影响页面中盒子布局的位置和文档布局就是重绘。

注意:

重绘不一定引起回流,而回流一定会引起重绘。

因为:
(1).若改变一个盒子的背景色,则是重绘过程,这个过程并没有影响页面中盒子的尺寸、结构、布局等信息的变化,所以重绘不一定引起回流。

(2)浏览器在进行页面渲染时,先进行回流,再进行重绘,进行回流时,页面中盒子的尺寸、结构、显示隐藏、内部文字大小等信息发生变化,导致页面结构重新布局,在布局后进行重绘。

会导致回流(重排)的操作:

1.页面的首次刷新

2.浏览器的窗口大小发生改变

3.元素的大小或位置发生改变

4.改变字体的大小

5.内容的变化(如:input框的输入,图片的大小)

6.激活css伪类 (如::hover)

7.脚本操作DOM(添加或者删除可见的DOM元素)

例:
在这里插入图片描述

7.综合案例——微博发布

在这里插入图片描述

7.1.需求

需求1

  1. 注册input事件
  2. 将文本的内容的长度赋值给对应的数值
  3. 表单的maxlength属性可以直接限制在200个数之间

需求2

克隆预定义好的模板,将模板的hidden属性设置为false, 并最终展示到页面上
判断如果内容为空,则提示不能输入为空, 并且直接return
防止输入无意义空格, 使用字符串.trim()去掉首尾空格, 并将表单的value值设置为空字符串

需求3

获取文本域的内容, 赋值给由模板克隆出来的新标签里面的content.innerText
随机获取数据数组里面的内容, 替换newNode的图片和名称
利用时间对象将时间动态化 new Date().toLocaleString()

需求4

在事件处理函数里面获取点击按钮,注册点击事件
(易错点: 必须在事件里面获取,外面获取不到)
删除对应的元素 (通过this获取对应的那条需要删除的元素)

需求5

将表单域内容重置为空
将userCount里面的内容重置为0

7.2.素材与素材分析

7.2.1.素材

html+css部分

HTML

<div class="w">
    <!-- 操作的界面 -->
    <div class="controls">
      <img src="./images/9.6/tip.png" alt="" /><br />
      <!-- maxlength 可以用来限制表单输入的内容长度 -->
      <textarea placeholder="说点什么吧..." id="area" cols="30" rows="10" maxlength="200"></textarea>
      <div>
        <span class="useCount" id="useCount">0</span>
        <span>/</span>
        <span>200</span>
        <button id="send">发布</button>
      </div>
    </div>
    <!-- 微博内容列表 -->
    <div class="contentList">
      <ul id="list">
         <!-- 添加了hidden属性元素会直接隐藏掉 -->
  <li hidden>
    <div class="info">
      <img class="userpic" src="./images/9.6/03.jpg" />
      <span class="username">死数据:百里守约</span>
      <p class="send-time">死数据:发布于 2020年12月05日 00:07:54</p>
    </div>
    <div class="content">死数据:111</div>
    <span class="the_del">X</span>
  </li>
      </ul>
    </div>
  </div>

css

 * {
      margin: 0;
      padding: 0;
    }

    ul {
      list-style: none;
    }

    .w {
      width: 900px;
      margin: 0 auto;
    }

    .controls textarea {
      width: 878px;
      height: 100px;
      resize: none;
      border-radius: 10px;
      outline: none;
      padding-left: 20px;
      padding-top: 10px;
      font-size: 18px;
    }

    .controls {
      overflow: hidden;
    }

    .controls div {
      float: right;
    }

    .controls div span {
      color: #666;
    }

    .controls div .useCount {
      color: red;
    }

    .controls div button {
      width: 100px;
      outline: none;
      border: none;
      background: rgb(0, 132, 255);
      height: 30px;
      cursor: pointer;
      color: #fff;
      font: bold 14px '宋体';
      transition: all 0.5s;
    }

    .controls div button:hover {
      background: rgb(0, 225, 255);
    }

    .controls div button:disabled {
      background: rgba(0, 225, 255, 0.5);
    }

    .contentList {
      margin-top: 50px;
    }

    .contentList li {
      padding: 20px 0;
      border-bottom: 1px dashed #ccc;
      position: relative;
    }

    .contentList li .info {
      position: relative;
    }

    .contentList li .info span {
      position: absolute;
      top: 15px;
      left: 100px;
      font: bold 16px '宋体';
    }

    .contentList li .info p {
      position: absolute;
      top: 40px;
      left: 100px;
      color: #aaa;
      font-size: 12px;
    }

    .contentList img {
      width: 80px;
      border-radius: 50%;
    }

    .contentList li .content {
      padding-left: 100px;
      color: #666;
      word-break: break-all;
    }

    .contentList li .the_del {
      position: absolute;
      right: 0;
      top: 0;
      font-size: 28px;
      cursor: pointer;
    }

JavaScript

// maxlength 是一个表单属性, 作用是给表单设置一个最大长度

    // 模拟数据
    let dataArr = [
      { uname: '司马懿', imgSrc: './images/9.5/01.jpg' },
      { uname: '女娲', imgSrc: './images/9.5/02.jpg' },
      { uname: '百里守约', imgSrc: './images/9.5/03.jpg' },
      { uname: '亚瑟', imgSrc: './images/9.5/04.jpg' },
      { uname: '虞姬', imgSrc: './images/9.5/05.jpg' },
      { uname: '张良', imgSrc: './images/9.5/06.jpg' },
      { uname: '安其拉', imgSrc: './images/9.5/07.jpg' },
      { uname: '李白', imgSrc: './images/9.5/08.jpg' },
      { uname: '阿珂', imgSrc: './images/9.5/09.jpg' },
      { uname: '墨子', imgSrc: './images/9.5/10.jpg' },
      { uname: '鲁班', imgSrc: './images/9.5/11.jpg' },
      { uname: '嬴政', imgSrc: './images/9.5/12.jpg' },
      { uname: '孙膑', imgSrc: './images/9.5/13.jpg' },
      { uname: '周瑜', imgSrc: './images/9.5/14.jpg' },
      { uname: '老夫子', imgSrc: './images/9.5/15.jpg' },
      { uname: '狄仁杰', imgSrc: './images/9.5/16.jpg' },
      { uname: '扁鹊', imgSrc: './images/9.5/17.jpg' },
      { uname: '马可波罗', imgSrc: './images/9.5/18.jpg' },
      { uname: '露娜', imgSrc: './images/9.5/19.jpg' },
      { uname: '孙悟空', imgSrc: './images/9.5/20.jpg' },
      { uname: '黄忠', imgSrc: './images/9.5/21.jpg' },
      { uname: '百里玄策', imgSrc: './images/9.5/22.jpg' },
    ]

7.2.2.素材分析

在这里插入图片描述

w大盒子包含controls和contentList两个盒子,controls盒子包含textarea标签和div标签,div标签包含3个span标签和一个button发布按钮,然后通过css调整样式和改变盒子的位置,在input标签添加 maxlength="200"来限制用户输入

在这里插入图片描述
contentList盒子内包含list类的ul盒子,后期通过js向list里面追加li,li标签里面包含info、content、the del 这三个盒子,其中info盒子存放用户的发布信息的细节信息:用户头像userpic盒子、用户名username盒子、信息发布时间send-time盒子,content盒子用来存放用户发布信息的内容,the del盒子用来操作删除的盒子。

7.3.需求实现

7.3.1.需求1:文本区域输入文字,字数统计同步变化

  1. 注册input事件
  2. 将文本的内容的长度赋值给对应的数值
  3. 表单的maxlength属性可以直接限制在200个数之间
代码与思路
// 需求1
    // 1. 注册input事件
    // 2. 将文本的内容的长度赋值给对应的数值
    // 3. 表单的maxlength属性可以直接限制在200个数之间
    //思路:
    //1.获取文本内容textarea标签和控制字数变化的useCount盒子标签
    let textarea = document.querySelector('textarea')
    let useCount = document.querySelector('#useCount')

    //2.事件监听,input事件,文本内容值得长度 value.length发生变化
    textarea.addEventListener('input',function(){
      // console.log(textarea.value.length)
      useCount.innerHTML = textarea.value.length
    })

运行结果:
在这里插入图片描述

7.3.2.需求2 :判断用户输入内容是否为空,并给予用户弹窗提示

详细需求

克隆预定义好的模板,将模板的hidden属性设置为false, 并最终展示到页面上
判断如果内容为空,则提示不能输入为空, 并且直接return
防止输入无意义空格, 使用字符串.trim()去掉首尾空格, 并将表单的value值设置为空字符串

思路:
总体思路:
点击发布,判断输入内容不能为空,并将去掉首尾空格, 并将表单的value值设置为空字符串

详细思路
//1.事件监听,点击发布,事件监听用户输入内容value值是否为空 ,返回提示输入输入内容不能为空,请重新输入的提示;
//2.使用字符串.trim()去掉首尾空格, 并将表单的value值设置为空字符串
代码:
//总体思路:
      //点击发布,判断输入内容不能为空,并将去掉首尾空格, 并将表单的value值设置为空字符串
      //详细思路
      //1.获取发送按钮标签
     
      //2.事件监听,点击发布,事件监听用户输入内容value值是否为空 ,返回提示输入输入内容不能为空,请重新输入的提示;
      //3.使用字符串.trim()去掉首尾空格, 并将表单的value值设置为空字符串
      let send =document.querySelector('button')
      send.addEventListener('click',function(){
        if(textarea.value.trim() === ''){
          //细节:
          //1.为什么要去空格?
          //若输入多个空格,点击发布也无法判断输入为空,不能给出输入不能为空,请重新输入的提示;
     
          //2.去除空格方法,字符串.trim()
          //   防止输入无意义空格, 使用字符串.trim()去掉首尾空格
          //console.log('  str')
          //console.log('  str '.trim())
          return alert('输入内容不能为空,请重新输入')
      }

在这里插入图片描述

细节:为什么要去除空格?以及去除空格的方法

1.为什么要去空格?
若输入多个空格,点击发布也无法判断输入为空,不能给出输入不能为空,请重新输入的提示;

2.去除空格方法,字符串.trim(), 防止输入无意义空格, 使用字符串.trim()去掉首尾空格
例:

//2.去除空格方法,字符串.trim()
//   防止输入无意义空格, 使用字符串.trim()去掉首尾空格
          console.log('  str')
          console.log('  str '.trim())

在这里插入图片描述

7.3.3.需求3:将用户发布的微博信息显示在网页中

详细需求

获取文本域的内容, 赋值给由模板克隆出来的新标签里面的content.innerText
随机获取数据数组里面的内容, 替换newNode的图片和名称
利用时间对象将时间动态化 new Date().toLocaleString()

思路:

业务明确:

必须是用户输入内容并点击发布后,在页面才能显示用户自己发布的各种信息,所以这个需求应当写在监听微博发布事件函数内,
即:这个需求写在需求2的发布微博事件监听中

详细思路:

1.获取父级标签,在父级ul中创建并后面追加li标签和文本内容
2.声明和调用随机函数,生成微博发布后的随机用户信息
3.修改li标签的文本内容,随机替换新子节点的文本内容,利用时间对象将时间动态化
4.追加节点  父元素.insertBefore(子元素, 放到那个元素的前面)
代码:
//1.获取父级标签,在父级ul中创建并后面追加li标签和文本内容
      //1.1.获取父级标签
      let list = document.querySelector('#list')
      //错误:1.1创建父节点list的ul盒子和子节点li
      // let list = document.createElement('#list')
      //1.2.创建子节点
      let li = document.createElement('li')
       //2.声明和调用随机函数,生成微博发布后的随机用户信息
       //2.1.声明随机函数
       function randomGet (min,max){
              return Math.floor(Math.random()*(max- min + 1))+min
        }
        //2.2.调用函数
        let random = randomGet(0,dataArr.length-1)
      //3.修改li标签的文本内容,随机替换新子节点的文本内容,利用时间对象将时间动态化
       li.innerHTML = `
    <div class="info">
      <img class="userpic" src = ${dataArr[random].imgSrc} />
      <span class="username">${dataArr[random].uname}</span>
      <p class="send-time">${new Date().toLocaleString()}</p>
    </div>
    <div class="content">${textarea.value}</div>
    <span class="the_del">X</span>`
      // 4.追加节点  父元素.insertBefore(子元素, 放到那个元素的前面)
      list.insertBefore(li,list.children[0])

在这里插入图片描述

遇到问题:

增加节点语法出错

错误示例:
获取父节点和子节点,修改子节点的innerHTML,追加子节点导致起初无法在页面中显示li
代码如下:

//错误:1.1创建父节点list的ul盒子和子节点li
      let list = document.createElement('#list')
      //1.2.创建子节点
      let li = document.createElement('li')
       //2.声明和调用随机函数,生成微博发布后的随机用户信息
       //2.1.声明随机函数
       function randomGet (min,max){
              return Math.floor(Math.random()*(max- min + 1))+min
        }
        //2.2.调用函数
        let random = randomGet(0,dataArr.length-1)
      //3.修改li标签的文本内容,随机替换新子节点的文本内容,利用时间对象将时间动态化
      
      // 4.追加节点  父元素.insertBefore(子元素, 放到那个元素的前面)
      list.insertBefore(li,list.children[0])
     

在这里插入图片描述
正确的增加子节点操作是:
获取父级标签,创建子级节点,在父级追加子级元素

补充知识点:实例化生成本地时间

语法:

new Date().toLocaleString()
7.3.4.需求5:控制显示输入空字符和重置

将表单域内容重置为空
将userCount里面的内容重置为0

详细需求:
输入为空或空格,控制输入字数字符为0,发布微博后重置输入内容和控制输入字数字符置零

思路:

需求:输入为空或空格,控制输入字数字符为0,

在微博内容输入事件中判断用户输入字符为空或空格,修改控制字数变化字符的innerHTML设置为0

需求:发布微博后重置输入内容和控制输入字数字符置零

在点击发布微博事件中:
 1.将输入内容定为空字符串
 2.将控制输入字数字符的innerHTML定为0
代码:

需求:输入为空或空格,控制输入字数字符为0,

textarea.addEventListener('input',function(){
      // console.log(textarea.value.length)
      useCount.innerHTML = textarea.value.length
      //优化:若用户输入多个空格,控制字数变化的useCount的值为0
      //判断用户输入字符为空或空格,修改控制字数变化字符的innerHTML设置为0
      if(textarea.value.trim() === ''){
          useCount.innerHTML = 0
      }
       
      })

在这里插入图片描述
需求:发布微博后重置输入内容和控制输入字数字符置零

send.addEventListener('click',function(){
      
      //需求5
      // 将表单域内容重置为空
      // 将userCount里面的内容重置为0
      //1.将输入内容定为空字符串
      textarea.value = ''
      //2.将控制输入字数字符的innerHTML定为0
      useCount.innerHTML = 0
   })

运行结果
点击发布前:
在这里插入图片描述

点击发布后:
在这里插入图片描述

7.3.5.需求4:删除微博留言

详细需求:

在事件处理函数里面获取点击按钮,注册点击事件
(易错点: 必须在事件里面获取,外面获取不到)
删除对应的元素 (通过this获取对应的那条需要删除的元素)

思路:
业务明确:只有在生成微博内容的li后,才会有删除标志X的标签
1.获取删除标签,注册点击删除事件,其中删除事件是移除li标签以及里面的所有内容
2.删除节点的语法 父元素.removeChild(子元素)
代码:
 // 需求4
      // 在事件处理函数里面获取点击按钮,注册点击事件
      // (易错点: 必须在事件里面获取,外面获取不到)
      // 删除对应的元素 (通过this获取对应的那条需要删除的元素)
      //业务明确:只有在生成微博内容的li后,才会有删除标志X的标签
      //1.获取删除标签,注册点击删除事件,其中删除事件是移除li标签以及里面的所有内容
      let del = document.querySelector('.the_del')
      del.addEventListener('click',function(){
        //2.删除节点的语法 父元素.removeChild(子元素)
        list.removeChild(li)
      })

运行结果:
在这里插入图片描述
点击孙悟空的留言进行删除:

在这里插入图片描述

细节:

1.界面没有X号,就无法获取和绑定事件,要有X号,必须在点击发布后,这样才能做删除微博留言操作,删除必须写在点击发布里面;
2.技巧:点击发布事件,就生成一个新的删除事件,在点击发布后li就绑定一个删除事件,这样在进行删除li时,逐个遍历获取所有的li,即:在追加前面绑定删除事件,创建元素同时顺便绑定事件。
3.删除节点的语法 父元素.removeChild(子元素),直接写子元素的有关js变量
错误示例:
父元素.removeChild(父元素.chilren[子元素数组序号]),这样会报错,无法删除

list.removeChild(list.children[random])

不能这样写,会报错
在这里插入图片描述

7.4.完整代码

<!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>微博发布</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    ul {
      list-style: none;
    }

    .w {
      width: 900px;
      margin: 0 auto;
    }

    .controls textarea {
      width: 878px;
      height: 100px;
      resize: none;
      border-radius: 10px;
      outline: none;
      padding-left: 20px;
      padding-top: 10px;
      font-size: 18px;
    }

    .controls {
      overflow: hidden;
    }

    .controls div {
      float: right;
    }

    .controls div span {
      color: #666;
    }

    .controls div .useCount {
      color: red;
    }

    .controls div button {
      width: 100px;
      outline: none;
      border: none;
      background: rgb(0, 132, 255);
      height: 30px;
      cursor: pointer;
      color: #fff;
      font: bold 14px '宋体';
      transition: all 0.5s;
    }

    .controls div button:hover {
      background: rgb(0, 225, 255);
    }

    .controls div button:disabled {
      background: rgba(0, 225, 255, 0.5);
    }

    .contentList {
      margin-top: 50px;
    }

    .contentList li {
      padding: 20px 0;
      border-bottom: 1px dashed #ccc;
      position: relative;
    }

    .contentList li .info {
      position: relative;
    }

    .contentList li .info span {
      position: absolute;
      top: 15px;
      left: 100px;
      font: bold 16px '宋体';
    }

    .contentList li .info p {
      position: absolute;
      top: 40px;
      left: 100px;
      color: #aaa;
      font-size: 12px;
    }

    .contentList img {
      width: 80px;
      border-radius: 50%;
    }

    .contentList li .content {
      padding-left: 100px;
      color: #666;
      word-break: break-all;
    }

    .contentList li .the_del {
      position: absolute;
      right: 0;
      top: 0;
      font-size: 28px;
      cursor: pointer;
    }
  </style>
</head>

<body>
  <div class="w">
    <!-- 操作的界面 -->
    <div class="controls">
      <img src="./images/9.6/tip.png" alt="" /><br />
      <!-- maxlength 可以用来限制表单输入的内容长度 -->
      <textarea placeholder="说点什么吧..." id="area" cols="30" rows="10" maxlength="200"></textarea>
      <div>
        <span class="useCount" id="useCount">0</span>
        <span>/</span>
        <span>200</span>
        <button id="send">发布</button>
      </div>
    </div>
    <!-- 微博内容列表 -->
    <div class="contentList">
      <ul id="list">
         <!-- 添加了hidden属性元素会直接隐藏掉 -->
         <!-- <li >
          <div class="info">
            <img class="userpic" src="./images/9.6/03.jpg" />
            <span class="username">死数据:百里守约</span>
            <p class="send-time">死数据:发布于 2020年12月05日 00:07:54</p>
          </div>
          <div class="content">死数据:111</div>
          <span class="the_del">X</span>
        </li> -->

      </ul>
    </div>
  </div>

  
  <script>
    // maxlength 是一个表单属性, 作用是给表单设置一个最大长度

    // 模拟数据
    let dataArr = [
      { uname: '司马懿', imgSrc: './images/9.5/01.jpg' },
      { uname: '女娲', imgSrc: './images/9.5/02.jpg' },
      { uname: '百里守约', imgSrc: './images/9.5/03.jpg' },
      { uname: '亚瑟', imgSrc: './images/9.5/04.jpg' },
      { uname: '虞姬', imgSrc: './images/9.5/05.jpg' },
      { uname: '张良', imgSrc: './images/9.5/06.jpg' },
      { uname: '安其拉', imgSrc: './images/9.5/07.jpg' },
      { uname: '李白', imgSrc: './images/9.5/08.jpg' },
      { uname: '阿珂', imgSrc: './images/9.5/09.jpg' },
      { uname: '墨子', imgSrc: './images/9.5/10.jpg' },
      { uname: '鲁班', imgSrc: './images/9.5/11.jpg' },
      { uname: '嬴政', imgSrc: './images/9.5/12.jpg' },
      { uname: '孙膑', imgSrc: './images/9.5/13.jpg' },
      { uname: '周瑜', imgSrc: './images/9.5/14.jpg' },
      { uname: '老夫子', imgSrc: './images/9.5/15.jpg' },
      { uname: '狄仁杰', imgSrc: './images/9.5/16.jpg' },
      { uname: '扁鹊', imgSrc: './images/9.5/17.jpg' },
      { uname: '马可波罗', imgSrc: './images/9.5/18.jpg' },
      { uname: '露娜', imgSrc: './images/9.5/19.jpg' },
      { uname: '孙悟空', imgSrc: './images/9.5/20.jpg' },
      { uname: '黄忠', imgSrc: './images/9.5/21.jpg' },
      { uname: '百里玄策', imgSrc: './images/9.5/22.jpg' },
    ]
    // 需求1
    // 1. 注册input事件
    // 2. 将文本的内容的长度赋值给对应的数值
    // 3. 表单的maxlength属性可以直接限制在200个数之间
    //思路:
    //1.获取文本内容textarea标签和控制字数变化的useCount盒子标签
    let textarea = document.querySelector('textarea')
    let useCount = document.querySelector('#useCount')
    //2.事件监听,input事件,文本内容值得长度 value.length发生变化
    textarea.addEventListener('input',function(){
      // console.log(textarea.value.length)
      useCount.innerHTML = textarea.value.length
      //优化:若用户输入多个空格,控制字数变化的useCount的值为0
      //判断用户输入字符为空或空格,修改控制字数变化字符的innerHTML设置为0
      if(textarea.value.trim() === ''){
          useCount.innerHTML = 0
      }
       
      })
     
     //需求2
      // 克隆预定义好的模板,将模板的hidden属性设置为false, 并最终展示到页面上
      // 判断如果内容为空,则提示不能输入为空, 并且直接return
      // 防止输入无意义空格, 使用字符串.trim()去掉首尾空格, 并将表单的value值设置为空字符串
      //总体思路:
      //点击发布,判断输入内容不能为空,并将去掉首尾空格, 并将表单的value值设置为空字符串
      //详细思路
      //1.获取发送按钮标签
     
      //2.事件监听,点击发布,事件监听用户输入内容value值是否为空 ,返回提示输入输入内容不能为空,请重新输入的提示;
      //3.使用字符串.trim()去掉首尾空格, 并将表单的value值设置为空字符串
      let send =document.querySelector('button')
      send.addEventListener('click',function(){
        if(textarea.value.trim() === ''){
          //细节:
          //1.为什么要去空格?
          //若输入多个空格,点击发布也无法判断输入为空,不能给出输入不能为空,请重新输入的提示;
          //2.去除空格方法,字符串.trim()
          //   防止输入无意义空格, 使用字符串.trim()去掉首尾空格
          // console.log('  str')
          // console.log('  str '.trim())
          return alert('输入内容不能为空,请重新输入')
      }
      
      //需求3
      //获取文本域的内容, 赋值给由模板克隆出来的新标签里面的content.innerText
      // 随机获取数据数组里面的内容, 替换newNode的图片和名称
      // 利用时间对象将时间动态化 new Date().toLocaleString()
      //业务明确:必须是用户输入内容并点击发布后,在页面才能显示用户自己发布的各种信息,所以这个需求应当写在监听微博发布事件函数内,即:这个需求写在需求2的发布微博事件监听中
      //1.获取父级标签,在父级ul中创建并后面追加li标签和文本内容
      //1.1.获取父级标签
      let list = document.querySelector('#list')
      //错误:1.1创建父节点list的ul盒子和子节点li
      // let list = document.createElement('#list')
      //1.2.创建子节点
      let li = document.createElement('li')
       //2.声明和调用随机函数,生成微博发布后的随机用户信息
       //2.1.声明随机函数
       function randomGet (min,max){
              return Math.floor(Math.random()*(max- min + 1))+min
        }
        //2.2.调用函数
        let random = randomGet(0,dataArr.length-1)
      //3.修改li标签的文本内容,随机替换新子节点的文本内容,利用时间对象将时间动态化
       li.innerHTML = `
    <div class="info">
      <img class="userpic" src = ${dataArr[random].imgSrc} />
      <span class="username">${dataArr[random].uname}</span>
      <p class="send-time">${new Date().toLocaleString()}</p>
    </div>
    <div class="content">${textarea.value}</div>
    <span class="the_del">X</span>`
      // 4.追加节点  父元素.insertBefore(子元素, 放到那个元素的前面)
      list.insertBefore(li,list.children[0])
      //需求5
      // 将表单域内容重置为空
      // 将userCount里面的内容重置为0
      //1.将输入内容定为空字符串
      textarea.value = ''
      //2.将控制输入字数字符的innerHTML定为0
      useCount.innerHTML = 0
      // 需求4
      // 在事件处理函数里面获取点击按钮,注册点击事件
      // (易错点: 必须在事件里面获取,外面获取不到)
      // 删除对应的元素 (通过this获取对应的那条需要删除的元素)
      //业务明确:只有在生成微博内容的li后,才会有删除标志X的标签
      //1.获取删除标签,注册点击删除事件,其中删除事件是移除li标签以及里面的所有内容
      let del = document.querySelector('.the_del')
      del.addEventListener('click',function(){
        //2.删除节点的语法 父元素.removeChild(子元素)
        list.removeChild(li)
        //错误示例:父元素.removeChild(父元素.chilren[子元素数组序号])
        // list.removeChild(list.children[random])
      })
  })
  </script>
</body>

</html>

8.综合案例-购物车

8.1.需求

8.1.1.需求页面

在这里插入图片描述

8.1.2.需求明确

1.点击加号事件

点击加号事件,输入框数字变化且自增,同时启用减号,小计内的价格也发生变化,已经选中的总商品数量和总价也发生变化;

2.点击减号事件

点击减号,输入框数字变化且自减,小计内的价格也发生变化,已经选中的总商品数量和总价也发生变化,当单件商品数量减到0时,禁用减号;

3.点击删除事件

点击删除,输入框数字变化且自增,同时启用减号,小计内的价格也发生变化,已经选中的总商品数量和总价也发生变化,当总商品数量,禁用删除;

4.商品数量总量和总价计算

在点击加号、减号和删除商品后,商品总数和商品总价发生变化

8.2.素材

HTML+CSS部分
HTML

 <div class="car">
    <table>
      <thead>
        <tr>
          <th><input type="checkbox" id="all" />全选</th>
          <th>商品</th>
          <th>单价</th>
          <th>商品数量</th>
          <th>小计</th>
          <th>操作</th>
        </tr>
      </thead>
      <tbody id="carBody">
        <tr>
          <td>
            <input class="s_ck" type="checkbox" readonly />
          </td>
          <td>
            <img src="./images/01.jpg" />
            <p>牛奶</p>
          </td>
          <td class="price">5¥</td>
          <td>
            <div class="count-c clearfix">
              <button class="reduce" disabled>-</button>
              <input type="text" value="1" />
              <button class="add">+</button>
            </div>
          </td>
          <td class="total">5¥</td>
          <td>
            <a href="javascript:" class="del">删除</a>
          </td>
        </tr>
        <tr>
          <td>
            <input class="s_ck" type="checkbox" />
          </td>
          <td>
            <img src="./images/01.jpg" />
            <p>牛奶</p>
          </td>
          <td class="price">10¥</td>
          <td>
            <div class="count-c clearfix">
              <button class="reduce" disabled>-</button>
              <input type="text" value="1" />
              <button class="add">+</button>
            </div>
          </td>
          <td class="total">20¥</td>
          <td>
            <a href="javascript:" class="del">删除</a>
          </td>
        </tr>
        <tr>
          <td>
            <input class="s_ck" type="checkbox" />
          </td>
          <td>
            <img src="./images/01.jpg" />
            <p>牛奶</p>
          </td>
          <td class="price">20¥</td>
          <td>
            <div class="count-c clearfix">
              <button class="reduce" disabled>-</button>
              <input type="text" value="1" />
              <button class="add">+</button>
            </div>
          </td>
          <td class="total">40¥</td>
          <td>
            <a href="javascript:" class="del">删除</a>
          </td>
        </tr>
        <tr>
          <td>
            <input class="s_ck" type="checkbox" />
          </td>
          <td>
            <img src="./images/01.jpg" />
            <p>牛奶</p>
          </td>
          <td class="price">35¥</td>
          <td>
            <div class="count-c clearfix">
              <button class="reduce" disabled>-</button>
              <input type="text" value="1" />
              <button class="add">+</button>
            </div>
          </td>
          <td class="total">70¥</td>
          <td>
            <a href="javascript:" class="del">删除</a>
          </td>
        </tr>
      </tbody>
    </table>
    <div class="controls clearfix">
      <a href="javascript:" class="del-all">删除所选商品</a>
      <a href="javascript:" class="clear">清理购物车</a>
      <a href="javascript:" class="pay">去结算</a>
      <p>
        已经选中<span id="totalCount">0</span>件商品;总价:<span id="totalPrice" class="total-price">0¥</span>
      </p>
    </div>
  </div>

CSS

<style>
    * {
      margin: 0;
      padding: 0;
    }

    ul {
      list-style: none;
    }

    a {
      text-decoration: none;
      color: #666;
    }

    body {
      background: #fff;
      color: #666;
      font-size: 14px;
    }

    input {
      outline: none;
    }

    .clearfix::before,
    .clearfix::after {
      content: '';
      display: block;
      clear: both;
    }

    .clearfix {
      *zoom: 1;
    }
  </style>
  <!-- 引入购物车样式 -->
  <style>
    table {
      width: 800px;
      margin: 0 auto;
      border-collapse: collapse;
    }

    th {
      font: normal 14px/50px '宋体';
      color: #666;
    }

    th,
    td {
      border: none;
      text-align: center;
      border-bottom: 1px dashed #ccc;
    }

    input[type='checkbox'] {
      width: 13px;
      height: 13px;
    }

    tbody p {
      position: relative;
      bottom: 10px;
    }

    tbody .add,
    tbody .reduce {
      float: left;
      width: 22px;
      height: 22px;
      border: 1px solid #ccc;
      text-align: center;
      background: none;
      outline: none;
      cursor: pointer;
    }

    tbody input[type='text'] {
      width: 50px;
      float: left;
      height: 18px;
      text-align: center;
    }

    tbody .count-c {
      width: 98px;
      margin: 0 auto;
    }

    button[disabled] {
      color: #ddd;
      cursor: not-allowed;
    }

    tbody tr:hover {
      background: #eee;
    }

    tbody tr.active {
      background: rgba(241, 209, 149, 0.945);
    }

    .controls {
      width: 790px;
      margin: 10px auto;
      border: 1px solid #ccc;
      line-height: 50px;
      padding-left: 10px;
      position: relative;
    }

    .controls .del-all,
    .controls .clear {
      float: left;
      margin-right: 50px;
    }

    .controls p {
      float: right;
      margin-right: 100px;
    }

    .controls span {
      color: red;
    }

    .controls .pay {
      position: absolute;
      right: 0;
      width: 80px;
      height: 54px;
      background: red;
      font: bold 20px/54px '宋体';
      color: #fff;
      text-align: center;
      bottom: -1px;
    }

    .controls .total-price {
      font-weight: bold;
    }
  </style>

8.3.素材分析

在这里插入图片描述
car这个大盒子包着table 标签和controls类盒子。table标签包着thead和tbody标签。thead标签里面tr包着商品信息和用户操作行为的内容标题信息。tbody标签中包着4个tr,每个tr包着若干个td,这里的td主要包裹着商品数据和用户对商品的操作(比如:加减商品数量、删除商品和选定商品)。controls类盒子包裹着用户对所有商品的操作信息的盒子(比如:删除所选商品、清理购物车、选中商品数量和价格、以及去支付页面结算)

8.4.我的代码与实现思路(JavaScript部分)

<script>
    //0.获取标签
    //获取加号标签
    let add = document.querySelectorAll('.add')
    //获取减号标签
    let reduce = document.querySelectorAll('.reduce')
    //获取删除标签
    let del = document.querySelectorAll('.del')
    //获取商品数量输入框标签input
    let input = document.querySelectorAll('.count-c input')
    //获取商品单价标签
    let price = document.querySelectorAll('.price')
    //获取当前商品小计价格标签
    let total = document.querySelectorAll('.total')
    //获取商品总数标签
    let totalCount = document.querySelector('#totalCount')
    //获取商品总价标签
    let totalPrice = document.querySelector('#totalPrice')
    //获取删除商品数据(即每个商品数据有关的tr的父级)
    let tbody = document.querySelector('#carBody')
      //测试获取标签代码
      // alert(111)
      // console.log(input)
      //console.log(add)
      // console.log(totalCount)
      // console.log(totalPrice)
    //1.点击加号事件
    //点击加号事件,输入框数字变化且自增,同时启用减号,小计内的价格也发生变化,已经选中的总商品数量和总价也发生变化;
    for(let i = 0 ; i < add.length; i++){
      // 总价和单价是一样的
      //total[i].innerText = price[i].innerText
      // 1.1.注册加号点击事件,修改商品数量有关输入框input数组对象的里面的值value属性发生自增变化、启用减号和商品小计变化
      add[i].addEventListener('click',function(){  
    //测试input的value属性自增变化代码
    // console.log(input.value++)
    // console.log(input.value)
    // console.log(input[i].value)
    // console.log(input[i].value++)
    //1.2.点击加号,商品数量自增
    //input[i].value的值是字符串(String类型),需要将其转换为数字型或整数型
    input[i].value = parseInt(input[i].value)
    input[i].value ++
    //1.3点击加号后启用减号
    reduce[i].disabled = false
    //1.4.商品小计变化模块
    //测试观察总计的值
    // console.log(typeof(price[i].innerHTML))
    // console.log(typeof(input[i].value))
    console.log(parseInt(input[i].value ))
    console.log(parseInt(price[i].innerHTML))
    // console.log(parseInt(price[i].innerHTML * input[i].value ))
    //  console.log(parseInt(price[i].innerHTML)*parseInt(input[i].value)) 
    total[i].innerHTML=`${parseInt(price[i].innerHTML)*input[i].value}`
    
    
    //1.5调用商品总件数和总价模块
    totalAccount ()   
      }  
    ) 
   }
    //2.点击减号事件
    // 点击减号,输入框数字变化且自减,小计内的价格也发生变化,已经选中的总商品数量和总价也发生变化,当单件商品数量减到0时,禁用减号;
    for (let i = 0 ; i < reduce.length; i++){
      // 总价和单价是一样的
      // total[i].innerText = price[i].innerText
      // 2.1.注册减号点击事件,修改商品数量有关输入框input数组对象的里面的值value属性发生自增变化、商品数量为0禁用减号和商品小计变化
      reduce[i].addEventListener('click',function(){
    input[i].value = parseInt(input[i].value)
    //测试观察input[i].value的值
    // console.log(input[i].value) 
    input[i].value--
    //2.2.当商品数量为0,就禁用减号
    if (input[i].value <= 0){
      reduce[i].disabled = true
    }
    //2.3.商品小计计算
    total[i].innerHTML=`${parseInt(price[i].innerHTML)*input[i].value}`
    //2.4.调用商品总件数和总价模块
    totalAccount ()
    
        }   
      ) 
    }
    //3.点击删除事件
    // 点击删除,商品数据(即每个商品数据有关的tr)被删除,同时商品总数量和总价也发生变化
    // 复习
    // // 需求:点击按钮,删除li
    //     //1.获取标签 事件触发按钮button和要删除的子级有关的父级ul
    //     let btn = document.querySelector('button')
    //     let ul = document.querySelector('ul')
    //     //2.注册事件,删除子级节点
    //     btn.addEventListener('click',function(){
    //         //删除节点的语法 父元素.removeChild(子元素)
    //         ul.removeChild(ul.children[0])
    //     })
      for(let i = 0 ; i < del.length ; i++){
        // console.log(del[i]);
        // console.log(tbody.children[i]) 
        // 总价和单价是一样的
      // total[i].innerText = price[i].innerText
        //3.1.点击删除,商品数据(即每个商品数据有关的tr)被删除,     
        del[i].addEventListener('click',function(){
          //删除tr,它的父级是tbody
           tbody.removeChild(tbody.children[0])
         //3.2重新计算删除某个商品后的商品总数和小计总价
          input[i].value = parseInt(input[i].value)
          input[i].value--
          total[i].innerHTML=`${parseInt(price[i].innerHTML)*input[i].value}`
          
          //3.3.同时商品总数量和总价也发生变化调用商品总件数和总价模块
          totalAccount ()
        })
       
      }
    //4.商品总件数和总价模块
   function totalAccount (){
    // let total = document.querySelectorAll('.total')
    // let input = document.querySelectorAll('.count-c input')
    //4.1.定义和初始化变量numProduct存放总计商品数量,变量priceTotal存放商品总价
    let numProduct = 0
    let priceTotal = 0
    //4.2.循环遍历输入框,计算每个对应商品数量输入框标签input的商品总数和总价
     for(let i = 0 ; i < input.length; i++){
      
      //全局变量与局部变量问题
      // let num = 0
      // console.log(input[i].value)
      numProduct  += parseInt(input[i].value)
      // console.log(parseInt(total[i].innerHTML))
      priceTotal  += parseInt(total[i].innerHTML)
     }
    //  console.log(numProduct)
    //4.3.将计算的商品总数和总价写入相应的有关标签
    //写入商品总数有关标签
    totalCount.innerHTML = `${numProduct}`
    //  console.log(priceTotal)
    //写入商品总价有关标签
    totalPrice.innerHTML = `${priceTotal}`
   } 
   //4.4.调用商品总件数和总价模块函数,注意:函数名不能与变量名有冲突
   totalAccount ()
    //商品总件数和总价模块代码思路
    // let numProduct = 0
    // let priceTotal = 0
    //  for(let i = 0 ; i < input.length; i++){
    //   //全局变量与局部变量问题
    //   // let num = 0
    //   // console.log(input[i].value)
    //   numProduct  += parseInt(input[i].value)
    //   // console.log(parseInt(total[i].innerHTML))
    //   priceTotal  += parseInt(total[i].innerHTML)
    //  }
    // //  console.log(numProduct)
    // totalCount.innerHTML = `${numProduct}`
    // //  console.log(priceTotal)
    // totalPrice.innerHTML = `${priceTotal}¥`

点击加号
在这里插入图片描述
点击减号
在这里插入图片描述
在这里插入图片描述

点击删除
在这里插入图片描述

8.5.优化

8.5.1.bug分析与修复

1.编写代码过程遇到的bug
(1)调用数组对象属性方法出错

如:input的value属性
有关编写测试代码

错误: console.log(input.value++)
    console.log(input.value)

在这里插入图片描述
修正:
商品数量输入框input标签是document.querySelectorAll获取,得到的一个伪数组NodeList,同时也是一个数组对象,value是数组中一个dom对象属性,因此书写时要带索引号即input[i]

 console.log(input)
console.log(input[i].value)
    console.log(input[i].value++)

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

(2)无法修改小计中的价格

原因:没有将单价和商品输入框里面的值修改为数字型或整型
有关测试代码
错误用例:

console.log(typeof(price[i].innerHTML))
    console.log(typeof(input[i].value))

在这里插入图片描述
修复:
parseInt获取字符串前面的数字

 console.log(parseInt(input[i].value ))
 console.log(parseInt(price[i].innerHTML))   total[i].innerHTML=`${parseInt(price[i].innerHTML)*input[i].value}`

在这里插入图片描述

(3)遗忘删除节点操作

复习

//复习
    // 需求:点击按钮,删除li
        //1.获取标签 事件触发按钮button和要删除的子级有关的父级ul
        let btn = document.querySelector('button')
        let ul = document.querySelector('ul')
        //2.注册事件,删除子级节点
        btn.addEventListener('click',function(){
            //删除节点的语法 父元素.removeChild(子元素)
            ul.removeChild(ul.children[0])
        })
(4)作用域问题与for循环输出结果预判出错问题——商品总件数和总价模块部分

错误:

一开始编写这个函数时,把商品总数的有关变量初始化和修改有关标签写在for循环里面,导致商品总件数输出的每个商品各自的个数,不是所有商品的总件数,且数据为字符串类型进行计算

    //4.商品总件数和总价模块
   function totalAccount (){
    // let total = document.querySelectorAll('.total')
    // let input = document.querySelectorAll('.count-c input')
    //4.1.定义和初始化变量numProduct存放总计商品数量,变量priceTotal存放商品总价
    
    //4.2.循环遍历输入框,计算每个对应商品数量输入框标签input的商品总数和总价
     for(let i = 0 ; i < input.length; i++){
      let numProduct = 0
      let priceTotal = 0
      //全局变量与局部变量问题
      // let numProduct = 0
      // console.log(input[i].value)
      numProduct  += input[i].value
      console.log(parseInt(total[i].innerHTML))
      priceTotal  += total[i].innerHTML
        console.log(numProduct)
    //4.3.将计算的商品总数和总价写入相应的有关标签
    //写入商品总数有关标签
    totalCount.innerHTML = `${numProduct}`
     console.log(priceTotal)
    //写入商品总价有关标签
    totalPrice.innerHTML = `${priceTotal}`
     }
   
   } 
  

在这里插入图片描述
修复:
分析:
1.我们要得到的计算所有商品总数和商品总价的计算结果,循环内的代码执行是对每种商品的总件数和总价的计算和标签结果文本修改,并不是针对所有商品,而循环输出后的结果是计算所有商品的总件数和总价和标签结果文本修改,且数据为数字类型,需要进行类型转换;
2.由于一开始素材中每件商品件数不为0,若在循环体内声明商品件数和商品总价有关变量为局部变量,则会让有关变量置为循环体中设置的初始化的值,且在循环体外无法输出结果。

修复代码:

 //4.商品总件数和总价模块
   function totalAccount (){
    // let total = document.querySelectorAll('.total')
    // let input = document.querySelectorAll('.count-c input')
    //4.1.定义和初始化变量numProduct存放总计商品数量,变量priceTotal存放商品总价
    let numProduct = 0
    let priceTotal = 0
    //4.2.循环遍历输入框,计算每个对应商品数量输入框标签input的商品总数和总价
     for(let i = 0 ; i < input.length; i++){
      
      //全局变量与局部变量问题
      // let num = 0
      // console.log(input[i].value)
      numProduct  += parseInt(input[i].value)
      // console.log(parseInt(total[i].innerHTML))
      priceTotal  += parseInt(total[i].innerHTML)
     }
    //  console.log(numProduct)
    //4.3.将计算的商品总数和总价写入相应的有关标签
    //写入商品总数有关标签
    totalCount.innerHTML = `${numProduct}`
    //  console.log(priceTotal)
    //写入商品总价有关标签
    totalPrice.innerHTML = `${priceTotal}`
   } 
   //4.4.调用商品总件数和总价模块函数,注意:函数名不能与变量名有冲突
   totalAccount ()

在这里插入图片描述

2.代码存在的bug

1.只能从第一个元素正确执行删除商品操作
相关错误代码1

for(let i = 0 ; i < del.length ; i++){
        // console.log(del[i]);
        // console.log(tbody.children[i]) 
        //3.1.点击删除,商品数据(即每个商品数据有关的tr)被删除,     
        del[i].addEventListener('click',function(){
          tbody.removeChild(tbody.children[0])
          // tbody.removeChild(this.parentNode.parentNode)
          //3.2重新计算删除所有商品后的总商品数和总价
          input[i].value = parseInt(input[i].value)
          input[i].value--
          total[i].innerHTML=`${parseInt(price[i].innerHTML)*input[i].value}`
          
          //3.3.同时商品总数量和总价也发生变化调用商品总件数和总价模块
          totalAccount ()
        })
       
      }

运行结果:
点击删除单价10元的商品,可是结果删除的5元的商品,且10元的商品单价和件数结果被置为0
在这里插入图片描述
相关错误代码2

for(let i = 0 ; i < del.length ; i++){
        // console.log(del[i]);
        // console.log(tbody.children[i]) 
        //3.1.点击删除,商品数据(即每个商品数据有关的tr)被删除,     
        del[i].addEventListener('click',function(){
          tbody.removeChild(tbody.children[i])
          // tbody.removeChild(this.parentNode.parentNode)
          //3.2重新计算删除所有商品后的总商品数和总价
          input[i].value = parseInt(input[i].value)
          input[i].value--
          total[i].innerHTML=`${parseInt(price[i].innerHTML)*input[i].value}`
          
          //3.3.同时商品总数量和总价也发生变化调用商品总件数和总价模块
          totalAccount ()
        })
       
      }

运行结果:
在这里插入图片描述

修复后的代码
错误代码2是tbody子级tr被删除后,它的索引号发生变化,导致删除时无法找到这个元素

1.核心代码与原理

  tbody.removeChild(this.parentNode.parentNode)

删除当前事件监听元素的爸爸的爸爸
this指向删除(del)这个类标签,它的父级是td,td的父级是tr,tr是tbody要删除的子级,也可四个tr都被循环获取后,再将其被删除。

2.完整代码

 for(let i = 0 ; i < del.length ; i++){
        // console.log(del[i]);
        // console.log(tbody.children[i]) 
        //3.1.点击删除,商品数据(即每个商品数据有关的tr)被删除,     
        del[i].addEventListener('click',function(){
          // tbody.removeChild(tbody.children[0])
          tbody.removeChild(this.parentNode.parentNode)
          //3.2重新计算删除所有商品后的总商品数和总价
          input[i].value = parseInt(input[i].value)
          input[i].value--
          total[i].innerHTML=`${parseInt(price[i].innerHTML)*input[i].value}`
          
          //3.3.同时商品总数量和总价也发生变化调用商品总件数和总价模块
          totalAccount ()
        })
       
      }

点击删除单价为10元的商品数据
在这里插入图片描述

2.由于素材中的数据是有不正确的计算结果,计算总价的起始结果不正确。

修复措施:
让总价和单价一致

//总价和单价是一样的
      total[i].innerText = price[i].innerText

0

8.5.2.参考代码与我的代码对比分析与我的代码优化

参考代码:

 <script>
    // + - 删除是相同的,一一对应的 我们完全可以用一个for来遍历绑定事件
    // +
    let adds = document.querySelectorAll('.add')
    // -
    let reduces = document.querySelectorAll('.reduce')
    // del
    let dels = document.querySelectorAll('.del')
    // 输入框input
    let inputs = document.querySelectorAll('.count-c input')

    // 单价 price  5
    let prices = document.querySelectorAll('.price')
    // 小计 total  5 * 2 = 10
    let totals = document.querySelectorAll('.total')
    // 总价元素获取
    let totalResult = document.querySelector('.total-price')
    // 总的数量获取
    let totalCount = document.querySelector('#totalCount')
    // tbody 获取过来
    let carBody = document.querySelector('#carBody')
    for (let i = 0; i < adds.length; i++) {
      // 总价和单价是一样的
      totals[i].innerText = prices[i].innerText
      //1. 加号的操作
      adds[i].addEventListener('click', function () {
        // 点击了谁,就让对应的输入框自增就行了
        inputs[i].value++
        // 减号要启用
        reduces[i].disabled = false
        // prices[i].innerText  得到的是 5¥     parseInt('5¥')  === 5
        console.log(parseInt(prices[i].innerText))
        // 计算小计模块 
        // totals[i].innerText =  单价 * 数量  
        // totals[i].innerText = 20
        totals[i].innerText = parseInt(prices[i].innerText) * inputs[i].value + '¥'
        // 计算现在的总额 调用
        result()
      })

      //2. 减号的操作
      reduces[i].addEventListener('click', function () {
        // 点击了谁,就让对应的输入框自增就行了
        inputs[i].value--
        // prices[i].innerText  得到的是 5¥     parseInt('5¥')  === 5
        // console.log(parseInt(prices[i].innerText))
        // 判断如果表单里面的值 小于等于1 则,禁用按钮
        if (inputs[i].value <= 1) {
          this.disabled = true
        }
        // 计算小计模块 
        // totals[i].innerText =  单价 * 数量  
        // totals[i].innerText = 20
        totals[i].innerText = parseInt(prices[i].innerText) * inputs[i].value + '¥'

        // 计算现在的总额 调用
        result()
      })


      // 3. 删除操作

      dels[i].addEventListener('click', function () {
        // 父元素.removeChild(子元素)  
        // 我们要删除的是那个元素   tr  他的爸爸是 tbody
        // 删除的是当前元素爸爸的爸爸  就是 tr 就是当前的tr
        carBody.removeChild(this.parentNode.parentNode)

        // 调用总计模块
        result()
      })
    }


    // div  span   ul  li  标签  有文字内容  怎么得到或则设置文字内容呢  元素.innerText   元素.innerHTML
    // 表单  input 单选 复选    textarea  select  怎么得到或则设置值   表单的value 
    // 特殊的  button 是通过inner来设置
    // 以前数组求和的方式 累加
    //  计算总价 result 函数  把所有的小计 totals 加起来的结果
    function result() {
      // 小计 total  5 * 2 = 10
      let totals = document.querySelectorAll('.total')
      // 输入框input
      let inputs = document.querySelectorAll('.count-c input')
      let sum = 0
      let num = 0
      for (let i = 0; i < totals.length; i++) {
        // sum = sum + 小计的数字  10¥
        sum = sum + parseInt(totals[i].innerText)
        num = num + parseInt(inputs[i].value)
      }
      // console.log(sum)
      totalResult.innerText = sum + '¥'
      // console.log(num)
      totalCount.innerText = num
    }
    result()
  </script>
1.删除商品数据后总价变化

我的代码是在删除后,在删除商品模块中重新计算删除某个商品后的商品总数和小计总价。

  for(let i = 0 ; i < del.length ; i++){
        // console.log(del[i]);
        // console.log(tbody.children[i]) 
        // 总价和单价是一样的
      total[i].innerText = price[i].innerText
        //3.1.点击删除,商品数据(即每个商品数据有关的tr)被删除,     
        del[i].addEventListener('click',function(){
          //删除tr,它的父级是tbody
          // tbody.removeChild(tbody.children[0])
          tbody.removeChild(tbody.children[i])
          //删除当前事件监听元素的爸爸的爸爸
          // this指向删除(del)这个类标签,它的父级是td,td的父级是tr,tr是tbody要删除的子级   
          // tbody.removeChild(this.parentNode.parentNode)
          //3.2重新计算删除某个商品后的商品总数和小计总价
          input[i].value = parseInt(input[i].value)
          input[i].value--
          total[i].innerHTML=`${parseInt(price[i].innerHTML)*input[i].value}`
          
          //3.3.同时商品总数量和总价也发生变化调用商品总件数和总价模块
          totalAccount ()
        })

参考代码中由于为了避免在页面执行删除操作刷新后页面获取的商品件数和小计得到的还是之前未删除前4个的数据,就在商品总件数和总价模块中重新获取商品被删除后的件数和小计,从而计算商品总件数和总价。

function totalAccount () {
      // 小计 total  5 * 2 = 10
      let totals = document.querySelectorAll('.total')
      // 输入框input
      let inputs = document.querySelectorAll('.count-c input')
      let sum = 0
      let num = 0
      for (let i = 0; i < totals.length; i++) {
        // sum = sum + 小计的数字  10¥
        sum = sum + parseInt(totals[i].innerText)
        num = num + parseInt(inputs[i].value)
      }
      // console.log(sum)
      totalResult.innerText = sum + '¥'
      // console.log(num)
      totalCount.innerText = num
    }
2.合并加号、减号和删除事件的循环

由于执行加号、减号和删除事件都是循环执行且都是对同一列数据操作,因此可以将这三个需求写在同一个循环中。

 for(let i = 0 ; i < add.length; i++){
      // 总价和单价是一样的
      total[i].innerText = price[i].innerText
      // 1.1.注册加号点击事件,修改商品数量有关输入框input数组对象的里面的值value属性发生自增变化、启用减号和商品小计变化
      add[i].addEventListener('click',function(){  
    //测试input的value属性自增变化代码
    // console.log(input.value++)
    // console.log(input.value)
    // console.log(input[i].value)
    // console.log(input[i].value++)
    //1.2.点击加号,商品数量自增
    //input[i].value的值是字符串(String类型),需要将其转换为数字型或整数型
    input[i].value = parseInt(input[i].value)
    input[i].value ++
    //1.3点击加号后启用减号
    reduce[i].disabled = false
    //1.4.商品小计变化模块
    //测试观察总计的值
    // console.log(typeof(price[i].innerHTML))
    // console.log(typeof(input[i].value))
    console.log(parseInt(input[i].value ))
    console.log(parseInt(price[i].innerHTML))
    // console.log(parseInt(price[i].innerHTML * input[i].value ))
    //  console.log(parseInt(price[i].innerHTML)*parseInt(input[i].value)) 
    total[i].innerHTML=`${parseInt(price[i].innerHTML)*input[i].value}`
    
    
    //1.5调用商品总件数和总价模块
    totalAccount ()   
      }  
    ) 
     //2.点击减号事件
    // 点击减号,输入框数字变化且自减,小计内的价格也发生变化,已经选中的总商品数量和总价也发生变化,当单件商品数量减到0时,禁用减号;
      // 2.1.注册减号点击事件,修改商品数量有关输入框input数组对象的里面的值value属性发生自增变化、商品数量为0禁用减号和商品小计变化
      reduce[i].addEventListener('click',function(){
    input[i].value = parseInt(input[i].value)
    //测试观察input[i].value的值
    // console.log(input[i].value) 
    input[i].value--
    //2.2.当商品数量为0,就禁用减号
    if (input[i].value <= 0){
      reduce[i].disabled = true
    }
    //2.3.商品小计计算
    total[i].innerHTML=`${parseInt(price[i].innerHTML)*input[i].value}`
    //2.4.调用商品总件数和总价模块
    totalAccount ()
    
        }   
      ) 
//3.点击删除事件
    // 点击删除,商品数据(即每个商品数据有关的tr)被删除,同时商品总数量和总价也发生变化
    // 复习
    // // 需求:点击按钮,删除li
    //     //1.获取标签 事件触发按钮button和要删除的子级有关的父级ul
    //     let btn = document.querySelector('button')
    //     let ul = document.querySelector('ul')
    //     //2.注册事件,删除子级节点
    //     btn.addEventListener('click',function(){
    //         //删除节点的语法 父元素.removeChild(子元素)
    //         ul.removeChild(ul.children[0])
    //     })
       //3.1.点击删除,商品数据(即每个商品数据有关的tr)被删除,     
   del[i].addEventListener('click',function(){
          //删除tr,它的父级是tbody
          // tbody.removeChild(tbody.children[0])
          // tbody.removeChild(tbody.children[i])
          //删除当前事件监听元素的爸爸的爸爸
          // this指向删除(del)这个类标签,它的父级是td,td的父级是tr,tr是tbody要删除的子级   
          tbody.removeChild(this.parentNode.parentNode)
          
          
          //3.2.同时商品总数量和总价也发生变化调用商品总件数和总价模块
          totalAccount ()
        })
   }

8.6.优化后的完整代码

<!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>购物车全选功能</title>
  <!-- 引入初始化 -->
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    ul {
      list-style: none;
    }

    a {
      text-decoration: none;
      color: #666;
    }

    body {
      background: #fff;
      color: #666;
      font-size: 14px;
    }

    input {
      outline: none;
    }

    .clearfix::before,
    .clearfix::after {
      content: '';
      display: block;
      clear: both;
    }

    .clearfix {
      *zoom: 1;
    }
  </style>
  <!-- 引入购物车样式 -->
  <style>
    table {
      width: 800px;
      margin: 0 auto;
      border-collapse: collapse;
    }

    th {
      font: normal 14px/50px '宋体';
      color: #666;
    }

    th,
    td {
      border: none;
      text-align: center;
      border-bottom: 1px dashed #ccc;
    }

    input[type='checkbox'] {
      width: 13px;
      height: 13px;
    }

    tbody p {
      position: relative;
      bottom: 10px;
    }

    tbody .add,
    tbody .reduce {
      float: left;
      width: 22px;
      height: 22px;
      border: 1px solid #ccc;
      text-align: center;
      background: none;
      outline: none;
      cursor: pointer;
    }

    tbody input[type='text'] {
      width: 50px;
      float: left;
      height: 18px;
      text-align: center;
    }

    tbody .count-c {
      width: 98px;
      margin: 0 auto;
    }

    button[disabled] {
      color: #ddd;
      cursor: not-allowed;
    }

    tbody tr:hover {
      background: #eee;
    }

    tbody tr.active {
      background: rgba(241, 209, 149, 0.945);
    }

    .controls {
      width: 790px;
      margin: 10px auto;
      border: 1px solid #ccc;
      line-height: 50px;
      padding-left: 10px;
      position: relative;
    }

    .controls .del-all,
    .controls .clear {
      float: left;
      margin-right: 50px;
    }

    .controls p {
      float: right;
      margin-right: 100px;
    }

    .controls span {
      color: red;
    }

    .controls .pay {
      position: absolute;
      right: 0;
      width: 80px;
      height: 54px;
      background: red;
      font: bold 20px/54px '宋体';
      color: #fff;
      text-align: center;
      bottom: -1px;
    }

    .controls .total-price {
      font-weight: bold;
    }
  </style>
</head>

<body>
  <div class="car">
    <table>
      <thead>
        <tr>
          <th><input type="checkbox" id="all" />全选</th>
          <th>商品</th>
          <th>单价</th>
          <th>商品数量</th>
          <th>小计</th>
          <th>操作</th>
        </tr>
      </thead>
      <tbody id="carBody">
        <tr>
          <td>
            <input class="s_ck" type="checkbox" readonly />
          </td>
          <td>
            <img src="./images/01.jpg" />
            <p>牛奶</p>
          </td>
          <td class="price">5¥</td>
          <td>
            <div class="count-c clearfix">
              <button class="reduce" disabled>-</button>
              <input type="text" value="1" />
              <button class="add">+</button>
            </div>
          </td>
          <td class="total">5¥</td>
          <td>
            <a href="javascript:" class="del">删除</a>
          </td>
        </tr>
        <tr>
          <td>
            <input class="s_ck" type="checkbox" />
          </td>
          <td>
            <img src="./images/01.jpg" />
            <p>牛奶</p>
          </td>
          <td class="price">10¥</td>
          <td>
            <div class="count-c clearfix">
              <button class="reduce" disabled>-</button>
              <input type="text" value="1" />
              <button class="add">+</button>
            </div>
          </td>
          <td class="total">20¥</td>
          <td>
            <a href="javascript:" class="del">删除</a>
          </td>
        </tr>
        <tr>
          <td>
            <input class="s_ck" type="checkbox" />
          </td>
          <td>
            <img src="./images/01.jpg" />
            <p>牛奶</p>
          </td>
          <td class="price">20¥</td>
          <td>
            <div class="count-c clearfix">
              <button class="reduce" disabled>-</button>
              <input type="text" value="1" />
              <button class="add">+</button>
            </div>
          </td>
          <td class="total">40¥</td>
          <td>
            <a href="javascript:" class="del">删除</a>
          </td>
        </tr>
        <tr>
          <td>
            <input class="s_ck" type="checkbox" />
          </td>
          <td>
            <img src="./images/01.jpg" />
            <p>牛奶</p>
          </td>
          <td class="price">35¥</td>
          <td>
            <div class="count-c clearfix">
              <button class="reduce" disabled>-</button>
              <input type="text" value="1" />
              <button class="add">+</button>
            </div>
          </td>
          <td class="total">70¥</td>
          <td>
            <a href="javascript:" class="del">删除</a>
          </td>
        </tr>
      </tbody>
    </table>
    <div class="controls clearfix">
      <a href="javascript:" class="del-all">删除所选商品</a>
      <a href="javascript:" class="clear">清理购物车</a>
      <a href="javascript:" class="pay"> 去结算</a>
      <p >
        已经选中<span id="totalCount">0</span>件商品;总价:<span id="totalPrice" class="total-price">0¥</span>
      </p>
    </div>
  </div>
  
  <script>
    //0.获取标签
    //获取加号标签
    let add = document.querySelectorAll('.add')
    //获取减号标签
    let reduce = document.querySelectorAll('.reduce')
    //获取删除标签
    let del = document.querySelectorAll('.del')
    //获取商品数量输入框标签input
    let input = document.querySelectorAll('.count-c input')
    //获取商品单价标签
    let price = document.querySelectorAll('.price')
    //获取当前商品小计价格标签
    let total = document.querySelectorAll('.total')
    //获取商品总数标签
    let totalCount = document.querySelector('#totalCount')
    //获取商品总价标签
    let totalPrice = document.querySelector('#totalPrice')
    //获取删除商品数据(即每个商品数据有关的tr的父级)
    let tbody = document.querySelector('#carBody')
      //测试获取标签代码
      // alert(111)
      // console.log(input)
      //console.log(add)
      // console.log(totalCount)
      // console.log(totalPrice)
    //1.点击加号事件
    //点击加号事件,输入框数字变化且自增,同时启用减号,小计内的价格也发生变化,已经选中的总商品数量和总价也发生变化;
    for(let i = 0 ; i < add.length; i++){
      // 总价和单价是一样的
      total[i].innerText = price[i].innerText
      // 1.1.注册加号点击事件,修改商品数量有关输入框input数组对象的里面的值value属性发生自增变化、启用减号和商品小计变化
      add[i].addEventListener('click',function(){  
    //测试input的value属性自增变化代码
    // console.log(input.value++)
    // console.log(input.value)
    // console.log(input[i].value)
    // console.log(input[i].value++)
    //1.2.点击加号,商品数量自增
    //input[i].value的值是字符串(String类型),需要将其转换为数字型或整数型
    input[i].value = parseInt(input[i].value)
    input[i].value ++
    //1.3点击加号后启用减号
    reduce[i].disabled = false
    //1.4.商品小计变化模块
    //测试观察总计的值
    // console.log(typeof(price[i].innerHTML))
    // console.log(typeof(input[i].value))
    console.log(parseInt(input[i].value ))
    console.log(parseInt(price[i].innerHTML))
    // console.log(parseInt(price[i].innerHTML * input[i].value ))
    //  console.log(parseInt(price[i].innerHTML)*parseInt(input[i].value)) 
    total[i].innerHTML=`${parseInt(price[i].innerHTML)*input[i].value}`
    
    
    //1.5调用商品总件数和总价模块
    totalAccount ()   
      }  
    ) 
     //2.点击减号事件
    // 点击减号,输入框数字变化且自减,小计内的价格也发生变化,已经选中的总商品数量和总价也发生变化,当单件商品数量减到0时,禁用减号;
      // 2.1.注册减号点击事件,修改商品数量有关输入框input数组对象的里面的值value属性发生自增变化、商品数量为0禁用减号和商品小计变化
      reduce[i].addEventListener('click',function(){
    input[i].value = parseInt(input[i].value)
    //测试观察input[i].value的值
    // console.log(input[i].value) 
    input[i].value--
    //2.2.当商品数量为0,就禁用减号
    if (input[i].value <= 0){
      reduce[i].disabled = true
    }
    //2.3.商品小计计算
    total[i].innerHTML=`${parseInt(price[i].innerHTML)*input[i].value}`
    //2.4.调用商品总件数和总价模块
    totalAccount ()
    
        }   
      ) 
//3.点击删除事件
    // 点击删除,商品数据(即每个商品数据有关的tr)被删除,同时商品总数量和总价也发生变化
    // 复习
    // // 需求:点击按钮,删除li
    //     //1.获取标签 事件触发按钮button和要删除的子级有关的父级ul
    //     let btn = document.querySelector('button')
    //     let ul = document.querySelector('ul')
    //     //2.注册事件,删除子级节点
    //     btn.addEventListener('click',function(){
    //         //删除节点的语法 父元素.removeChild(子元素)
    //         ul.removeChild(ul.children[0])
    //     })
       //3.1.点击删除,商品数据(即每个商品数据有关的tr)被删除,     
   del[i].addEventListener('click',function(){
          //删除tr,它的父级是tbody
          // tbody.removeChild(tbody.children[0])
          // tbody.removeChild(tbody.children[i])
          //删除当前事件监听元素的爸爸的爸爸
          // this指向删除(del)这个类标签,它的父级是td,td的父级是tr,tr是tbody要删除的子级   
          tbody.removeChild(this.parentNode.parentNode)
          
          
          //3.2.同时商品总数量和总价也发生变化调用商品总件数和总价模块
          totalAccount ()
        })
   }
  
   
   
    
    
    //4.商品总件数和总价模块
   function totalAccount (){
    let total = document.querySelectorAll('.total')
    let input = document.querySelectorAll('.count-c input')
    //4.1.定义和初始化变量numProduct存放总计商品数量,变量priceTotal存放商品总价
    let numProduct = 0
    let priceTotal = 0
    //4.2.循环遍历输入框,计算每个对应商品数量输入框标签input的商品总数和总价
     for(let i = 0 ; i < input.length; i++){
      
      //全局变量与局部变量问题
      // let num = 0
      // console.log(input[i].value)
      numProduct  += parseInt(input[i].value)
      // console.log(parseInt(total[i].innerHTML))
      priceTotal  += parseInt(total[i].innerHTML)
     }
    //  console.log(numProduct)
    //4.3.将计算的商品总数和总价写入相应的有关标签
    //写入商品总数有关标签
    totalCount.innerHTML = `${numProduct}`
    //  console.log(priceTotal)
    //写入商品总价有关标签
    totalPrice.innerHTML = `${priceTotal}`
   } 
   //4.4.调用商品总件数和总价模块函数,注意:函数名不能与变量名有冲突
   totalAccount ()
    //商品总件数和总价模块代码思路
    // let numProduct = 0
    // let priceTotal = 0
    //  for(let i = 0 ; i < input.length; i++){
    //   //全局变量与局部变量问题
    //   // let num = 0
    //   // console.log(input[i].value)
    //   numProduct  += parseInt(input[i].value)
    //   // console.log(parseInt(total[i].innerHTML))
    //   priceTotal  += parseInt(total[i].innerHTML)
    //  }
    // //  console.log(numProduct)
    // totalCount.innerHTML = `${numProduct}`
    // //  console.log(priceTotal)
    // totalPrice.innerHTML = `${priceTotal}¥`




    

  </script>
 
  
</body>

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值