05_JavaScript DOM编程_特效篇

发布时间 2023-10-31 15:28:27作者: 城市炊烟

一、JavaScript操作表格

1.1、操作表格API

1、HTML DOM中,给table操作提供了一些属性和方法。

table元素节点的独有属性和方法:

属性或方法 说明
caption 保存着<caption>元素的引用
tBodies 保存着<tbody>元素的HTMLCollection 集合
tFoot 保存着对<tfoot>元素的引用
tHead 保存着对<thead>元素的引用
createTHead() 创建<thead>元素,并返回引用
createTFoot() 创建<tfoot>元素,并返回引用
createTBody() 创建<tbody>元素,并且返回引用
createCaption() 创建<caption>元素,并返回引用
deleteTHead() 删除<thead>元素
deleteTFoot() 删除<tfoot>元素
deleteCaption() 删除<caption>元素

2、<tbody>/<thead>/<tfoot>/<table>元素添加的属性和方法

属性或方法 说明
rows 保存着指定元素中行的HTMLCollection
deleteRow(pos) 删除指定位置的行
insertRow(pos) 向rows 集合中的指定位置插入一行,并返回引用

3、<tr>元素添加的属性和方法

属性或方法 说明
cells 保存着<tr>元素中单元格的HTMLCollection
deleteCell(pos) 删除指定位置的单元格
insertCell(pos) 向cells集合的指定位置插入一个单元格,并返回引用

1.2、案例

使用新API实现动态创建表格;

<!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>
        /* 我们如果直接写标签名,这就是所谓的标签选择器 */
        /* 选择器{}     给指定的标签添加样式 */
        /* 选择器1,选择器2,...{}    给多个指定的元素添加样式 */
        table,
        td,
        th {
            border: 1px solid black;
        }

        /* 
            一般情况下: 我们给表格设置宽度;
            一般情况下: 我们给行设置高度;
         */
        table {
            /* border-collapse 边框合并 */
            border-collapse: collapse;
            margin: 0 auto;
            width: 800px;
            /* 我们给表格设置文本居中,那么td单元格会继承到该样式 */
            /* text-align: center;  */
        }

        tr {
            height: 38px;
        }

        caption {
            font-size: 40px;
            font-weight: bold;
            height: 80px;
            line-height: 80px;
        }

        /* tbody>tr    选择tbody里面所有的行 */
        tbody>tr:nth-child(odd) {
            background: #eee;
        }

        thead td {
            text-align: center;
            font-weight: bold;
        }
    </style>
</head>

<body>
    <table id="tab1">

    </table>
    <script>
        // 获取得到table元素节点
        var stuTable = document.getElementById("tab1");

        // 创建thead、tbody
        var thead = stuTable.createTHead();
        var tbody = stuTable.createTBody();


        // 表头数据
        var headDatas = ['姓名', '科目', '成绩', '操作'];

        // 在thead里面创建一行
        var theadTr = thead.insertRow(0);

        for (var i in headDatas) { //使用for in来遍历数组的时候,in前面的变量表示数组的下标
            // 获取每一条表头数据
            // theadTr.insertCell(i) 根据下标创建行,并且给行插入内容
            theadTr.insertCell(i).innerText = headDatas[i];;
        }

        // 模拟数据
        var datas = [{
                name: '张三',
                subject: '语文',
                score: 90
            },
            {
                name: '李四',
                subject: '数学',
                score: 80
            },
            {
                name: '王武',
                subject: '英语',
                score: 99
            },
            {
                name: '马六',
                subject: '英语',
                score: 100
            },
            {
                name: '田七',
                subject: '英语',
                score: 60
            },
            {
                name: '赵八',
                subject: '英语',
                score: 70
            }
        ];

        // 根据上述数据,在tbody里面生成行及其单元格和数据
        // datas数组里有几个元素,就应该创建几行; 每个元素里面的对象有多少个key-vaue就创建多少个单元格
        for (var i in datas) { // datas里面存储的6个学生对象
            // 通过下标获取到每个学生对象
            var stuInfo = datas[i];

            // 每个学生都对应了一行数据
            var tbodyTr = tbody.insertRow(i);

            var index = 0;
            // 学生的每一个数据就是一个单元格
            for (var j in stuInfo) { // j在遍历对象的时候,是字符串的key
                // j  "name"    "subject"   "score"
                tbodyTr.insertCell(index++).innerText = stuInfo[j];
            }

            tbodyTr.insertCell(index).innerHTML = "<a href='javascript:void(0)'>删除</a>";

        }
    </script>

</body>

</html>

1.3、练习

使用JavaScript创建如下表格:

1573046337814

实现如图功能:

​ 1、动态计算数量和商品总价格

​ 2、可以实现删除和修改操作

​ 3、可以实现添加商品操作

1573046370946

二、JavaScript事件(重点)

2.1、事件介绍

​ JavaScript与HTML之间的交互是通过事件来实现的,事件就是用户或浏览器自身执行的某种动作,比如click、mounseover、load……,而响应事件的函数就叫做事件处理函数

​ 事件(Event)是JavaScript应用跳动的心脏 ,也是把所有东西粘在一起的胶水。当我们与浏览器中Web页面进行某些类型的交互时,事件就发生了。事件可能是用户在某些内容上的点击、鼠标经过某个特定元素或按下键盘上的某些按键。事件还可能是Web浏览器中发生的事情,比如说某个Web页面加载完成,或者是用户滚动窗口或改变窗口大小。

事件:触发-响应机制

事件三要素:

  • 事件源:触发(被)事件的元素
  • 事件名称: click 点击事件
  • 事件处理程序:事件触发后要执行的代码(函数形式)

1573104570603

事件发生常见情况:

当鼠标点击某个地方的时候,发生了...

当鼠标移入某个地方的时候,发生了...

页面载入完毕的时候,发生了...

当输入框获取光标的时候,发生了...

当输入框失去光标的时候,发生了...

当按下某个键的时候,发生了...

​ 通过使用 JavaScript ,你可以监听特定事件的发生,并规定让某些事件发生以对这些事件做出响应。JavaScript 事件是由访问Web页面的用户引起的一系列操作,例如:用户点击。当用户执行某些操作的时候,再去执行一系列代码。

​ 注意:事件通常与函数配合使用,当事件发生时函数才会执行。

2.2、事件三种使用方式

事件一般是用于浏览器和用户操作进行交互。

使用JavaScript有三种方法:内联(行内)、脚本和事件侦听;

2.2.1、内联方式

​ 这种模型是最传统简单的一种处理事件的方法。在内联模型中,事件处理函数是HTML标签的一个属性,用于处理指定事件。

​ 虽然内联在早期使用较多,但它是和HTML混写的,并没有与HTML分离。

//在HTML 中把事件处理函数作为属性执行JS代码
<div id="div1" onclick="this.style.background='blue'">我是div里面的文字</div>

//在HTML 中把事件处理函数作为属性执行JS 函数1
<div id="div1" onclick="myfun1(this)">我是div里面的文字</div>

<script>
    function myfun1(t){t.style.background='blue'}
</script>

​ 注:内联可以给一个元素定义的同一事件添加多个执行函数

<div id="div1" onclick="myfun1(),myfun2()">我是div里面的文字</div>

2.2.2、脚本方式

​ 由于内联模型违反了HTML 与JavaScript 代码层次分离的原则。为了解决这个问题,我们可以在JavaScript 中处理事件。这种处理方式就是脚本模型。

获取元素,给其加事件,事件赋值某个函数名,切记不加括号.

// 获取页面元素
var div1 = document.getElementById("div1");

// 定义事件函数
function myfun1(){
  div1.style.background="blue";
}

// 给元素绑定事件及其事件函数
div1.onclick=myfun1;

匿名函数写法:

var div1 = document.getElementById("div1");

div1.onclick=function(){
	div1.style.background="blue";
}

 // 脚本方式移除事件处理函数
div1.onclick = null;

​ 通过匿名函数,可以直接触发对应的代码。

​ 注:脚本形式不可以给同一事件添加多个执行函数;

2.2.3、事件监听(侦听)方式

JavaScript中定义了两个方法,用于添加事件和删除事件处理程序的操作:addEventListener()和removeEventListener()。

所有DOM 节点中都包含这两个方法,并且它们都接受3个参数;事件类型、事件处理函数、冒泡或捕获的布尔值(true 表示捕获,false 表示冒泡)。

标准的浏览器使用W3C定义的addEventListener函数绑定,函数定义如下:

function addEventListener(string eventType, function eventFunc, [bool useCapture=false])
​ 参数eventType: 事件名称,如click、mouseover…
​ eventFunc: 绑定到事件中执行的动作
​ ​useCapture: 指定是否绑定在捕获阶段,true为是,false为否,默认为false。
​注:事件冒泡和捕捉在之后讲!

document.addEventListener('click', function () {
  alert('document');
});

box.addEventListener('click', function () {
  alert('Lee');
});

事件侦听的优势:

1、当同一个对象使用.onclick的写法触发多个方法的时候,后一个方法会把前一个方法覆盖掉,也就是说,在对象的onclick事件发生时,只会执行最后绑定的方法。

而用事件监听则不会有覆盖的现象,每个绑定的事件都会被执行。如下:

window.onload = function(){

  var btn = document.getElementById("yuanEvent");
  btn.onclick = function(){
    alert("第一个事件");
  }

  btn.onclick = function(){
    alert("第二个事件");
  }

  btn.onclick = function(){
    alert("第三个事件");
  }
}

最后只输出:第三个事件,因为后一个方法都把前一个方法覆盖掉。

原生态的事件绑定函数addEventListener:
function eventOne (){
  alert("第一个监听事件");
}

function eventTwo(){
  alert("第二个监听事件");
}

window.onload = function(){
  var btn = document.getElementById("yuanEvent");
  //addEventListener:绑定函数
  btn.addEventListener("click",eventOne);
  btn.addEventListener("click",eventTwo);
}
输出:第一个监听事件和第二个监听事件

2、采用事件监听给对象绑定方法后,可以解除相应的绑定,写法如下:

var eventOne = function(){
  alert("第一个监听事件");
}
function eventTwo(){
  alert("第二个监听事件");
}
  var btn = document.getElementById("yuanEvent");
  btn.addEventListener("click",eventOne);
  btn.addEventListener("click",eventTwo);
  btn.removeEventListener("click",eventOne);
输出:第二个监听事件
解除绑定事件的时候一定要用函数的句柄,把整个函数写上是无法解除绑定的。

2.2.4、IE兼容

IE9以前不支持addEventListener和removeEventListener方法;

实现了两个类似的方法:

  • attachEvent
  • detachEvent
// 第一个参数,事件的名称前要加 on
box.attachEvent('onclick', eventCode);
box.detachEvent('onclick', eventCode);

function eventCode() {
  console.log('点击后执行');
}

这两个方法都接受两个相同的参数。

​ 1.事件处理程序名称

​ 2.事件处理程序方法

attachEvent——兼容:IE7、IE8;不兼容firefox、chrome、IE9、IE10、IE11、safari、opera

addEventListener——兼容:firefox、chrome、IE、safari、opera;不兼容IE7、IE8

function addEventListener(element, type, fn) {
    if (element.addEventListener) {
        element.addEventListener(type, fn, false);
    } else if (element.attachEvent) {
        element.attachEvent('on' + type, fn);
    } else {
        element['on' + type] = fn;
    }
}

function removeEventListener(element, type, fn) {
    if (element.removeEventListener) {
        element.removeEventListener(type, fn, false);
    } else if (element.detachEvent) {
        element.detachEvent('on' + type, fn);
    } else {
        element['on' + type] = null;
    }
}

addEventListener(box, "click", function () {
    console.log("兼容解决策略")
})

2.3、常用的事件类型

​ JavaScript 可以处理的事件类型为:鼠标事件、键盘事件、框架事件、表单事件。

​ 所有的事件处理函数都会都有两个部分组成,on + 事件名称,例如click 事件的事件处理函数就是:onclick。在这里,我们主要谈论脚本的方式来构建事件,违反分离原则的内联,我们忽略掉。

鼠标事件:

1、click 当用户点击某个对象时调用的事件句柄。

2、dblclick 当用户双击某个对象时调用的事件句柄。

3、mousedown 鼠标按钮被按下。

4、mouseup 当用户释放鼠标按钮时触发[鼠标左右键]。

5、mouseover 鼠标移到某元素之上。

6、mouseout 鼠标从某元素移开。

7、mousemove 当鼠标被移动。

8、mouseenter 当鼠标指针移动到元素上时触发。

9、mouseleave 当鼠标指针移出元素时触发

onmouseenter 事件类似于 onmouseover 事件。 唯一的区别是 onmouseenter 事件不支持冒泡 。

键盘事件:

1、keydown:当用户按下键盘上任意键触发,如果按住不放,会重复触发。

​ onkeydown = function () {alert('Lee');};

2、keypress:当用户按下键盘上的字符键触发,如果按住不放,会重复触发。

​ onkeypress = function () {alert('Lee');};

3、keyup:当用户释放键盘上的键触发。

​ onkeyup = function () {alert('Lee');};

​ 注:键盘事件一般用在可获取焦点的元素上或doument对象上

​​ 在发生键盘事件时,键盘事件对象的keyCode 属性中会包含一个代码,与键盘上一个特定的键对应。

keypress 和 keydown 是有区别?

​ 1.keydown 先于 keypress 发生。

​ 2.keypress 无法系统按钮。

​ 3.keydown 捕获的 keyCode 不区分字母大小,而 keypress 区分。

​ 4.keypress是在用户按下并放开任何字母数字键时发生。系统按钮(例如,箭头键和功能键)无法得到识别。

​ 5.keyup 是在用户放开任何先前按下的键盘键时发生。

​ 6.keydown 是在用户按下任何键盘键(包括系统按钮,如箭头键和功能键)时发生。

框架/对象(Frame/Object)事件

1、load 当页面完全加载后在window 上面触发,或当框架集加载完毕后在框架集上触发。当页面完全加载所有内容(包括图像、脚本文件、CSS 文件等)执行

2、unload 用户退出页面。 ( <body><frameset>)

<div id="box">
    
</div>
<script>
    // BOM   onload  页面加载完成之后执行  
    // 页面加载完成 页面上所有的元素创建完毕,并且引用的外部资源下载完毕(js,css,图片)
    // window.onload = function () {
    onload = function () {
    var box = document.getElementById('box');
    console.log(box);
    }

    // window.onunload = function () {
    // 页面卸载的时候执行
    onunload = function () {
    // 在onunload中所有的对话框都无法使用 window 对象被冻结
    // Blocked alert('欢迎下次再来') during unload.
    // alert('欢迎下次再来');
    console.log('bye bye');
    }
    
    // f5 刷新页面  
    // 1 卸载页面
    // 2 重新加载页面
</script>

3、beforeunload 该事件在即将离开页面(刷新或关闭)时触发

4、hashchange 该事件在当前 URL 的锚部分发生修改时触发。

5、resize 当窗口或框架的大小变化时在window 或框架上触发。

6、scroll 当用户滚动时触发。

表单事件:

1、select 当用户选择文本框(input 或textarea)中的一个或多个字符触发。

2、change 该事件在表单元素的内容改变时触发( <input>, <keygen>, <select>, 和 <textarea>)。

3、focus 元素获取焦点时触发。

4、blur 元素失去焦点时触发。

5、submit 当用户点击提交按钮在<form>元素上触发。

6、reset 当用户点击重置按钮在<form>元素上触发。

<form action="" onsubmit="myFun1()" onreset="myfun2()">
    <input type="text" name="userName" placeholder="请输入您的名字" > <br>
    <input type="submit" value="提交表单"><input type="reset" value="重置表单">
</form>
<script>
    function myFun1(){
        var userName = document.getElementsByTagName("input")[0].value;
        alert("hello,"+userName);
    }
    function myfun2(){alert("您重置了表单");}
</script>

7、input 元素获取用户输入时触发

2.4、事件的冒泡和捕获

2.4.1、事件冒泡和捕获

​ 什么是事件冒泡?

​ 在一个对象上触发某类事件(比如单击click事件),如果此对象定义了此事件的处理程序,那么此事件就会调用这个处理程序,如果没有定义此事件处理程序或者事件返回true,那么这个事件会向这个对象的父级对象传播,从里到外,直至它被处理(父级对象所有同类事件都将被激活),或者它到达了对象层次的最顶层,即document对象(有些浏览器是window)。

​ 事件的冒泡:事件按照从最特定的事件目标到最不特定的事件目标的顺序触发。

​ 事件冒泡可以形象地比喻为把一颗石头投入水中,泡泡会一直从水底冒出水面。也就是说,事件会从最内层的元素开始发生,一直向上传播,直到document对象。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
        <style>
            #box1 {
                width: 300px;
                height: 300px;
                background-color: red;
            }

            #box2 {
                width: 200px;
                height: 200px;
                background-color: green;
            }

            #box3 {
                width: 100px;
                height: 100px;
                background-color: blue;
            }
        </style>
    </head>
    <body>
        <div id="box1">
            <div id="box2">
                <div id="box3">
                </div>
            </div>
        </div>
        <script>
            // addEventListener 的第三个参数的作用
            var box1 = document.getElementById('box1');
            var box2 = document.getElementById('box2');
            var box3 = document.getElementById('box3');

            var array = [box1, box2, box3];

            // addEventListener 的第三个参数为false的时候 : 事件冒泡
            // addEventListener 的第三个参数为true的时候 :  事件捕获

            for (var i = 0; i < array.length; i++) {
                array[i].addEventListener('click', function () {
                    console.log(this.id);
                }, false);
            }

            document.body.addEventListener('click', function () {
                console.log('body');
            }, false);
        </script> 
    </body>
</html>

​ 事件的捕获:与事件冒泡相反,事件会从最外层开始发生,直到最具体的元素。

​ 事件捕获时,父级元素先触发,子级元素后触发。

​ 可以自己选择绑定事件时采用事件捕获还是事件冒泡,方法就是绑定事件时通过addEventListener函数,它有三个参数,第三个参数若是true,则表示采用事件捕获,若是false,则表示采用事件冒泡。

​ IE9以前的版本只支持事件冒泡,不支持事件捕获,它也不支持addEventListener函数,不会用第三个参数来表示是冒泡还是捕获,它提供了另一个函数attachEvent。

2.4.2、事件的三个阶段

事件的处理过程主要有三个阶段:捕获阶段,目标阶段,冒泡阶段;

捕获阶段:当我们在 DOM 树的某个节点发生了一些操作(例如单击、鼠标移动上去),就会有一个事件发射过去。这个事件从 Window 发出,不断经过下级节点直到触发的目标节点。在到达目标节点之前的过程,就是捕获阶段(Capture Phase)。事件由页面元素接收,逐级向下,到具体的元素。

目标阶段:当事件不断的传递直到目标节点的时候,最终在目标节点上触发这个事件,就是目标阶段。具体的元素本身。

冒泡阶段:事件冒泡即事件开始时,由最具体的元素接收(也就是事件发生所在的节点),然后逐级传播到较为不具体的节点。跟捕获相反,具体元素本身,逐级向上,到页面元素(我们平时用的事件绑定就是利用的事件冒泡的原理)。

W3C : 任何事件发生时,先从顶层开始进行事件捕获,直到事件触发到达事件源,再从事件源向上进行事件捕获(事件冒泡)。

事件对象.eventPhase属性可以查看事件触发时所处的阶段

1573104917347

2.4.3、事件对象的属性和方法(核心)

	在标准的DOM 事件中,event 对象包含创建它的特定事件有关的属性和方法。Event 对象代表事件的状态,比如事件在其中发生的元素、键盘按键的状态、鼠标的位置、鼠标按钮的状态。

什么时候会产生Event 对象呢? 
	事件对象,我们一般称作为event 对象,这个对象是浏览器通过函数把这个对象作为参数传递过来的。通过事件绑定的执行函数是可以得到一个隐藏参数的,这个参数其实就是event对象。

事件通常与函数结合使用,函数不会在事件发生前被执行!
var oDIv = document.getElementById('box');

oDiv.onclick = function(event){
    .........
}
  • event.eventPhase 返回事件传播的当前阶段。
  • event.target || event.srcElement 返回触发此事件的元素(事件的目标节点)。
  • event.currentTarget 返回其事件监听器触发该事件的元素
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    #box1 {
      width: 300px;
      height: 300px;
      background-color: red;
    }

    #box2 {
      width: 200px;
      height: 200px;
      background-color: green;
    }

    #box3 {
      width: 100px;
      height: 100px;
      background-color: blue;
    }
  </style>
</head>

<body>
  <input type="button" value="按钮" id="btn">
  <div id="box1">
    <div id="box2">
      <div id="box3">
      </div>
    </div>
  </div>
  <script>
    // 通过事件对象,可以获取到事件发生的时候和事件相关的一些数据
    var btn = document.getElementById('btn');
    btn.onclick = function (e) {
      // DOM标准中,是给事件处理函数一个参数
      // e就是事件对象
      // 在老版本的IE中获取事件对象的方式  window.event
      // 处理事件对象的浏览器兼容性
      e = e || window.event;

      // 事件的阶段:1  捕获阶段   2  目标阶段  3 冒泡阶段    了解
      // console.log(e.eventPhase);

      // e.target  获取真正触发事件的对象  浏览器兼容问题
      // 在老版本的IE中  srcElement
      // 处理兼容性问题
      var target = e.target || e.srcElement;
      console.log(e.target);
        
      // e.currentTarget  事件处理函数所属的对象this
      console.log(e.currentTarget);
    }

    var box1 = document.getElementById('box1');
    var box2 = document.getElementById('box2');
    var box3 = document.getElementById('box3');
    var array = [box1, box2, box3];
    for (var i = 0; i < array.length; i++) {
      var box = array[i];
      box.onclick = function (e) {
        e = e || window.event;
        // 事件的阶段
        console.log(e.eventPhase);
        // e.target 获取真正触发事件的对象
        var target = e.target || e.srcElement;
        console.log(target);
        // e.curentTarget   和this一样 获取事件处理函数所属的对象
        console.log(e.currentTarget);
        console.log(this);
      }
    }
  </script>
</body>

</html>
  • event.bubbles 返回布尔值,指示事件是否是冒泡事件类型
  • event.cancelable 返回布尔值,指示事件是否可拥可取消的默认动作。
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    #box1 {
      width: 300px;
      height: 300px;
      background-color: red;
    }

    #box2 {
      width: 200px;
      height: 200px;
      background-color: green;
    }

    #box3 {
      width: 100px;
      height: 100px;
      background-color: blue;
    }
  </style>
</head>

<body>
  <input type="button" value="按钮" id="btn">
  <div id="box1">
    <div id="box2">
      <div id="box3">
      </div>
    </div>
  </div>

  <input type="text" id="input1">
  <script>
    var box1 = document.getElementById('box1');
    var box2 = document.getElementById('box2');
    var box3 = document.getElementById('box3');
    var input1 = document.getElementById('input1');
    var array = [box1, box2, box3];
    for (var i = 0; i < array.length; i++) {
      var box = array[i];
      //冒泡事件类型:click,mousedown,mouseup,keydown,keyup,keypress等
      box.onclick = function (e) {
        e = e || window.event;
        console.log(e);
        // 是否是冒泡事件类型
        console.log(e.bubbles);
        // 是否可拥可取消的默认动作
        console.log(e.cancelable);
      }

    }

    input1.onfocus = function (e) {
      e = e || window.event;
      // 是否是冒泡事件类型
      console.log(e.bubbles);
      // 是否可拥可取消的默认动作
      console.log(e.cancelable);
    }
  </script>
</body>

</html>
  • event.type 获取事件类型
  • event.timeStamp 返回事件生成的日期和时间。
var box1 = document.getElementById('box1');
var box2 = document.getElementById('box2');
var box3 = document.getElementById('box3');
var input1 = document.getElementById('input1');
var array = [box1, box2, box3];
for (var i = 0; i < array.length; i++) {
    var box = array[i];
    //冒泡事件类型:click,mousedown,mouseup,keydown,keyup,keypress等
    box.onclick = function (e) {
        e = e || window.event;
        // 获取事件类型
        console.log(e.type);
        //   返回事件生成的日期和时间。
        console.log(e.timeStamp)
    }

}

input1.onfocus = function (e) {
    e = e || window.event;
    // 获取事件类型
    console.log(e.type);
    //   返回事件生成的日期和时间。
    console.log(e.timeStamp)
}

// 获取时间类型的作用: 可以给多个事件绑定一个事件函数,提升性能
var box = document.getElementById('box');
// box.onclick = function (e) {
//   e = e || window.event;
//   // 获取事件名称
//   console.log(e.type);
// }

box.onclick = fn;
box.onmouseover = fn;
box.onmouseout = fn;

function fn(e) {
    e = e || window.event;
    switch (e.type) {
        case 'click': 
            console.log('点击box');
            break;
        case 'mouseover': 
            console.log('鼠标经过box');
            break;
        case 'mouseout': 
            console.log('鼠标离开box');
            break;
    }
}
  • event.clientX/clientY 所有浏览器都支持,窗口位置
  • event.pageX/pageY IE9以前不支持,页面位置
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    body {
      margin: 0;
      height: 1000px;
    }
    #box {
      margin: 100px;
      margin-top: 500px;
      width: 200px;
      height: 200px;
      background-color: red;
    }
  </style>
</head>
<body>
  <div id="box">
  </div> 
  <script>
    var box = document.getElementById('box');
    box.onclick = function (e) {
      e = e || window.event;

      // 获取的鼠标在浏览器的可视区域的坐标
      // console.log(e.clientX);
      // console.log(e.clientY);

      // 鼠标在当前页面的位置
      console.log(e.pageX);
      console.log(e.pageY);

    }

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

pageX和pageY兼容性解决

​ e.clientX/e.clientY 鼠标在可视区域中的位置

​ e.pageX/e.pageY 鼠标在页面中的位置 有兼容性问题 从IE9以后才支持

​ pageY = clientY + 页面滚动出去的距离

步骤1:获取页面滚动出去的距离

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

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    body {
      height: 1000px;
    }

    div {
      width: 300px;
      height: 300px;
      background: yellow;
      margin-top: 360px;
    }
  </style>
</head>

<body>
  <div>

  </div>
  <script>
    document.onclick = function () {
      // 输出页面滚动出去的距离
      console.log(document.body.scrollLeft);
      console.log(document.body.scrollTop);

      // documentElement  文档的根元素  html标签
      // console.log(document.documentElement);
      // 有些浏览器 是使用这两个属性来获取的
      console.log(document.documentElement.scrollLeft);
      console.log(document.documentElement.scrollTop);

    }

    // 获取页面滚动距离的浏览器兼容性问题
    // 获取页面滚动出去的距离
    function getScroll() {
      var scrollLeft = document.body.scrollLeft || document.documentElement.scrollLeft;
      var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
      return {
        scrollLeft: scrollLeft,
        scrollTop: scrollTop
      }
    }
  </script>
</body>

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

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    body {
      height: 1000px;
    }
  </style>
</head>

<body>

  <script>
    // e.clientX/e.clientY   鼠标在可视区域中的位置
    // e.pageX/e.pageY       鼠标在页面中的位置 有兼容性问题  从IE9以后才支持
    // pageY = clientY + 页面滚动出去的距离
    document.onclick = function (e) {
      e = e || window.event;
      console.log(getPage(e).pageX);
      console.log(getPage(e).pageY);

    }

    // 获取鼠标在页面的位置,处理浏览器兼容性
    function getPage(e) {
      var pageX = e.pageX || e.clientX + getScroll().scrollLeft;
      var pageY = e.pageY || e.clientY + getScroll().scrollTop;
      return {
        pageX: pageX,
        pageY: pageY
      }
    }

    // 获取页面滚动距离的浏览器兼容性问题
    // 获取页面滚动出去的距离
    function getScroll() {
      var scrollLeft = document.body.scrollLeft || document.documentElement.scrollLeft;
      var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
      return {
        scrollLeft: scrollLeft,
        scrollTop: scrollTop
      }
    }
  </script>
</body>

</html>
  • 修改键

有时,我们需要通过键盘上的某些键来配合鼠标来触发一些特殊的事件。这些键为:Shfit、Ctrl、Alt,它们经常被用来修改鼠标事件和行为,所以叫修改键。

属性 说明

shiftKey 判断是否按下了Shfit 键

ctrlKey 判断是否按下了ctrlKey 键

altKey 判断是否按下了alt 键

1、判断是否按下alt键、shift键、ctrl键

document.onkeydown = function (e) {
    if(e.altKey){
        alert("alt按下");
    }else if(e.shiftKey){
        alert("shift按下");
    }else if(e.ctrlKey){
        alert("ctrl按下");
    }else{
        alert("按下的不是alt/shift/ctrl");
    }
}

2、判断alt键、shift键、ctrl键是否同时按下

document.onkeydown = function (e) {
    if(e.altKey && e.shiftKey ){
        alert("同时按下");
    }
}


document.onkeydown = function (e) {
    if(e.altKey && e.shiftKey && e.ctrlKey){
        alert("同时按下");
    }
}
  • 键码

在发生keydown 和keyup 事件时,event 对象的keyCode 属性中会包含一个代码,与键盘上一个特定的键对应。对数字字母字符集,keyCode 属性的值与ASCII 码中对应小写字母或数字的编码相同。字母中大小写不影响。

document.onkeydown = function (evt) {
	alert(evt.keyCode); //按任意键,得到相应的keyCode
};

// 在键盘上每个键都有一个keyCode,可以通过如下方法获取每个键对应的keyCode。
document.onkeydown = function (e) {
   alert(e.keyCode);
}

document.onkeypress = function (e) {
   alert(e.keyCode);
}

​ Firefox、Chrome和Safari 的event 对象都支持一个charCode 属性,这个属性只有在发生keypress 事件时才包含值,而且这个值是按下的那个键所代表字符的ASCII 编码。此时的keyCode 通常等于0 或者也可能等于所按键的编码。IE 和Opera 则是在keyCode 中保存字符的ASCII 编码。

document.onkeypress = function (e) {
   var key = String.fromCharCode(e.charCode);
    alert(key);
}

需要注意的是,可以使用String.fromCharCode()将ASCII 编码转换成实际的字符。

2.4.4、事件委托

什么叫事件委托呢?

​ JavaScript高级程序设计上讲:事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。

举例:

​ 有三个同事预计会在周一收到快递。为签收快递,有两种办法:一是三个人在公司门口等快递;二是委托给前台MM代为签收。现实当中,我们大都采用委托的方案(公司也不会容忍那么多员工站在门口就为了等快递)。前台MM收到快递后,她会判断收件人是谁,然后按照收件人的要求签收,甚至代为付款。这种方案还有一个优势,那就是即使公司里来了新员工(不管多少),前台MM也会在收到寄给新员工的快递后核实并代为签收。

这里其实还有2层意思的:

第一,现在委托前台的同事是可以代为签收的,即程序中的现有的dom节点是有事件的;

第二,新员工也是可以被前台MM代为签收的,即程序中新添加的dom节点也是有事件的。

为什么要用事件委托?

​ 一般来说,dom需要有事件处理程序,我们都会直接给它设事件处理程序就好了,那如果是很多的dom需要添加事件处理呢?比如我们有100个li,每个li都有相同的click点击事件,可能我们会用for循环的方法,来遍历所有的li,然后给它们添加事件,那这么做会存在什么影响呢?

​ 在JavaScript中,添加到页面上的事件处理程序数量将直接关系到页面的整体运行性能,因为需要不断的与dom节点进行交互,访问dom的次数越多,引起浏览器重绘与重排的次数也就越多,就会延长整个页面的交互就绪时间,这就是为什么性能优化的主要思想之一就是减少DOM操作的原因;如果要用事件委托,就会将所有的操作放到js程序里面,与dom的操作就只需要交互一次,这样就能大大的减少与dom的交互次数,提高性能;

​ 每个函数都是一个对象,是对象就会占用内存,对象越多,内存占用率就越大,自然性能就越差了,比如上面的100个li,就要占用100个内存空间,如果是1000个,10000个呢,那只能说呵呵了,如果用事件委托,那么我们就可以只对它的父级(如果只有一个父级)这一个对象进行操作,这样我们就需要一个内存空间就够了,是不是省了很多,自然性能就会更好。

事件委托的原理:

​ 事件委托是利用事件的冒泡原理来实现的

事件委托怎么实现:

<ul id="ul1">
    <li>111</li>
    <li>222</li>
    <li>333</li>
    <li>444</li>
</ul>

实现功能是点击li,弹出123:

window.onload = function(){
    var oUl = document.getElementById("ul1");
    var aLi = oUl.getElementsByTagName('li');
    for(var i=0;i<aLi.length;i++){
        aLi[i].onclick = function(){
            alert(123);
        }
    }
}

上面的代码的意思很简单,相信很多人都是这么实现的,我们看看有多少次的dom操作,首先要找到ul,然后遍历li,然后点击li的时候,又要找一次目标的li的位置,才能执行最后的操作,每次点击都要找一次li;

那么我们用事件委托的方式做又会怎么样呢?

window.onload = function(){
    var oUl = document.getElementById("ul1");
   oUl.onclick = function(){
        alert(123);
    }
}

这里用父级ul做事件处理,当li被点击时,由于冒泡原理,事件就会冒泡到ul上,因为ul上有点击事件,所以事件就会触发,当然,这里当点击ul的时候,也是会触发的,那么问题就来了,如果我想让事件代理的效果跟直接给节点的事件效果一样怎么办,比如说只有点击li才会触发,不怕,我们有绝招:

Event对象提供了一个属性叫target,可以返回事件的目标节点,我们成为事件源,也就是说,target就可以表示为当前的事件操作的dom,但是不是真正操作dom,当然,这个是有兼容性的,标准浏览器用ev.target,IE浏览器用event.srcElement,此时只是获取了当前节点的位置,并不知道是什么节点名称,这里我们用nodeName来获取具体是什么标签名,这个返回的是一个大写的,我们需要转成小写再做比较(习惯问题):

解释var ev = ev || window.event;:https://www.jianshu.com/p/e8a6fad0f7bc

window.onload = function(){
  var oUl = document.getElementById("ul1");
  oUl.onclick = function(ev){
    var ev = ev || window.event;
    var target = ev.target || ev.srcElement;
       // 检查事件源e.target是否为Li 
    if(target.nodeName.toLowerCase() == 'li'){
         alert(123);
         alert(target.innerHTML);
    }
  }
}

这样改下就只有点击li会触发事件了,且每次只执行一次dom操作,如果li数量很多的话,将大大减少dom的操作,优化的性能可想而知!

上面的例子是说li操作的是同样的效果,要是每个li被点击的效果都不一样,那么用事件委托还有用吗?

<div id="box">
    <input type="button" id="add" value="添加" />
    <input type="button" id="remove" value="删除" />
    <input type="button" id="move" value="移动" />
    <input type="button" id="select" value="选择" />
</div>
window.onload = function(){
    var Add = document.getElementById("add");
    var Remove = document.getElementById("remove");
    var Move = document.getElementById("move");
    var Select = document.getElementById("select");

    Add.onclick = function(){
        alert('添加');
    };
    Remove.onclick = function(){
        alert('删除');
    };
    Move.onclick = function(){
        alert('移动');
    };
    Select.onclick = function(){
        alert('选择');
    }

}

上面实现的效果我就不多说了,很简单,4个按钮,点击每一个做不同的操作,那么至少需要4次dom操作,如果用事件委托,能进行优化吗?

window.onload = function(){
    var oBox = document.getElementById("box");
    oBox.onclick = function (ev) {
        var ev = ev || window.event;
        var target = ev.target || ev.srcElement;
        if(target.nodeName.toLocaleLowerCase() == 'input'){
            switch(target.id){
                case 'add' :
                    alert('添加');
                    break;
                case 'remove' :
                    alert('删除');
                    break;
                case 'move' :
                    alert('移动');
                    break;
                case 'select' :
                    alert('选择');
                    break;
            }
        }
    }

}

用事件委托就可以只用一次dom操作就能完成所有的效果,比上面的性能肯定是要好一些的

现在讲的都是document加载完成的现有dom节点下的操作,那么如果是新增的节点,新增的节点会有事件吗?也就是说,一个新员工来了,他能收到快递吗?

看一下正常的添加节点的方法:

<input type="button" name="" id="btn" value="添加" />
<ul id="ul1">
    <li>111</li>
    <li>222</li>
    <li>333</li>
    <li>444</li>
</ul>

现在是移入li,li变红,移出li,li变白,这么一个效果,然后点击按钮,可以向ul中添加一个li子节点

window.onload = function(){
    var oBtn = document.getElementById("btn");
    var oUl = document.getElementById("ul1");
    var aLi = oUl.getElementsByTagName('li');
    var num = 4;

    //鼠标移入变红,移出变白
    for(var i=0; i<aLi.length;i++){
        aLi[i].onmouseover = function(){
            this.style.background = 'red';
        };
        aLi[i].onmouseout = function(){
            this.style.background = '#fff';
        }
    }
    //添加新节点
    oBtn.onclick = function(){
        num++;
        var oLi = document.createElement('li');
        oLi.innerHTML = 111*num;
        oUl.appendChild(oLi);
    };
}

这是一般的做法,但是你会发现,新增的li是没有事件的,说明添加子节点的时候,事件没有一起添加进去,这不是我们想要的结果,那怎么做呢?一般的解决方案会是这样,将for循环用一个函数包起来,命名为mHover,如下:

window.onload = function(){
    var oBtn = document.getElementById("btn");
    var oUl = document.getElementById("ul1");
    var aLi = oUl.getElementsByTagName('li');
    var num = 4;

    function mHover () {
        //鼠标移入变红,移出变白
        for(var i=0; i<aLi.length;i++){
            aLi[i].onmouseover = function(){
                this.style.background = 'red';
            };
            aLi[i].onmouseout = function(){
                this.style.background = '#fff';
            }
        }
    }
    mHover ();
    //添加新节点
    oBtn.onclick = function(){
        num++;
        var oLi = document.createElement('li');
        oLi.innerHTML = 111*num;
        oUl.appendChild(oLi);
        mHover ();
    };
}

虽然功能实现了,看着还挺好,但实际上无疑是又增加了一个dom操作,在优化性能方面是不可取的,那么有事件委托的方式,能做到优化吗?

window.onload = function(){
    var oBtn = document.getElementById("btn");
    var oUl = document.getElementById("ul1");
    var aLi = oUl.getElementsByTagName('li');
    var num = 4;

    //事件委托,添加的子元素也有事件
    oUl.onmouseover = function(ev){
        var ev = ev || window.event;
        var target = ev.target || ev.srcElement;
        if(target.nodeName.toLowerCase() == 'li'){
            target.style.background = "red";
        }

    };
    oUl.onmouseout = function(ev){
        var ev = ev || window.event;
        var target = ev.target || ev.srcElement;
        if(target.nodeName.toLowerCase() == 'li'){
            target.style.background = "#fff";
        }

    };

    //添加新节点
    oBtn.onclick = function(){
        num++;
        var oLi = document.createElement('li');
        oLi.innerHTML = 111*num;
        oUl.appendChild(oLi);
    };
}

看,上面是用事件委托的方式,新添加的子元素是带有事件效果的,我们可以发现,当用事件委托的时候,根本就不需要去遍历元素的子节点,只需要给父级元素添加事件就好了,其他的都是在js里面的执行,这样可以大大的减少dom操作,这才是事件委托的精髓所在。

适合用事件委托的事件:click,mousedown,mouseup,keydown,keyup,keypress。

值得注意的是,mouseover和mouseout虽然也有事件冒泡,但是处理它们的时候需要特别的注意,因为需要经常计算它们的位置,处理起来不太容易。

不适合的就有很多了,举个例子,mousemove,每次都要计算它的位置,非常不好把控,在不如说focus,blur之类的,本身就没用冒泡的特性,自然就不能用事件委托了。

2.4.5、阻止事件

​ event.stopPropagation()方法阻止事件在 DOM 中继续传播,防止再触发定义在别的节点上的监听函数,但是不包括在当前节点上其他的事件监听函数。

function stopEvent(e) {
  // 标准的DOM方法
  e.stopPropagation();
    
  // 取消冒泡  非标准的方式 老版本的IE支持
  e.cancelBubble = true;
}

el.addEventListener('click', stopEvent, false);

​ 上面代码中,click事件将不会进一步冒泡到el节点的父节点。

//阻止事件冒泡的兼容写法
function stopBubble(e){
    //如果提供了事件对象,则是一个非IE浏览器
    if(e && e.stopPropagation)
        //因此它支持W3C的stopPropagation()方法
        e.stopPropagation();
    else
        //否则,我们需要使用IE的方式来取消事件冒泡
        window.event.cancelBubble = true;
}

​ event.preventDefault()方法取消浏览器对当前事件的默认行为这是阻止默认事件的方法,调用此方法是,连接不会被打开,但是会发生冒泡,冒泡会传递到上一层的父元素;

​ 比如点击链接后,浏览器默认会跳转到另一个页面,使用这个方法以后,就不会跳转了;

​ 该方法生效的前提是,事件对象的cancelable属性为true,如果为false,调用该方法没有任何效果。

​ 阻止事件冒泡的方法,其中e是function中的参数;

<input type="checkbox" id="my-checkbox" />

<script>
    var cb = document.getElementById('my-checkbox');
    cb.addEventListener(
        'click',
        function (e) {
            // DOM标准方法
            e.preventDefault();

            // IE的老版本,非标准方式
      		e.returnValue = false;
        },
        false
    );
</script>

​ 上面代码中,浏览器的默认行为是单击会选中单选框,取消这个行为,就导致无法选中单选框。

​ return false,这个方法也会阻止默认事件;

<div id="div1">
    <a href="http://www.baidu.com" id="myA">我是超链接</a>
</div>

<script>
    var myA = document.getElementById('myA');
    var div1 = document.getElementById("div1");
    div1.onclick = function () {
        alert("div1触发了事件");
    }

    myA.onclick = function () {
        return false;
    }
</script>
//阻止事件默认行为的兼容写法
function stopDefault(e){
    //如果提供了事件对象,则是一个非IE浏览器
    if(e && e.preventDefault)
        //因此它支持W3C的preventDefault()方法
        e.preventDefault();
    else
        //否则,我们需要使用IE的方式来取消事件默认行为
        window.event.returnValue = false;
}

2.5、事件流

​ 很久以前有个叫Netscape的姑娘,她制订了Javascript的一套事件驱动机制(即事件捕获)。后来又有一个叫“IE”的小子,这孩子比较傲气,他认为“凭什么我要依照你的规则走”,于是他又创造了一套自己的规则(事件冒泡)。再后来,有个叫W3C的媒婆,想撮合这两个孩子,将他们的特点融合在了一起,这下,事件产生的顺序变成:事件从根节点开始,逐级派送到子节点,若节点绑定了事件动作,则执行动作,然后继续走,这个阶段称为“捕获阶段(Capture)”;执行完捕获阶段后,事件由子节点往根节点派送,若节点绑定了事件动作,则执行动作,然后继续走,这个阶段称为“冒泡阶段(Bubble)”。

事件流是描述的从页面接受事件的顺序,当几个都具有相同事件的元素层叠在一起的时候,比如点击事件,那么你点击其中一个元素,并不是只有当前被点击的元素会触发事件,而层叠在你点击范围的所有元素都会触发事件。事件流包括两种模式:冒泡和捕获(捕捉)

​ 事件冒泡,是从里往外逐个触发。事件捕获,是从外往里逐个触发。那么现代的浏览器默认情况下都是冒泡模型,而捕获模式则是早期的Netscape 默认情况

​ 当我们在 DOM 树的某个节点发生了一些操作(例如单击、鼠标移动上去),就会有一个事件发射过去。这个事件从 Window 发出,不断经过下级节点直到目标节点。在到达目标节点之前的过程,就是捕获阶段(Capture Phase)。所有经过的节点,都会触发这个事件。捕获阶段的任务就是建立这个事件传递路线,以便后面冒泡阶段顺着这条路线返回 Window。

​ 监听某个在捕获阶段触发的事件,需要在事件监听函数传递第三个参数 true。

1573104923299

2.6、案例

  • 跟着鼠标飞的天使
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    body {
      height: 1000px;
    }
    #ts {
      position: absolute;
    }
  </style>
</head>
<body>
  <img src="images/tianshi.gif" id="ts" alt="">
  <script>
    var ts = document.getElementById('ts');
    document.onmousemove = function (e) { 
      e = e || window.event;
      // ts.style.left = e.clientX - 10 + 'px';
      // ts.style.top = e.clientY - 10 + 'px';

      ts.style.left = e.pageX - 10 + 'px';
      ts.style.top = e.pageY - 10 + 'px';
    }
  </script>
</body>
</html>
  • 只能输入数值
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <script>
    
  </script>
</head>
<body>
  <input id="txt" type="text">
  <script>
    // 键盘事件
    // keydown 键盘按下的时候
    // keyup   键盘弹起的时候
    // keydown 和 keyup的区别  keydown的时候我们所按的键还没有落入文本框
    // keyup键盘弹起的时候按的键已经落入文本框
    var txt = document.getElementById('txt');
    txt.onkeydown = function (e) {
      // 判断当前用户按下的键是否是数字
      e = e || window.event;

      // 如果e.keyCode 的值在 48-57 是数字
      // e.keyCode  键盘码
      // console.log(e.keyCode);
      // 按下后退键  8,删除一个字符 
      if ((e.keyCode < 48 || e.keyCode > 57) && e.keyCode !== 8) {
        // 非数字
        // 取消默认行为
        e.preventDefault();
        // return false;
      }

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

三、JavaScript操作CSS样式

3.1、使用style操作样式(重点)

3.1.1、使用规则

​ 任何HTML元素标签都会有一个通用的属性:style,它会返回CSSStyleDeclaration对象。其语法格式为HTML元素.style.样式属性="值"。但是需要注意的是,JavaScript样式属性的写法和CSS写法有点不同,比如字体的大小,CSS代码为font-size,而JavaScript的代码为fontSize。

​ 以下两张表格都是JavaScript写样式属性的,注意和CSS代码的区别。

1573192561359

1573192598031

注意:通过样式属性设置宽高、位置的属性类型是字符串,需要加上px

3.1.2、简单动画案例

效果1:点击改变div的颜色,再次点击返回原样:

1573192623745

<!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>
        #div1 {
            width: 200px;
            height: 200px;
            background: yellow;
        }
    </style>
</head>

<body>
    <div id="div1">

    </div>
    <script>
        // 获取div1元素节点
        var div1 = document.getElementById("div1");

        var flag = 1; // 如果是1的时候,代表的黄色,要变为红色
        div1.onclick = function () {
            if (flag === 1) {
                div1.style.background = "red";
                flag = 2; //改变flag的值
            } else {
                div1.style.background = "yellow";
                flag = 1; //改变flag的值
            }
        }
    </script>
</body>

</html>

效果2:鼠标移入列表改变背景图片,移出恢复原样:

1573192691578

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

        ul {
            list-style: none;
            font-size: 12px;
            color: blue;
        }

        li {
            display: inline-block;
            width: 87px;
            height: 30px;
            line-height: 30px;
            text-align: center;
            user-select: none;
            cursor: pointer;
            background: url("images/5.png");
        }

        /* li:hover {
            background: url("images/3.png");
        } */
    </style>
</head>

<body>
    <ul id="ul1">
        <li>首 页</li>
        <li>水果生鲜</li>
        <li>牛奶饮品</li>
        <li>帮助中心</li>
        <li>关于我们</li>
    </ul>
    <script>
        var ul1 = document.getElementById("ul1");
        ul1.onmouseover = function (e) {
            e = e || window.event;
            var target = e.target || e.srcElement;

            if (target.nodeName.toLowerCase() === 'li') {
                console.log("点击了" + target.innerHTML); // target就是元素节点
                // console.log(target.style); // CSSStyleDeclaration
                // css样式写的是什么值,现在通过js来进行操作的也应是什么值
                target.style.background = 'url("images/3.png")';
            }
        }

        ul1.onmouseout = function (e) {
            e = e || window.event;
            var target = e.target || e.srcElement;

            if (target.nodeName.toLowerCase() === 'li') {
                console.log("点击了" + target.innerHTML); // target就是元素节点
                // console.log(target.style); // CSSStyleDeclaration
                // css样式写的是什么值,现在通过js来进行操作的也应是什么值
                target.style.background = 'url("images/5.png")';
            }
        }
    </script>
</body>

</html>

3.1.3、CSSStypeDeclaration的属性和方法

​ 正如上面提到的,HTML元素.style返回的是返回CSSStypeDeclaration对象,该对象中定义了一些属性和方法,可以在JavaScript中操作HTML元素的样式。

属性或方法 说明
cssText 访问或设置CSS 代码
length 获取样式属性的数量
getPropertyValue(name) 返回包含给定属性值的CSSValue 对象
item(index) 返回指定位置CSS 属性名称
removeProperty(name) 从样式中删除指定属性
setProperty(name,v) 给属性设置为相应的值
<!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>
        #div1 {
            width: 300px;
            height: 200px;
            background-color: yellow;
        }
    </style>
</head>

<body>

    <div id="div1" style="font-size: 12px;color:green;border: 1px solid black;">
        我是div的文本内容
    </div>

    <script>
        // 获取得到div1元素节点
        var div1 = document.getElementById("div1");

        // 元素节点.style   ->CSSStyleDeclaration
        console.log(div1.style);

        var cssStyleDec = div1.style;

        // cssText      访问或设置CSS 代码
        // 获取得到行内样式
        console.log("cssText代码:" + cssStyleDec.cssText);

        // 三次dom操作
        // cssStyleDec.fontSize = "20px";
        // cssStyleDec.color = "hotpink";
        // cssStyleDec.border = "3px solid blue";


        // 结合今天上午讲的内容,好好想一下? 
        // cssStyleDec.cssText = "font-size: 20px;color:hotpink;border: 1px solid blue;";

        // 一次性设置多个样式,可以怎么办? 通过className属性替换类

        // 获取样式属性的数量
        // console.log("样式属性的数量:" + cssStyleDec.length)

        // getPropertyValue(name)   返回包含给定属性值的CSSValue 对象
        console.log("div1的字体颜色:" + cssStyleDec.getPropertyValue("color"));
        console.log("div1的字体颜色:" + cssStyleDec.color);

        // 两者都是可以获取到行内样式的值,但是第一种方式对于-分割的样式,必须去除中划线,中划线后面的首字母大小
        console.log("div1的字体颜色:" + cssStyleDec.fontSize);
        // 第二种方式,可以使用css样式属性名
        console.log("div1的字体大小:" + cssStyleDec.getPropertyValue("font-size"));

        // item(index) 返回指定位置CSS 属性名称
        console.log("-------------------------------");
        for (var i = 0; i < cssStyleDec.length; i++) {
            console.log(cssStyleDec[i]);
        }

        // removeProperty(name) 从样式中删除指定属性
        cssStyleDec.removeProperty("color");


        cssStyleDec.setProperty("color", "skyblue");

        cssStyleDec.color = "red";
    </script>
</body>

</html>

3.2、使用className访问样式

​ 除了使用HTML元素.style.样式属性="值"来访问元素的样式外,这种方法只能对元素的样式一个一个的访问,如果样式设置比较多,就比较繁琐。还可以使用HTML元素.className=’类样式名’来设置元素的样式,这时可以把多个样式写在同一个类样式中。

案例: 用className实现菜单制作:

​ 实现思路:
​ 1、设置每一个li标签的初始状态。
​ 2、设置两个样式over和out,表示鼠标移至菜单和移出菜单的效果。
​ 3、统一为每一个li标签设置onmouseover事件和onmouseout事件效果

1573193051509

1573193056959

3.3、操作内部或外部CSS样式(了解)

​ 之前我们使用style 属性,仅仅只能获取和设置行内的样式,如果是通过内联<style>或链接<link>提供的样式规则就无可奈何了,CSSStyleSheet 类型表示通过<link>元素和<style>元素包含的样式表。

通过代码document.getElementsByTagName('link')[0]; 获取一个HTMLLinkElement对象;

通过document.getElementsByTagName('style')[0];获取一个HTMLStyleElement。

这两个元素本身返回的是HTMLLinkElement 和HTMLStyleElement 类型,但CSSStyleSheet 类型更加通用一些。

通过sheet属性,可以把 HTMLLinkElement 和 HTMLStyleElement 对象转换为CSSStyleSheet 类型

常见CSSStyleSheet属性和方法:

属性或方法 说明
disabled 获取和设置样式表是否被禁用
cssRules 样式表包含样式规则的集合
deleteRule(index) 删除cssRules 集合中指定位置的规则
insertRule(rule, index) 向cssRules 集合中指定位置插入rule 字符串,IE也可以用addRule(选择器名, 属性名:属性值, 索引)
var img1=document.getElementById("img");

var link = document.getElementsByTagName('link')[0];

var sheet = link.sheet || link.styleSheet; //得到CSSStyleSheet

sheet.insertRule("body {background-color:red}", 2);

link.sheet.deleteRule(2);

​ 通过CSSRules 属性,我们可以获得样式表的规则集合列表。这样我们就可以对每个样式进行具体的操作了。

​ 通过cssRules[下标值]可以获取一个CSSStyleRule对象,通过访问CSSStyleRule对象来操作样式。

CSSStyleRule常用属性:

属性 说明
cssText 获取当前整条规则对应的文本,IE 不支持
selectorText 获取当前规则的选择符文本
style 返回CSSStyleDeclaration 对象,可以获取和设置样式
rule.cssText; //当前规则的样式文本
rule.selectorText; //#box,样式的选择符
rule.style.color; //red,得到具体样式值

通过计算属性操作样式

//精准操作内部和外部css样式 [这种方式就是获取到作用域元素的样式,然后再去操作]

//1. 获取到要操作样式的元素节点
var div1 = document.getElementById("div1"); //获取到div1元素节点

//2. 获取到作用与div1的所有样式 CSSStyleDecalaration
var div1Style = getComputedStyle(div1);   //window.getComputedStyle(元素节点)   获取到作用于这个元素节点的样式    
            
//只能获取到作用于元素的样式
document.write("div1的宽:"+div1Style.width+"<br>");
document.write("div1的字体大小:"+div1Style.fontSize+"<br>");

 // div1Style.width = "500px";

3.4、案例

设置div的大小和位置

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    #box {
      width: 100px;
      height: 100px;
      background-color: pink;
    }

    .cls {
      width: 200px;
      height: 200px;
      position: absolute;
      top: 100px;
      left: 100px;
      background-color: pink;
    }
  </style>
</head>
<body>
  <input type="button" value=" 点我 " id="btn">
  <br>
  <div id="box"></div>
  <script>
  	 btn.onclick = function () {
      // 改变box的大小和位置
      // 使用class
      // box.className = 'cls';
      // 
      // 使用style  设置大小和位置的时候 数字必须带单位
      box.style.width = '200px';
      box.style.height = '200px';

      box.style.position = 'absolute';
      box.style.left = '200px';
      box.style.top = '200px';

      console.log(box.style);
    }

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

div显示或隐藏

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

<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        #box {
            width: 200px;
            height: 200px;
            background-color: red;
        }
    </style>
</head>

<body>
    <input type="button" value=" 点我 " id="btn">
    <br>
    <div id="box"></div>
    <script>
        // 获取得到页面元素节点
        var btn = document.getElementById("btn");
        var box = document.getElementById("box");
        var flag = 1;
        var boxHeight = 200;
        var boxOpacity = 1;

        // 隐藏的时候: 高度逐渐变为0,并且透明度变为0
        // 1500ms
        btn.onclick = function () {
            if (flag === 1) { // 隐藏
                // box.style.display = "none";
                var interId = setInterval(function () {
                    // 不能使用boxHeight / 30
                    boxHeight = boxHeight - 200 / 30;
                    boxOpacity = boxOpacity - 1 / 30;
                    box.style.height = boxHeight + "px";
                    box.style.opacity = boxOpacity;
                    if (boxHeight <= 0 || boxOpacity <= 0) {
                        box.style.opacity = 0;
                        box.style.height = "0px";

                        clearInterval(interId);
                    }
                }, 10)
                flag = 2;
            } else { // 显示
                // box.style.display = "block";
                var interId = setInterval(function () {
                    // 不能使用boxHeight / 30
                    boxHeight = boxHeight + 200 / 30;
                    boxOpacity = boxOpacity + 1 / 30;
                    box.style.height = boxHeight + "px";
                    box.style.opacity = boxOpacity;
                    if (boxHeight >= 200 || boxOpacity >= 1) {
                        box.style.opacity = 1;
                        box.style.height = "200px";

                        clearInterval(interId);
                    }
                }, 10)
                flag = 1;
            }
        }
    </script>
</body>

</html>

开关灯

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>
<body>
  <input type="button" value="关灯" id="btn">
    
  <script>
    // 是否开灯  false 关灯状态  true 开灯状态
    var isOpen = true;
    btn.onclick = function () {
      if (isOpen) {
        document.body.style.backgroundColor = 'black';
        this.value = '开灯';
        isOpen = false;
      } else {
        document.body.style.backgroundColor = 'white';
        this.value = '关灯';
        isOpen = true;
      }
    }
  </script>
  
</body>
</html>
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>灯控页面</title>
    <script src="js/jquery.min.js">
    </script>
    <style>
        * {
            padding: 0;
            margin: 0;
        }

        /*灯控页面的所有样式代码*/
        /*给body设置背景图片,背景图片居中显示,左右平铺*/
        /*url("../images/bg.jpg")  找背景图片
  center 0      水平居中,上下靠顶部
  repeat-x      水平平铺
*/
        body {
            background: #404040 url("images/bg.jpg") center 0 repeat-x;
        }

        /*选择titleDiv元素*/
        .titleDiv {
            font-size: 24px;
            /*设置字体大小*/
            font-weight: bold;
            /*设置字体粗体*/
            color: yellow;
            /*设置字体颜色*/
            margin: 30px 0 -5px 270px;
            /*设置元素的外边距*/
        }

        /*选择lampDiv元素*/
        .lampDiv {
            width: 340px;
            /*设置宽度*/
            height: 520px;
            /*设置高度*/
            margin: 0 auto;
            /*设置左右居中*/
        }

        /*选择lampBtmDiv元素*/
        .lampBtmDiv {
            width: 366px;
            /*设置宽度*/
            height: 212px;
            /*设置高度*/
            background: url("images/light_btm_bg.png");
            /*设置背景图片*/
            margin: -80px auto 0;
            position: relative;
            /*给该元素设置定位属性,为了可以设置z轴的关系*/
            z-index: 2;
            text-align: center;
        }

        /*选择lampBtmDiv下面的img元素*/
        .lampBtmDiv>img {
            margin-top: 25px;
            /*这里可以通过外边距让元素居中,但是不能用auto*/
            cursor: pointer;
        }

        /*选择colorBtnDiv元素*/
        .colorBtnDiv {
            width: 430px;
            /*设置宽度*/
            height: 66px;
            /*设置高度*/
            margin: -27px auto 0;
            /*设置居中*/
            position: relative;
            /*给该元素设置定位属性,为了可以设置z轴的关系*/
            z-index: 3;
        }

        .colorBtnDiv>img {
            cursor: pointer;
        }

        /*分别调整第二张到第五张图片的位置*/
        .twoImg {
            margin-left: 27px;
        }

        .threeImg {
            margin-left: 23px;
        }

        .fourImg {
            margin-left: 19px;
        }

        .fiveImg {
            margin-left: 20px;
        }

        /*选择textTips*/
        .textTips {
            text-align: center;
            margin: 35px 0;
            color: #8B978F;
            font-weight: bold;
        }

        /*设置进度框样式*/
        .progress {
            width: 525px;
            height: 30px;
            background: #101823;
            margin: 0 auto;
            border-radius: 8px;
            box-shadow: 0 0 1px 2px #171D22;
        }

        /*设置btnListDiv的样式*/
        .btnListDiv {
            text-align: center;
            margin: 25px 0 35px;
        }

        /*设置所有的亮度按钮样式*/
        .btnListDiv>button {
            padding: 5px 13px;
            margin: 0 5px;
            border-radius: 5px;
            cursor: pointer;
        }

        /*进度条*/
        .progress-bar {
            height: 100%;
            border-radius: 8px;
        }
    </style>
</head>

<body>
    <!--标题内容-->
    <div class="titleDiv">
        智能家居灯控系统
    </div>


    <!--定义灯泡-->
    <div class="lampDiv">
        <img src="images/l-close.png" alt="">
    </div>

    <!--定义灯的底座-->
    <div class="lampBtmDiv">
        <!--lampStatus="closed" 的作用是为了让程序员自身知道是开灯还是关灯;-->
        <img src="images/switch-close.png" alt="" title="点击开关" lampStatus="closed">
    </div>

    <!--定义颜色按钮区域-->
    <div class="colorBtnDiv">
        <!--顺序是红 黄 蓝 绿 紫-->
        <img src="images/off-red.png" alt="" class="oneImg">
        <img src="images/off-yellow.png" alt="" class="twoImg">
        <img src="images/off-blue.png" alt="" class="threeImg">
        <img src="images/off-green.png" alt="" class="fourImg">
        <img src="images/off-purple.png" alt="" class="fiveImg">
    </div>

    <!--定义文本提示-->
    <div class="textTips">
        点击开关开启;点击下边颜色按钮可以换色!
    </div>


    <!--定义页面动态效果的代码-->
    <script>
        /*获取到开关按钮图片元素*/
        var onOffImgBtn = $(".lampBtmDiv>img");

        /*获取到灯泡图片元素*/
        var lampImg = $(".lampDiv>img");

        /*获取到五个颜色按钮*/
        var allColorBtns = $(".colorBtnDiv>img");

        /*获取到进度条*/
        var progressBar = $(".progress-bar");

        /*获取到所有的亮度按钮*/
        var allLightBtns = $(".btnListDiv>button");

        /* 获取要操作设备的IP */
        var ip = $("#ip").val(); /* 获取到ip的值 */

        /* 获取灯开关状态的值 */
        var lampOnOff = $("#lampOnOff").val();

        if (lampOnOff == "11") {
            var lampLight = $("#lampLight").val();
            onOffImgBtn.attr("src", "images/switch-red.png");
            lampImg.attr("src", "images/l-red.png");
            $(".oneImg").attr("src", "images/on-red.png");
            onOffImgBtn.attr("lampStatus", "open");

        }

        /*给开关按钮图片添加单击事件,当我们点击图片的时候要让他做某些事情,这是代码写在function的{}*/
        onOffImgBtn.click(function () {
            /*这里写单击事件触发的时候,要做的事情
             * 每次触发点击的时候,都要获取lampStatus的值,判断是开灯还是关灯
             * */
            /*我们通过获取图片自定义属性lampStatus的值来判断到底是开灯还是关灯*/
            /*改变onOffImgBtn图片的路径,只需要改变onOffImgBtn元素的src属性的值*/
            var lampStatus = onOffImgBtn.attr("lampStatus");
            if (lampStatus == "closed") {
                onOffImgBtn.attr("src", "images/switch-red.png");
                lampImg.attr("src", "images/l-red.png");
                $(".oneImg").attr("src", "images/on-red.png");

                /*设置进度条100%,颜色为红色*/
                progressBar.css("width", "100%");
                progressBar.css("background", "red");

                /*设置状态为开灯*/
                onOffImgBtn.attr("lampStatus", "open");


            } else {
                onOffImgBtn.attr("src", "images/switch-close.png");
                lampImg.attr("src", "images/l-close.png");
                //调用关闭所有颜色按钮的函数
                closeAllBtns();

                progressBar.css("width", "0");

                /*设置状态为关灯*/
                onOffImgBtn.attr("lampStatus", "closed");

            }
        });


        /*自定义一个数组,来保存颜色值:必须和图片的颜色名称一致*/
        var colorArr = ["red", "yellow", "blue", "green", "purple"];


        /*给五个颜色按钮添加点击事件*/
        /*allColorBtns 本质是一个数组   [红色按钮元素,黄色按钮元素,蓝色按钮元素,绿色按钮元素,紫色按钮元素]*/
        allColorBtns.click(function () {
            /*必须是开灯状态才可以调节灯的颜色*/
            /*如果不是开灯状态,则警告框提示;如果是开灯状态,则调节颜色*/
            var lampStatus = onOffImgBtn.attr("lampStatus"); /*灯的状态*/
            if (lampStatus == "closed") {
                alert("必须是开灯状态才能调节灯的颜色,请开灯!");
            } else {
                /*点击哪个颜色按钮,则哪个颜色按钮变为on-xxx.png,其他颜色按钮都是off-xxx.png*/
                /*获取点击元素的下标*/
                var index = $(this).index();

                //调用关闭所有颜色按钮的函数
                closeAllBtns();

                /*从allColorBtns数组里面获取到要改为on-xxx.png的元素*/
                allColorBtns.eq(index).attr("src", "images/on-" + colorArr[index] + ".png");
                onOffImgBtn.attr("src", "images/switch-" + colorArr[index] + ".png");
                lampImg.attr("src", "images/l-" + colorArr[index] + ".png");
            }
        });


        /*定义一个函数,用来关闭所有的颜色按钮,提高代码重用率*/
        function closeAllBtns() {
            /*先把所有的颜色按钮都改为关闭状态*/
            for (var i = 0; i < allColorBtns.length; i++) {
                /*循环获取每个数组里面的元素*/
                allColorBtns.eq(i).attr("src", "images/off-" + colorArr[i] + ".png");
            }
        }
    </script>
</body>

</html>

点击菜单项切换选中状态

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

<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    #menu ul li {
      list-style-type: none;
      line-height: 30px;
      background-color: beige;
      text-align: center;
      float: left;
      margin-left: 5px;
    }

    #menu ul li.current {
      background-color: burlywood;
    }

    #menu ul li a {
      display: inline-block;
      width: 80px;
      height: 30px;
      text-decoration: none;
    }
  </style>
</head>

<body>
  <div id="menu">
    <ul>
      <li class="current"><a href="javascript:void(0)">首页</a></li>
      <!--<li><a href="javascript:undefined">博客</a></li>-->
      <li><a href="javascript:void(0)">博客</a></li>
      <li><a href="javascript:void(0)">相册</a></li>
      <li><a href="javascript:void(0)">关于</a></li>
      <li><a href="javascript:void(0)">帮助</a></li>
    </ul>
  </div>

  <script>
    // void 是运算符   
    // 执行void后面的表达式,并始终返回undefined

    var menu = document.getElementById("menu");
    // 获取menu中的ul
    var ul = menu.firstElementChild;

    for (var i = 0; i < ul.children.length; i++) {
      var li = ul.children[i];
      // 获取li中的a标签
      var link = li.firstElementChild;
      // 注意:此时是把函数赋给onclick 而不是函数的调用
      link.onclick = linkClick;
    }

    function linkClick() {
      // 所有的li取消高亮显示 
      for (var i = 0; i < ul.children.length; i++) {
        var li = ul.children[i];
        li.className = '';
      }

      // 让当前a标签所在的li高亮显示
      // 
      // this是当前点击的a标签对应的元素
      this.parentNode.className = 'current';

      // 取消后续内容的执行
      return false;
    }
  </script>
</body>

</html>

动态生成列表

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

<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>

<body>
  <input type="button" value="按钮" id="btn">
  <div id="box"></div>

  <script>
    var datas = ['西施', '貂蝉', '凤姐', '芙蓉姐姐'];

    var box = document.getElementById("box");
    // 点击按钮 动态创建列表,鼠标放上高亮显示
    var btn = document.getElementById("btn");

    btn.onclick = function () {
      // 动态创建ul,内存中创建对象
      var ul = document.createElement('ul');
      // 把ul 放到页面上    把ul放到DOM树上,并且会重新绘制
      box.appendChild(ul);

      for (var i = 0; i < datas.length; i++) {
        var data = datas[i];
        // 在内存中动态创建li
        var li = document.createElement('li');
        // 把li添加到DOM树,并且会重新绘制
        ul.appendChild(li);
        // 设置li中显示的内容
        // li.innerText = data;
        setInnerText(li, data);

        // 给li注册事件
        li.onmouseover = liMouseOver;
        li.onmouseout = liMouseOut;
      }
    }
    // 当鼠标经过li的时候执行
    function liMouseOver() {
      // 鼠标经过高亮显示
      this.style.backgroundColor = 'red';
    }

    function liMouseOut() {
      // 鼠标离开取消高亮显示
      this.style.backgroundColor = '';
    }

    // 设置标签之间的内容
    function setInnerText(element, content) {
      // 判断当前浏览器是否支持 innerText
      if (typeof element.innerText === 'string') {
        element.innerText = content;
      } else {
        element.textContent = content;
      }
    }
  </script>
</body>

</html>

四、offset、client、scroll三大家

4.1、偏移量

  • offsetParent用于获取定位的父级元素
  • offsetParent和parentNode的区别

1498743216279

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

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    body {
      margin: 0;
    }

    #box {
      /* position: relative; */
      width: 300px;
      height: 300px;
      background-color: red;
      /* 解决顶部外边距问题 */
      overflow: hidden;
      margin: 50px;
    }

    #child {
      width: 100px;
      height: 100px;
      background-color: blue;
      margin: 50px;
      border: 10px solid yellow;
      padding: 10px;
    }
  </style>
</head>

<body>
  <div id="box">
    <div id="child">

    </div>
  </div>

  <script>
    // 3组和大小 位置相关的属性
    // offset  client  scroll
    // 
    // offset  偏移量
    var box = document.getElementById('box');
    // 获取box的坐标
    console.log(box.offsetLeft);
    console.log(box.offsetTop);
    // 获取box的大小
    console.log(box.offsetWidth);
    console.log(box.offsetHeight);

    console.log("----------------")

    // offsetParent   获取距离当前元素最近的定位父元素,如果没有定位父元素此时是body

    // 获取子元素的位置和大小
    var child = document.getElementById('child');
    console.log(child.offsetParent);
    // 获取child的位置     offsetLeft 距离offsetParent的横向偏移
    console.log(child.offsetLeft);
    console.log(child.offsetTop);

    // 获取child的大小   包括边框和padding
    console.log(child.offsetWidth);
    console.log(child.offsetHeight);
  </script>
</body>

</html>

4.2、客户区大小

1504075813134

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

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    body {
      margin: 0;
    }

    #box {
      width: 100px;
      height: 100px;
      margin: 50px;
      border: 30px solid red;
      padding: 10px;
      background-color: green;
    }
  </style>
</head>

<body>
  <div id="box">

  </div>
  <script>
    // client
    var box = document.getElementById('box');
    // clientLeft  是border-left 的宽度
    // clientTop    border-top 的宽度
    console.log(box.clientLeft);
    console.log(box.clientTop);


    // 获取大小   包括padding  但是不包括边框
    console.log(box.clientWidth);
    console.log(box.clientHeight);

    // offsetWidth   offsetHeight     包括padding和边框
  </script>
</body>
</html>

4.3、滚动偏移

1498743288621

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

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    body {
      margin: 0;
    }

    #box {
      width: 100px;
      height: 100px;
      margin: 50px;
      border: 30px solid red;
      padding: 10px;
      background-color: green;
      overflow: auto;
    }
  </style>
</head>

<body>
  <div id="box">
    超文本是一种组织信息的方式,它通过超级链接方法将文本中的文字、图表与其他信息媒体相关联。这些相互关联的信息媒体可能在同一文本中,也可能是其他文件,或是地理位置相距遥远的某台计算机上的文件。
  </div>
  <script>
    // scroll
    var box = document.getElementById('box');
    // 当拖动box中的滚动条的时候触发
    box.onscroll = function () {
      console.log(box.scrollLeft);
      console.log(box.scrollTop);

    }


    // // box滚动出去的距离
    // console.log(box.scrollLeft);
    // console.log(box.scrollTop);

    // // 内容的大小,包括padding 和未显示的内容,不包括滚动条
    // console.log(box.scrollWidth);
    // console.log(box.scrollHeight);

    // // 元素的大小 + padding   不包括滚动条
    // console.log(box.clientWidth);
    // console.log(box.clientHeight);
  </script>
</body>

</html>

4.4、案例

  • div匀速移动
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    body {
      margin: 0;
    }
    #box {
      position: relative;
      background-color: red;
      width: 100px;
      height: 100px;
    }
  </style>
</head>
<body>
  <input type="button" value="开始" id="btn">
  <div id="box"></div>
  <script>
    var btn = document.getElementById('btn');
    var box = document.getElementById('box');
    btn.onclick = function () {
      var step = 6; 
      var target = 500;
      var timerId = setInterval(function () {
        if (box.offsetLeft >= target) {  
          clearInterval(timerId);
          box.style.left = target + 'px';
          return;
        }
        box.style.left = box.offsetLeft + step + 'px';
      }, 30);
    }
  </script>
</body>
</html>
  • 获取鼠标在div内的坐标

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    body {
      margin: 0;
    }

    #box {
      width: 300px;
      height: 300px;
      border: 1px solid red;
      margin: 100px 10px 10px 100px;
    }
  </style>
</head>
<body>
  <div id="box">
    
  </div>
  <script src="common.js"></script>
  <script>
    var box = document.getElementById('box');
    box.onclick = function (e) {
      // 获取盒子在页面上的位置
      // console.log(this.offsetLeft);
      // console.log(this.offsetTop);

      e = e || window.event;
      // 获取鼠标在盒子中的位置 = 鼠标的坐标 - 盒子的坐标
      // var x = e.pageX
      var x = getPage(e).pageX - this.offsetLeft;
      var y = getPage(e).pageY - this.offsetTop;
      console.log(x);
      console.log(y);

    }
  </script>
</body>
</html>
  • 拖拽案例
<!DOCTYPE html>
<html>

<head lang="en">
  <meta charset="UTF-8">
  <title></title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    .nav {
      height: 30px;
      background: #87cefa;
      line-height: 30px;
      padding-left: 30px;
    }

    .nav a {
      color: #fff;
      text-align: center;
      font-size: 14px;
      text-decoration: none;

    }

    .d-box {
      width: 400px;
      height: 300px;
      border: 5px solid #eee;
      box-shadow: 2px 2px 2px 2px #666;
      position: absolute;
      top: 50%;
      left: 50%;
      background-color: white;
      /* 不让文字被选中 */
      user-select: none;
      transform: translate(-50%, -50%);
    }

    .hd {
      width: 100%;
      height: 35px;
      background-color: #87cefa;
      border-bottom: 1px solid #369;
      line-height: 35px;
      color: white;
      cursor: move;
    }

    #box_close {
      float: right;
      cursor: pointer;
    }
  </style>
</head>

<body>
  <div class="nav">
    <a href="javascript:;" id="register">注册信息</a>
  </div>
  <div class="d-box" id="d_box">
    <div class="hd" id="drop">注册信息 (可以拖拽)
      <span id="box_close">【关闭】</span>
    </div>
    <div class="bd"></div>
  </div>

  <script>
    // 获取盒子
    var box = document.getElementById('d_box');
    // 获取盒子标题区块
    var drop = document.getElementById('drop');

    drop.onmousedown = function (e) {
      // 兼容性处理
      var e = e || window.event;

      // 当鼠标在盒子标题区域按下的时候,获取鼠标在盒子里面的下标
      // 鼠标在盒子的下标 = 鼠标在页面的的下标 - 盒子的偏移量;
      var x = e.pageX - box.offsetLeft;
      var y = e.pageY - box.offsetTop;

      console.log(x + " " + y);

      document.onmousemove = function (e) {
        // 兼容性处理
        var e = e || window.event;
        // 当鼠标在页面上移动的时候。求盒子的坐标
        // 盒子的坐标 = 鼠标当前在页面中的位置 - 鼠标在盒子中的位置
        var boxX = e.pageX - x;
        var boxY = e.pageY - y;
        box.style.left = boxX + "px";
        box.style.top = boxY + "px";

      }
    }

    // 当鼠标在页面文档中弹起,移除移动跟随
    document.onmouseup = function () {
      document.onmousemove = null;
    }

    // 点击关闭按钮,隐藏盒子
    var box_close = document.getElementById('box_close');
    box_close.onclick = function () {
      box.style.display = 'none';
    }
  </script>
</body>

</html>
  • 弹出登录窗口
<!DOCTYPE html>
<html>

<head lang="en">
  <meta charset="UTF-8">
  <title></title>
  <style>
    .login-header {
      width: 100%;
      text-align: center;
      height: 30px;
      font-size: 24px;
      line-height: 30px;
    }

    ul,li,ol,dl,dt,dd,div,p,span,h1,h2,h3,h4,h5,h6,a {
      padding: 0px;
      margin: 0px;
    }

    .login {
      width: 512px;
      height: 280px;
      position: absolute;
      border: #ebebeb solid 1px;
      left: 50%;
      top: 50%;
      background: #ffffff;
      box-shadow: 0px 0px 20px #ddd;
      z-index: 9999;
      display: none;
      transform: translate(-50%, -50%);
    }

    .login-title {
      width: 100%;
      margin: 10px 0px 0px 0px;
      text-align: center;
      line-height: 40px;
      height: 40px;
      font-size: 18px;
      position: relative;
      cursor: move;
      -moz-user-select: none;
      /*火狐*/
      -webkit-user-select: none;
      /*webkit浏览器*/
      -ms-user-select: none;
      /*IE10*/
      -khtml-user-select: none;
      /*早期浏览器*/
      user-select: none;
    }

    .login-input-content {
      margin-top: 20px;
    }

    .login-button {
      width: 50%;
      margin: 30px auto 0px auto;
      line-height: 40px;
      font-size: 14px;
      border: #ebebeb 1px solid;
      text-align: center;
    }

    .login-bg {
      width: 100%;
      height: 100%;
      position: fixed;
      top: 0px;
      left: 0px;
      background: #000000;
      filter: alpha(opacity=30);
      -moz-opacity: 0.3;
      -khtml-opacity: 0.3;
      opacity: 0.3;
      display: none;
    }

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

    .login-button a {
      display: block;
    }

    .login-input input.list-input {
      float: left;
      line-height: 35px;
      height: 35px;
      width: 350px;
      border: #ebebeb 1px solid;
      text-indent: 5px;
    }

    .login-input {
      overflow: hidden;
      margin: 0px 0px 20px 0px;
    }

    .login-input label {
      float: left;
      width: 90px;
      padding-right: 10px;
      text-align: right;
      line-height: 35px;
      height: 35px;
      font-size: 14px;
    }

    .login-title span {
      position: absolute;
      font-size: 12px;
      right: -20px;
      top: -30px;
      background: #ffffff;
      border: #ebebeb solid 1px;
      width: 40px;
      height: 40px;
      border-radius: 20px;
    }
  </style>
</head>

<body>
  <div class="login-header"><a id="link" href="javascript:void(0);">点击,弹出登录框</a></div>
  <div id="login" class="login">
    <div id="title" class="login-title">登录会员
      <span><a id="closeBtn" href="javascript:void(0);" class="close-login">关闭</a></span>
    </div>
    <div class="login-input-content">
      <div class="login-input">
        <label>用户名:</label>
        <input type="text" placeholder="请输入用户名" name="info[username]" id="username" class="list-input">
      </div>
      <div class="login-input">
        <label>登录密码:</label>
        <input type="password" placeholder="请输入登录密码" name="info[password]" id="password" class="list-input">
      </div>
    </div>
    <div id="loginBtn" class="login-button"><a href="javascript:void(0);" id="login-button-submit">登录会员</a></div>
  </div>
  <!-- 遮盖层 -->
  <div id="bg" class="login-bg"></div>
  <script>
    // 显示登录框和遮盖层
    var login = document.getElementById('login');
    var bg = document.getElementById('bg');
    //1 点击按钮,弹出登录框和遮盖层
    var link = document.getElementById('link');
    link.onclick = function () {
      login.style.display = 'block';
      bg.style.display = 'block';
      return false;
    }

    // 2 点击关闭按钮,隐藏 登录框和遮盖层
    var closeBtn = document.getElementById('closeBtn');
    closeBtn.onclick = function () {
      // 隐藏 登录框和遮盖层
      login.style.display = 'none';
      bg.style.display = 'none';
    }

    // 3 拖拽
    var title = document.getElementById('title');
    title.onmousedown = function (e) {
      // 鼠标按下,求鼠标在盒子中的位置
      var x = e.pageX - login.offsetLeft;
      var y = e.pageY - login.offsetTop;

      document.onmousemove = function (e) {
        // 鼠标移动的时候, 盒子的坐标
        var loginX = e.pageX - x;
        var loginY = e.pageY - y;

        login.style.left = loginX + 'px';
        login.style.top = loginY + 'px';
      }
    }

    document.onmouseup = function () {
      // 移除鼠标移动的事件
      document.onmousemove = null;
    }
  </script>
</body>

</html>
  • 放大镜案例

    1、鼠标经过box的时候 显示 mask和bigBox , 当鼠标离开box的时候隐藏mask和bigBox

    2、当鼠标在盒子中移动的时候,让mask和鼠标一起移动

    3、当mask移动的时候,让大图片移动

    ​ mask移动的距离 / mask最大能够移动的距离 = 大图片移动的距离 / 大图片最大能够移动的距离

<!DOCTYPE html>
<html>

<head lang="en">
  <meta charset="UTF-8">
  <title></title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    .box {
      width: 350px;
      height: 350px;
      margin: 100px;
      border: 1px solid #ccc;
      position: relative;
    }

    .big {
      width: 400px;
      height: 400px;
      position: absolute;
      top: 0;
      left: 360px;
      border: 1px solid #ccc;
      overflow: hidden;
      display: none;
    }

    .big img {
      position: absolute;
    }

    .mask {
      width: 175px;
      height: 175px;
      background: rgba(255, 255, 0, 0.4);
      position: absolute;
      top: 0px;
      left: 0px;
      cursor: move;
      display: none;
    }

    .small {
      position: relative;
    }
  </style>
</head>

<body>
  <div class="box" id="box">
    <div class="small">
      <img src="images/small.jpg" width="350" alt="" />
      <div class="mask"></div>
    </div>
    <div class="big">
      <img src="images/big.jpg" width="800" alt="" />
    </div>
  </div>
  <script>
    // 获取box、small盒子、big盒子
    var box = document.getElementById("box");
    var smallBox = box.children[0];
    var bigBox = box.children[1];

    // 获取小图片
    var smallImg = smallBox.children[0];
    // 获取遮罩
    var mask = smallBox.children[1];
    // 获取大图片
    var bigImg = bigBox.children[0];

    // 1、 鼠标经过box的时候 显示 mask和bigBox, 当鼠标离开box的时候隐藏mask和bigBox
    box.onmouseover = function () {
      mask.style.display = "block";
      bigBox.style.display = "block";
    }

    box.onmouseout = function () {
      mask.style.display = "none";
      bigBox.style.display = "none";
    }

    // 2、 当鼠标在盒子中移动的时候, 让mask和鼠标一起移动
    box.onmousemove = function (e) {
      // 获取鼠标在盒子里面的坐标,也就是mask的坐标
      var maskX = e.pageX - box.offsetLeft;
      var maskY = e.pageY - box.offsetTop;

      // 让鼠标位于盒子中间
      maskX = maskX - mask.offsetWidth / 2;
      maskY = maskY - mask.offsetHeight / 2;

      // 限制mask的活动范围
      maskX = maskX < 0 ? 0 : maskX;
      maskY = maskY < 0 ? 0 : maskY;

      maskX = maskX > box.offsetWidth - mask.offsetWidth ? box.offsetWidth - mask.offsetWidth : maskX;
      maskY = maskY > box.offsetHeight - mask.offsetHeight ? box.offsetHeight - mask.offsetHeight : maskY;

      // 把坐标设置给mask
      mask.style.left = maskX + "px";
      mask.style.top = maskY + "px";

      // 3、 当mask移动的时候, 让大图片移动
      //  mask移动的距离 / mask最大能够移动的距离 = 大图片移动的距离 / 大图片最大能够移动的距离
      // mask最大能够移动距离
      var maskMax = box.offsetWidth - mask.offsetWidth;
      var bingImgMax = bigImg.offsetWidth - bigBox.offsetWidth;

      // 计算得出大图片移动距离
      var bigImgX = maskX * bingImgMax / maskMax;
      var bigImgY = maskY * bingImgMax / maskMax;

      bigImg.style.left = -bigImgX + "px";
      bigImg.style.top = -bigImgY + "px";

    }
  </script>
</body>

</html>
  • 回到顶部
<!DOCTYPE html>
<html lang="en">

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

        header {
            width: 1024px;
            height: 150px;
            background: red;
            margin: auto;
        }

        section {
            width: 1024px;
            height: 550px;
            background: yellow;
            margin: 15px auto;

        }

        footer {
            width: 1024px;
            height: 150px;
            background: red;
            margin: auto;
        }

        aside {
            position: fixed;
            right: 0;
            bottom: 100px;
        }
    </style>
</head>

<body>
    <header></header>
    <section></section>
    <section></section>
    <section></section>
    <footer></footer>
    <aside>
        <a href="javascript:void(0)" id="goTop">回到顶部</a>
    </aside>

    <script src="comm.js"></script>
    <script>
        // 无论多远,300ms都要回到顶部
        var goTop = document.getElementById("goTop");

        goTop.onclick = function () {
            // 获取页面滚动出去的距离
            var y = getScroll().scrollTop;

            // 分30次滚动完毕,每次滚动多少距离
            var step = y / 10;

            var interId = setInterval(function () {
                if (getScroll().scrollTop <= 0) {
                    clearInterval(interId);
                    return;
                }
                // x 和 y 每次滚动的距离
                scrollBy(0, -step);
            }, 10)
        }
    </script>
</body>

</html>

五、动画效果

5.1、动态时间显示

1573389094458

需求:

  1. 当页面打开的时候,显示系统时间,并且每1s发生一次变化;
  2. 月日时分秒必须确保是2位数
<!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>
        #timeDiv {
            font-size: 12px;

        }
    </style>
</head>

<body>
    <div id="timeDiv">

    </div>

    <script>
        var timeDiv = document.getElementById("timeDiv");

        setInterval(getTime, 1000)
        getTime();

        function getTime() {
            // 内置对象Date相关API
            var time = new Date();
            // 获取年月日
            var year = time.getFullYear();
            var month = time.getMonth() + 1;
            var date = time.getDate();
            // 获取时分秒
            var hour = time.getHours();
            var minutes = time.getMinutes();
            var seconds = time.getSeconds();

            var apm = hour >= 12 ? "下午" : "上午";

            var timeStr = "今天是 : " + year + "年" + toDoubleDigit(month) + "月" + toDoubleDigit(date) +
                "日,现在时间是 : " + apm + " " + toDoubleDigit(hour) + ":" + toDoubleDigit(minutes) + ":" +
                toDoubleDigit(seconds);

            timeDiv.innerHTML = timeStr;
        }

        function toDoubleDigit(num) {
            return num < 10 ? "0" + num : num;
        }
    </script>
</body>

</html>

5.2、随页面滚动的广告/侧边栏

1573389232791

亚马逊广告效果:

  1. 固定在页面底部,而且居中
  2. 点击右上角的X,可以关闭这个广告

1573389371924

document.documentElement.scrollTop 获取内容距离顶部的距离;

京东侧边栏回到顶部:

1.固定在右侧的一个小导航

2.当我们点击回到顶部的时候,可以过渡回滚到顶部(不能使用锚点,因为锚点是瞬间回到顶部)

<!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>
        body {
            margin: 0;
            height: 1600px;
        }

        #amazonGg {
            width: 700px;
            height: 100px;
            background: yellow;
            position: fixed;
            bottom: 0;
            left: 50%;
            transform: translateX(-50%);
        }

        #amazonGg>span {
            background: #ccc;
            padding: 3px 8px;
            display: inline-block;
            user-select: none;
            cursor: pointer;
            position: absolute;
            right: 0;
            top: 0;
        }
    </style>
</head>

<body>
    <div id="amazonGg">
        <span>&times;</span>
    </div>

    <script>
        var amazonGg = document.querySelector("#amazonGg");
        var closeBtn = document.querySelector("#amazonGg > span");
        closeBtn.onclick = function () {
            amazonGg.style.display = "none";
        }
    </script>
</body>

</html>

5.3、手风琴菜单

1573389474346

需求如下:
1、每张图片都有一个与之对应的内容块,但是这些内容块都是隐藏的

2、当我们点击哪张图片,那么对应的内容块显示,但是其他的内容块隐藏

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;
        }

        body {
            margin-top: 10px;
            margin-left: 100px;
        }

        #bigDiv {
            width: 183px;
            height: 550px;
            overflow: hidden;
            background: url("images/customer.jpg");
        }

        #imgDiv {
            margin: 210px auto 0;
            text-align: center;
            font-size: 14px;
            user-select: none;
            cursor: pointer;
            color: orangered;
            font-weight: bold;
        }

        #imgDiv>img {
            margin-top: -3px;
            cursor: pointer;
        }

        #imgDiv p {
            line-height: 26px;
        }

        #imgDiv>div {
            display: none;
        }
    </style>
</head>

<body>
    <div id="bigDiv">
        <div id="imgDiv">
            <img src="images/reg.jpg" alt="">
            <div>
                <p>点击注册</p>
                <p>认证中心</p>
            </div>
            <img src="images/buy.jpg" alt="">
            <div>
                <p>咨询</p>
                <p>投诉</p>
                <p>举报</p>
                <p>退款</p>
            </div>
            <img src="images/sale.jpg" alt="">
            <div>
                <p>安全中心</p>
                <p>物流中心</p>
                <p>资金流动</p>
                <p>纠纷处理</p>
            </div>
            <img src="images/person.jpg" alt="">
            <div>
                <p>修改资料</p>
                <p>账号申诉</p>
            </div>
        </div>
    </div>

    <script src="comm.js"></script>
    <script>
        var imgDiv = document.getElementById("imgDiv");

        var divs = imgDiv.getElementsByTagName("div");
        imgDiv.onclick = function (e) {
            e = e || window.event;
            var target = e.target || e.srcElement;
            if (target.nodeName.toLowerCase() === "img") {
                var imgTargetNext = getNextElementSibling(target);

                if (imgTargetNext.style.display === "block") {
                    imgTargetNext.style.display = "none"
                } else {
                    // 关闭所有div
                    for (var i = 0; i < divs.length; i++) {
                        divs[i].style.display = "none";
                    }

                    imgTargetNext.style.display = "block";
                }


            }
        }
    </script>
</body>

</html>

5.4、无缝滚动

需求:
1、四张图片的循环滚动(第一张图片上去、那么还是四张图片,第一张应该在最后面....)

2、鼠标移入暂停,移出继续

1573389514671

<!doctype html>
<html>

<head>
    <title>页面标题</title>
    <meta charset="utf-8">
    <style>
        body,
        div,
        p,
        img {
            padding: 0;
            margin: 0;
        }

        .wrapDiv {
            /* 高度一定是计算出来的,一定要精确 */
            height: 277px;
            width: 230px;
            border: 1px solid #ccc;
            margin: 50px;
            border-radius: 5px 5px 0 0;
            position: relative;
            overflow: hidden;
        }

        .titleDiv {
            height: 35px;
            background: linear-gradient(#FBD596 5px, #F6BE81 12px, #F5B607 14px, #F6B908 16px, #F98A07 18px, #F58020);
            color: white;
            line-height: 35px;
            font-weight: bold;
            padding-left: 15px;
            font-size: 14px;
            user-select: none;
            position: relative;
            z-index: 3;
        }

        .cententDiv {
            height: 242px;
            font-size: 12px;
            position: absolute;
            /* 想让cententDiv动起来,把top的值变小即可 */
            top: 35px;
            z-index: 2;
        }

        .cententDiv img {
            /* 只给图片设置左右外边距 */
            margin: 0 10px;
            border: 1px solid #eee;
            vertical-align: middle;
        }

        .cententDiv>p {
            margin: 10px 0;
        }
    </style>
</head>

<body>
    <!-- 
            无缝滚动这个动态效果,需要对布局有非常精准的要求
            首先: 整个wrapDiv的大小要精确计算得出: 标题高度 (35px)+ 内容高度 (四张图片的高度(184)+8px+50px)
        -->
    <!-- wrapDiv是最外层的大div -->
    <div class="wrapDiv">
        <!-- 标题div -->
        <div class="titleDiv">
            新品上架
        </div>

        <!-- 滚动内容块 -->
        <div class="cententDiv" id="cententDiv">
            <p><img src="image/show1.jpg" alt=""> 打牌狂降价,三折直送</p>
            <p><img src="image/show2.jpg" alt=""> 大学要求老师开网店</p>
            <p><img src="image/show3.jpg" alt=""> 黑眼圈推荐,美白不停</p>
            <p><img src="image/show4.jpg" alt=""> 瘦身狂潮,修行之选</p>
            <p><img src="image/show1.jpg" alt=""> 打牌狂降价,三折直送</p>
            <p><img src="image/show2.jpg" alt=""> 大学要求老师开网店</p>
            <p><img src="image/show3.jpg" alt=""> 黑眼圈推荐,美白不停</p>
            <p><img src="image/show4.jpg" alt=""> 瘦身狂潮,修行之选</p>
        </div>
    </div>

    <script>
        var cententDiv = document.getElementById("cententDiv");
        var topVal = 35;
        var interId;
        // 使用定时器每隔多长时间改变cententDiv的top值
        interId = setInterval(slideFun, 20)

        cententDiv.onmouseover = function () {
            clearInterval(interId);
        }

        cententDiv.onmouseout = function () {
            interId = setInterval(slideFun, 20)
        }

        function slideFun() {
            if (topVal === -197) {
                topVal = 35;
            }
            topVal--;
            cententDiv.style.top = topVal + "px";
        }
    </script>
</body>

</html>

5.5、轮播图

1573389606013

常见轮播图分类:

  1. 普通轮播图(无过渡效果)
  2. 滑动轮播图
  3. 淡入淡出轮播图
<!doctype html>
<html>

<head>
    <title>页面标题</title>
    <meta charset="utf-8">
    <style>
        body,
        div,
        ul,
        li,
        img {
            margin: 0;
            padding: 0;
        }

        .slideDiv {
            position: relative;
        }

        .slideDiv img {
            width: 100%;
        }

        .slideDiv>ul {
            list-style: none;
            position: absolute;
            right: 20px;
            bottom: 40px;

        }

        .slideDiv li {
            width: 15px;
            height: 15px;
            background: #CCCACA;
            border: 1px solid white;
            display: inline-block;
            margin: 0 2px;
            cursor: pointer;
        }

        /* 这个.liSelected选择器里面定义是li选中的效果,但是因为优先级问题,没有显示效果 */
        /* 方法1: !important */
        /* 方式2: 通过选择器加强优先级 */
        .slideDiv li.liSelected {
            background: #CC0000;
            border: 1px solid #FF0000;
        }

        .textDiv {
            width: 100%;
            height: 30px;
            background: rgba(0, 0, 0, 0.3);
            position: absolute;
            bottom: 3px;
            font-size: 12px;
            color: white;
            line-height: 30px;
            padding-left: 20%;
            box-sizing: border-box;
        }
    </style>
</head>

<body>
    <!-- 
            普通轮播图,没有任何过渡效果的轮播图
            实现原理:
                每隔一段时间之后,改变图片的路径
         -->
    <div class="slideDiv" id="slideDiv">
        <a href=""><img src="image/product01.jpg" alt="" id="slideImg"></a>
        <ul>
            <li class="liSelected"></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
        </ul>
        <div class="textDiv">
            新开普新闻:新开普获蚂蚁金服战略投资,携手打造校园新生态 2019-01-07
        </div>
    </div>

    <script>
        /*
            任意效果的轮播图,一般都是分两部分完成
                自动轮播
                手动轮播
        */
        // 获取得到图片元素节点
        var slideImg = document.getElementById("slideImg");

        // 用来控制图片值的变化,最小值是1,最大是6;如果到6就变为1
        var imgNum = 1;

        // 获取得到所有的li
        var lis = document.querySelectorAll("#slideDiv li");
        var ulEle = document.querySelector("#slideDiv ul");

        // 获取得到slideDiv
        var slideDiv = document.getElementById("slideDiv");

        var interId;

        // 自动轮播: 每隔指定时间,更换一张图片;每换一张图片,让对应的li处于选中状态(给对应的li添加liSelected类)
        interId = setInterval(sliderFun, 3000)

        function sliderFun() {
            if (imgNum === 6) {
                imgNum = 1;
            } else {
                imgNum++;
            }

            // 修改图片路径
            slideImg.src = "image/product0" + imgNum + ".jpg";

            // 修改对应li的选中效果
            changeLiStyle(imgNum - 1);
        }


        // 根据下标改变li的选中,参数是要选中li的下标
        function changeLiStyle(index) {
            for (var i = 0; i < lis.length; i++) {
                if (i === index) {
                    lis[i].className = "liSelected";
                } else {
                    lis[i].className = "";
                }
            }
        }


        // 手动轮播: 鼠标移入哪个li,则显示对应的图片; 鼠标移入整个slideDiv,那么停止轮播,移出继续轮播
        // 事件委托: 最好委托给直接父元素
        ulEle.onmouseover = function (e) {
            e = e || window.event;
            // 获取事件源
            var target = e.target || e.srcElement;

            // 如果事件源是li,则切换图片;如果是slideDiv,则停止轮播
            if (target.nodeName.toLowerCase() === "li") {
                var liIndex = getLiIndex(target);
                // 根据li的下标,改变li的选中效果
                changeLiStyle(liIndex);
                // 显示对应的图片
                slideImg.src = "image/product0" + (liIndex + 1) + ".jpg";

                imgNum = liIndex + 1;
            }
        }

        slideDiv.onmouseover = function () {
            clearInterval(interId);
        }

        slideDiv.onmouseout = function () {
            interId = setInterval(sliderFun, 3000)
        }

        // 获取li的下标,参数是li元素节点对象
        function getLiIndex(o) {
            var index = -1;
            for (var i = 0; i < lis.length; i++) {
                if (lis[i] === o) {
                    index = i;
                }
            }
            return index;
        }
    </script>
</body>

</html>

5.6、tab切换(tab标签页)

tab标签页: 在有限的区域显示更多的内容;

网易tab切换: 鼠标移入,切换到对应的内容;但是需要停留一定时间才能切换,避免了无脑切换问题;

1573389657375

1573389846131

<!doctype html>
<html>

<head>
    <title>页面标题</title>
    <meta charset="utf-8">
    <style>
        body,
        ul,
        li,
        img,
        div {
            padding: 0;
            margin: 0;
        }

        /* 里面的图片是360*230 */
        .tabDiv {
            width: 360px;
            height: 260px;
            /* border: 1px solid black; */
            margin: 30px;
        }

        .tabDiv>ul {
            list-style: none;
            height: 30px;
            background: #F8F8F8;
            font-size: 14px;
            line-height: 30px;
            user-select: none;
            /* box-sizing: border-box; */
        }

        .tabDiv li {
            display: inline-block;
            padding: 0 15px;
            cursor: pointer;
            /* box-sizing: border-box; */
        }

        /* .tabDiv li:hover{
                background: white;
                border-top: 3px solid orangered;
            } */
        .tabDiv li.selected {
            background: white;
            border-top: 3px solid orangered;
            color: orangered;
        }

        .contentDiv div {
            display: none;
        }

        .contentDiv div.show {
            display: block;
        }
    </style>
</head>

<body>
    <div class="tabDiv" id="tabDiv">
        <!-- 我们使用ul和li来表示可以切换的tab标签 -->
        <ul>
            <li class="selected">图片</li>
            <li>专栏</li>
            <li>热点</li>
        </ul>

        <!-- 我们定义一个 contentDiv,这个contentDIv相当于用来实现对应内容的窗口-->
        <!-- 在contendDiv里面定义三个子div,然后给其添加内容 -->
        <div class="contentDiv" id="contentDiv">
            <div class="show"><img src="image/tupian.png" alt=""></div>
            <div><img src="image/zhuanlan.png" alt=""></div>
            <div><img src="image/redian.png" alt=""></div>
        </div>
    </div>
    <script src="comm.js"></script>
    <script>
        // 鼠标移入哪个li,则让li处于选中状态,其他li恢复默认
        var ulEle = document.querySelector("#tabDiv>ul");

        // 获取得到所有li的集合
        var lis = document.querySelectorAll("#tabDiv li");

        // 获取得到contentDiv里面的所有div元素集合
        var divs = document.querySelectorAll("#contentDiv > div");

        var outId;

        ulEle.onmouseover = function (e) {
            e = e || window.event;
            // 获取得到事件源
            var target = e.target || e.srcElement;

            if (target.nodeName.toLowerCase() === 'li') {
                // 获取到鼠标移入li,在ul里面的下标
                var liIndex = getElementIndex(lis, target);

                outId = setTimeout(function () {
                    // 给指定的li添加指定类,其他li取消该类效果
                    changeElementClass(lis, liIndex, "selected");

                    // 指定div显示,其他div隐藏,指定div添加.show,其他div取消类
                    changeElementClass(divs, liIndex, "show");
                }, 300)
            }
        }

        ulEle.onmouseout = function (e) {
            e = e || window.event;
            // 获取得到事件源
            var target = e.target || e.srcElement;

            if (target.nodeName.toLowerCase() === 'li') {
                clearTimeout(outId);
            }
        }
    </script>
</body>

</html>

5.7、表单的客户端检测

1573389923367

5.8、省市级联

需求:
1、如果没有选择第一个,那么就第二个下拉框就没有内容
2、如果选择了第一个,那么显示第一个对应的内容

1573389948461

<!doctype html>
<html>

<head>
    <title>页面标题</title>
    <meta charset="utf-8">
    <style>
        select {
            width: 200px;
            height: 32px;
        }
    </style>
</head>

<body>
    所在地区:
    <select name="province" id="province">
        <option value="-1">请选择省份</option>
    </select>
    <select name="city" id="city">
        <option value="-1">请选择城市</option>
    </select>

    <script>
        //我们通过JS来定义一些假数据
        //无论是省份/直辖市/城市/地区,我们都使用假数据来存储

        //这个数组里面放:省份/直辖市
        var provinceArr = ["北京市", "河南省", "河北省", "上海市", "山东省"];

        //cityObj
        var cityObj = {
            "北京市": ["海淀区", "朝阳区", "昌平区", "丰台区"],
            "河南省": ["郑州市", "焦作市", "南阳市", "洛阳市", "许昌市", "信阳市", "安阳市", "濮阳市"],
            "河北省": ["石家庄市", "廊坊市", "邢台市", "雄安", "沙河市"],
            "上海市": ["黄浦区", "静安区", "徐汇区"],
            "山东省": ["青岛市", "日照市", "济南市", "烟台市"]
        }

        // 获取省份下拉框
        var province = document.getElementById("province");

        // 给省份select添加节点,provinceArr有几个数据,就在省份select添加几个节点
        for (var i = 0; i < provinceArr.length; i++) {
            // 创建option节点
            var opt = document.createElement("option");
            // 给option节点添加内容和value属性
            opt.value = provinceArr[i];
            opt.innerText = provinceArr[i];

            // 把节点添加到province下拉框
            province.appendChild(opt);
        }

        // 获取得到city元素节点
        var city = document.getElementById("city");

        // 当我们选择了指定的省份之后,在city下拉框里面加载对应的城市
        province.onchange = function () {
            // 每次改变,先清空city下拉列表的内容
            city.innerHTML = '<option value="-1">请选择城市</option>';

            // 获取选择的省份
            var selectProVal = this.value;
            console.log(selectProVal)
            if (selectProVal !== "-1") {
                // 根据省份获取得到省份对应的所有城市
                var cityArray = cityObj[selectProVal]; // 对象名["属性名"]

                for (var i = 0; i < cityArray.length; i++) {
                    // 创建option节点
                    var opt = document.createElement("option");
                    // 给option节点添加内容和value属性
                    opt.value = cityArray[i];
                    opt.innerText = cityArray[i];

                    city.appendChild(opt);
                }
            }
        }
    </script>
</body>

</html>

5.9、购物车操作

需求:
1、修改数量,下面的总价和节省金额发生变化
2、删除商品,提示是否删除,如果确定删除,那么才删除
3、删除之后更新总价和节省金额

1573389969907

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

<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        table,
        td {
            border: 1px solid black;
            border-collapse: collapse;
        }

        table {
            width: 980px;
            text-align: center;
            margin: 0 auto;
        }

        tr {
            height: 30px;
            line-height: 30px;
        }

        thead {
            font-weight: bold;

        }

        input[type=text] {
            width: 40px;
            height: 28px;
            text-align: center;
            border: 1px solid #A9A9A9;
        }

        tr td:nth-child(1) {
            width: 400px;
        }

        .showNull {
            display: none;
        }

        tbody>tr>td:nth-child(5) span {
            border: 1px solid #A9A9A9;
            height: 30px;
            display: inline-block;
            vertical-align: middle;
            width: 25px;
            user-select: none;
            cursor: pointer;
        }

        tbody>tr>td:nth-child(5) span:nth-child(1) {
            border-right: 0;
        }

        tbody>tr>td:nth-child(5) span:nth-child(3) {
            border-left: 0;
        }
    </style>
</head>

<body>
    <div id="showNull" class="showNull">
        购物车空空如也
    </div>
    <table id="shopCar">
        <caption>
            <h3>购物车</h3>
        </caption>
        <thead>
            <tr>
                <td>商品名</td>
                <td>原价</td>
                <td>优惠价</td>
                <td>打折</td>
                <td>数量</td>
                <td>删除</td>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>攀高搓背按摩器(蓝色) 定制版</td>
                <td>¥<span>258.00</span></td>
                <td>¥<span>129.00</span></td>
                <td>5.0折</td>
                <td><span>-</span><input type="text" value="1"><span>+</span></td>
                <td><a href="" class="deleteA">删除</a></td>
            </tr>
            <tr>
                <td>郝斌美国口语-美国口语成功训练系统(MP3)</td>
                <td>¥<span>480.00</span></td>
                <td>¥<span>292.90</span></td>
                <td>6.0折</td>
                <td><span>-</span><input type="text" value="2"><span>+</span></td>
                <td><a href="" class="deleteA">删除</a></td>
            </tr>
            <tr>
                <td>简 奥斯丁全集(DVD-9) (增BBC产品目录)</td>
                <td>¥<span>138.00</span></td>
                <td>¥<span>103.90</span></td>
                <td>8.0折</td>
                <td><span>-</span><input type="text" value="1"><span>+</span></td>
                <td><a href="" class="deleteA">删除</a></td>
            </tr>
        </tbody>
        <tfoot>
            <tr>
                <td>应付金额:</td>
                <td colspan="2"></td>

                <td>节省金额:</td>
                <td colspan="2"></td>

            </tr>
        </tfoot>
    </table>
    <script src="comm.js"></script>
    <script>
        // 获取得到表格元素节点
        var shopCar = document.getElementById("shopCar");

        // 获取得到tbody的所有行
        var tbodyEle = shopCar.tBodies[0];

        var tbodyRows = tbodyEle.rows;

        // 获取得到tfoot
        var tfootEle = shopCar.tFoot;

        var tfootRows = tfootEle.rows[0];

        var tfootCells = tfootRows.cells;
        // 计算应付金额
        // 计算节省金额
        // 应付金额 = 每件商品的优惠价 * 数量 的累加
        function getPay() {
            // 计算应付金额
            var paySum = 0;

            // 原本的金额
            var originalSum = 0;

            for (var i = 0; i < tbodyRows.length; i++) {
                // 获取得到tbody的每一行
                var row = tbodyRows[i];

                // 获取到当前行的所有单元格
                var rowCells = row.cells;

                // 获取得到原价
                var originalTd = rowCells[1];
                var originalSpan = originalTd.getElementsByTagName("span")[0];
                var originalPrice = parseFloat(originalSpan.innerText);

                // 获取得到优惠价
                var disTd = rowCells[2];
                var disSpan = disTd.getElementsByTagName("span")[0];
                var disPrice = parseFloat(disSpan.innerText);

                // 获取得到数量
                var countTd = rowCells[4];
                var countInput = countTd.getElementsByTagName("input")[0];
                var count = parseFloat(countInput.value);

                // 计算得到应付金额
                paySum += disPrice * count;

                // 计算得到原本金额
                originalSum += originalPrice * count;
            }

            tfootCells[1].innerText = "¥" + paySum.toFixed(2);
            tfootCells[3].innerText = "¥" + (originalSum - paySum).toFixed(2);
        }

        getPay();

        // 当修改输入框的值的时候,重新计算,对输入的值进行判断
        var inputs = shopCar.getElementsByTagName("input");

        for (var i = 0; i < inputs.length; i++) {
            var ip = inputs[i];

            // 添加获取焦点事件
            ip.onfocus = function () {
                var val = this.value;
                // 自定义属性
                this.setAttribute("val", val);
            }

            // 添加失去焦点事件
            ip.onblur = function () {
                // 判断值是否符合要求
                var v = Number(this.value);

                // 如果v是NaN,那么恢复之前的值
                if (isNaN(v)) {
                    this.value = this.getAttribute("val");
                } else {
                    getPay();
                }
            }

            // 给input之前的减号添加鼠标单击事件
            var previousSpan = getPreviousElementSibling(ip);
            previousSpan.onclick = function () {
                // 获取输入框的当前值
                var input = getNextElementSibling(this);
                if (input.value === "1") {
                    return;
                } else {
                    input.value--;

                }
                getPay();
            }

            // 给input之后的加号添加鼠标单击事件
            var nextSpan = getNextElementSibling(ip);
            nextSpan.onclick = function () {
                var input = getPreviousElementSibling(this);
                input.value++;
                getPay();
            }

        }


        var deleteAs = document.getElementsByClassName("deleteA");
        for (var i = 0; i < deleteAs.length; i++) {
            deleteAs[i].onclick = function () {
                var t = confirm("是否删除?")
                if (t) {
                    var deleteTr = this.parentNode.parentNode;
                    tbodyEle.removeChild(deleteTr);
                    getPay();
                }
                return false;
            }
        }
    </script>
</body>

</html>

5.10、模拟京东导航

1573390008060

<!doctype html>
<html>

<head>
    <title>页面标题</title>
    <meta charset="utf-8">
    <style>
        body,
        div,
        ul,
        li,
        img {
            padding: 0;
            margin: 0;
        }

        body {
            background: #F4F4F4;
        }

        .navDiv {
            width: 190px;
            height: 470px;
            background: white;
            border: 1px solid black;
            padding: 10px 0;
            box-sizing: border-box;
            font-size: 14px;
            user-select: none;
            position: relative;
        }

        .navDiv>ul {
            list-style: none;
            padding: 0;
        }

        .navDiv li {
            cursor: pointer;
            /* padding: 4px; */
            padding: 4px 10px;
        }

        .navDiv li:hover {
            background: #F4F4F4;
        }

        /* 让li的div子元素,根据navDiv来定位 */
        .navDiv div {
            position: absolute;
            top: 0;
            left: 190px;
            display: none;
        }
    </style>
</head>

<body>
    <div class="navDiv" id="navDiv">
        <!-- 把li对应的内容都放在li里面即可 -->
        <ul>
            <li>家用电器
                <div><img src="image/jd1.png" alt=""></div>
            </li>
            <li>手机/运营商/数码
                <div><img src="image/jd2.png" alt=""></div>
            </li>
            <li>电脑/办公
                <div><img src="image/jd3.png" alt=""></div>
            </li>
            <li>家居/家具/家装/厨具
                <div><img src="image/jd4.png" alt=""></div>
            </li>
            <li>男装/女装/童装/内衣
                <div><img src="image/jd5.png" alt=""></div>
            </li>
            <li>美妆/个护清洁
                <div><img src="image/jd6.png" alt=""></div>
            </li>
        </ul>

    </div>

    <script>
        var navDiv = document.getElementById("navDiv");

        var allLis = navDiv.getElementsByTagName("li");

        for (var i = 0; i < allLis.length; i++) {
            allLis[i].onmouseover = function () {
                this.firstElementChild.style.display = "block";
            }
            allLis[i].onmouseout = function () {
                this.firstElementChild.style.display = "none";
            }
        }
    </script>
</body>

</html>