JavaScript进阶篇(DOM BOM)

目录

一、Web APIs

1、Web APIs 和JS基础关联性

1.1 JS的组成

1.2 JS基础阶段和Web APIs阶段

2、API和Web API

2.1 API

2.2 Web API

二、DOM

1、DOM简介

1.1 定义

1.2 专有名词

2、获取元素

2.1根据ID获取 getElementByID(id)

2.2 根据标签名获取 getElementsByTagName()

2.3 HTML5新增获取方法

 2.4 获取特殊元素(body,html)

3、事件基础

3.1事件

3.2 执行事件的步骤

 4、操作元素

 4.1 改变元素内容

element.innerText

 4.2 修改元素常见属性

案例:分时问候

4.3 修改表单属性

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

 4.4 修改样式属性 

4.5 排他思想

4.6 自定义属性的操作

案例:tab栏切换

4.7 H5自定义属性

4.7.1 设置H5自定义属性

 4.7.2 获取H5自定义属性

5.节点操作

 5.1 节点概述

5.2 节点操作

5.2.1 父节点 parentNode

5.2.2 子节点

parentNode.childNodes(标准)

parentNode.children(非标准)

 获取第一个元素和最后一个元素

新浪下拉菜单案例

5.2.3 兄弟节点

5.2.4  创建和添加节点

 简单发布留言板案例

5.2.5 删除节点

删除留言案例

5.2.6  复制节点 

创建学生表格(动态)

5.2.7 三种动态创建元素区别

6.DOM重点总结

6.1 创建

6.2 增

 6.3 删

6.4 改

6.5 查

6.6 属性操作

6.7 事件操作

 7.事件高级

7.1 注册事件

7.1.1 注册事件两种方式

传统注册方式

 方法监听注册方式

7.1.2 addEventListener 事件监听方式

 7.1.3 attachEvent 事件监听方式

7.1.4 注册事件兼容性解决方案 

7.2 删除事件

7.2.1 删除事件的方式

7.3  DOM事件流

7.4 事件对象

 7.5 常见事件对象属性和方法

7.5.1 e.target  

7.5.2 返回事件类型及阻止默认行为

7.5.3 阻止事件冒泡

 7.5.4 事件委托

8.常用鼠标事件

8.2 鼠标事件对象(MouseEvent)

 8.2.1 跟随鼠标的小图案

9.2 键盘事件对象

案例:模拟京东输入内容

案例:模拟京东快递单号查询案例

10、BOM

10.1 BOM

10.2 window对象的常见事件

 10.2.1 窗口加载事件

10.2.2 调整窗口大小

 10.3 定时器

10.3.1 setTimeout() 定时器

案例:5秒自动关闭广告

10.3.2 停止setTimeout()定时器

10.3.3 setInterval()定时器

案例:京东倒计时

10.3.4 清除setInterval定时器

案例:发送短信

10.3.5 this指向问题

 11.JS执行队列

 11.1 JS是单线程

11.2 同步与异步

11.3 JS执行机制

 12.location对象

12.1 什么是location对象

12.2 location常用对象属性

 案例:5秒后跳转页面

案例:获取URL参数

12.3 location对象方法

13.navigation对象

14.history对象

15.PC端网页特效

15.1 元素偏移量offset系列

15.1.1 offset和style区别

案例:获取鼠标移动坐标

案例:模态框拖拽

15.2 元素可视区client系列

15.2.1 淘宝flexble.js源码分析

15.3 元素滚动sroll系列

 案例:淘宝固定侧边栏

返回顶部 

15.4 三大系列总结

15.5 mouseenter和mouseover的区别

15.6 动画封装效果

15.6.1 动画实现原理

15.6.2 简单动画函数封装

15.6.3 缓动动画+多个目标值间移动

15.6.4 缓动动画添加回调函数

15.6.5 动画函数封装及使用

带有动画的返回顶部

15.6.6 节流阀

15.6.7 常见网页特效案例

筋斗云

16、移动端网页特效

16.1 触屏事件

16.1.1 常见的触屏事件

16.1.2 触摸事件对象(TouchEvent)

16.1.3 移动端拖动元素

16.2移动端常见开发插件

16.2.1 click延时解决方案

16.2.2 插件(fastclick)

16.2.3 Swiper插件

16.2.4 其他移动端常见插件

16.2.5 视频插件zy.media.js

16.3 移动端常用开发框架

16.3.1 Bootstrap

16.3.2 本地存储导读

window.sessionStorage

window.localStorage

案例:记住用户名


一、Web APIs

1、Web APIs 和JS基础关联性

1.1 JS的组成

JavaScript由ECMAScript(js语法,基础部分)、DOM(页面文档对象模型)和BOM(浏览器对象模型)组成。 

1.2 JS基础阶段和Web APIs阶段

js基础:掌握基础语法,是做不了常用的网页交互效果,目的是为了是后面js学习打基础

Web APIs阶段:它是w3c组织的标准;是js所独有的部分;主要学习页面交互功能

2、API和Web API

2.1 API

API(应用程序编程接口)是给程序员提供的一种工具,以便能轻松的实现想要完成的功能。

总而言之,API是为我们程序员提供的一个接口,帮助我们实现某种功能,我们会使用即可,不要纠结内部如何实现。

2.2 Web API

浏览器提供的一套操作浏览器功能页面元素的API(BOM和DOM);因为Web API很多,所以这个阶段我们称为Web APIs。

主要针对浏览器提供的接口,主要用于浏览器做交互效果,一般有输入和输出(函数的传参和返回值),Web API很多都是方法(函数)

二、DOM

1、DOM简介

1.1 定义

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

1.2 专有名词

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

2、获取元素

2.1根据ID获取 getElementByID(id)

语法:var 变量 = document.getElementByID(id);

<body>
        <p id = 'test'>hello world!</p>
        <script>  //因为我们文档是自上而下的 所以要先有标签,script写在标签里面
        var test1 = document.getElementById('test'); //对 “Id” 的拼写一定要正确
        console.log(test1); //返回的是元素对象
        console.dir(test1); //console.dir() 打印我们返回的元素对象更好的查看里面的属性和方法
        </script>
</body>

2.2 根据标签名获取 getElementsByTagName()

返回的是带有指定标签名的对象的集合 以伪数组的形式存储

<ul>
        <li>hello world</li>
        <li>hello world</li>
        <li>hello world</li>
        <li>hello world</li>
    </ul>
    <script>
        //注意:要加引号
        var lis = document.getElementsByTagName('li');
        console.log(lis);  //HTMLCollection(4) [li, li, li, li]
        //可以通过遍历依次打印里面的元素
        for (var i = 0;i <= lis.length;i++){
            console.log(lis[i]);
        }
        /* 
        注:如果页面只有一个li 返回的还是伪数组的形式
        如果没有这个元素 返回的是空的伪数组的形式
        */
    </script>

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

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

 <ul>
        <li>hello world</li>
        <li>hello world</li>
        <li>hello world</li>
        <li>hello world</li>
    </ul>
    <ol id="ol">
        <li>同时有两个标签里有li</li>
        <li>同时有两个标签里有li</li>
        <li>同时有两个标签里有li</li>
    </ol>
    <script>
       //获取ol里面的li 方法一element.getElementsByTagName('标签名')
       var ol = document.getElementsByTagName('ol');
       console.log(ol[0].getElementsByTagName('li')); //必须是单个对象
       //方法二 ID 先获取ol这个元素
       var ol = document.getElementById('ol');
       console.log(ol.getElementsByTagName('li'));
    </script>

2.3 HTML5新增获取方法

  1. 通过类名返回元素对象集合 getElementsByClassName('类名')
  2. 根据指定选择器返回第一个元素对象 querySelectot('选择器')
  3. 根据指定选择器返回所有元素对象集合querySelectorAll('选择器')
<div class="box">盒子1</div>
   <div class="box">盒子2</div>
   <div id="nav">
       <ul>
           <li>首页</li>
           <li>产品</li>
       </ul>
   </div>
   <script>
       var boxs = document.getElementsByClassName('box');
       console.log(boxs); //通过类名  HTMLCollection(2) [div.box, div.box]
//不同的选择器括号里符号不同 返回的是第一个元素对象
       var firstBox = document.querySelector('.box');
       console.log(firstBox);
       var nav = document.querySelector('#nav');
       console.log(nav);
       //返回的是所有元素对象集合
       var allBox = document.querySelectorAll('.box');
       console.log(allBox); //NodeList(2) [div.box, div.box]
   </script>

 2.4 获取特殊元素(body,html)

 document.body    //获取body元素

document.documentElement   //获取html元素

3、事件基础

3.1事件

触发---响应机制

三要素:例:点击一个按钮 弹出对话框

  1. 事件源 事件被触发的对象 谁 (按钮)
  2. 事件类型 如何触发 什么事件 比如鼠标点击(onclick) 还是鼠标经过 还是键盘按下
  3. 事件处理程序 通过一个函数赋值的方式 完成
<button id="btn">javaScript</button>
 <script>
     var btn = document.getElementById('btn');
     btn.onclick = function(){
         alert('脚本语言');
     }
 </script>

3.2 执行事件的步骤

  1. 获取事件源
  2. 注册事件(绑定事件)
  3. 添加事件处理程序(采取函数赋值形式)
<div>点我</div>
    <script>
        var div = document.querySelector('div');//获取
        // div.onclick 绑定
        div.onclick = function(){
            console.log('我被选中了'); //添加事件处理程序
        }
    </script>

 4、操作元素

 4.1 改变元素内容

  • element.innerText

<button>显示当前时间</button>
    <div>时间</div>
    <script>
        //获取元素
        var btn = document.querySelector('button');
        var div = document.querySelector('div');
        //注册事件
        btn.onclick = function(){
            div.innerText = getDate();
        }

       function getDate(){
       var date = new Date();
       var year = date.getFullYear(); 
       var month = date.getMonth() + 1;
       var dates = date.getDay(); 
       var arr = ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'];
       var day = date.getDay();   
       return '今天是 : '+ year + '年' +month + '月' + dates +'日'+ arr[day];
       }
    </script>
  • element.innerHTML(常用)
<div></div>
    <p>
        我是文字
        <span>123</span>
    </p>
    <script>
        //innerHTML可以识别html标签 W3C
        var div = document.querySelector('div');
        div.innerHTML = '今天是<strong>周末</strong>'

        var p =document.querySelector('p');
        console.log(p.innerText);//去掉空格和换行
        console.log(p.innerHTML);//保留空格和换行标签也显示
    </script>

 4.2 修改元素常见属性

src、href、title、alt等

<botton id="ldh">刘德华</botton>
    <botton id="zxy">张学友</botton>
    <img src="images/ldh.jpg" alt="" title="刘德华">
    <script>
        var ldh = document.getElementById('ldh');
        var zxy = document.getElementById('zxy');
        var img = document.querySelector('img');
        zxy.onclick = function(){
            img.src = 'images/zxy.jpg';
            img.title = '张学友';
        }
        ldh.onclick = function(){
            img.src = 'images/ldh.jpg';
            img.title = '刘德华';
        }
    </script>

案例:分时问候

<body>
    <div>上午好</div> //同理,也可以添加src标签
<script>
    var div = document.querySelector('div');

    var date = new Date();
    var h = date.getHours();
    if(h < 12){
        div.innerHTML = '上午好呀!';
    }else if(h < 18){
        div.innerHTML = '下午好呀!';
    }else{
        div.innerHTML = '晚上好呀!'
    }
</script>
</body>

4.3 修改表单属性

type、value、checked、selected、disabled

<body>
    <button>按钮</button>
    <input type="text" value="输入内容">
    <script>
        //获取元素
        var btn = document.querySelector('button');
        var input = document.querySelector('input');
        //注册事件 处理程序
        btn.onclick = function(){
            //表单里的值 文字内容是通过value来修改的
            input.value = '被点击了';
     //我们想让按钮被禁用 btn.disabled = true;
            this.disabled = true; //this指向的是事件函数的调用者 btn
        }
    </script>
</body>

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

  <style>
        input{
            color: #999;
        }
    </style> 
</head>
<body>
   <input type="text" value="手机">
   <script>
       //获取元素
       var text = document.querySelector('input');
       //注册事件 获得焦点 onfocus
       text.onfocus = function(){  
           if (this.value === '手机'){
               this.value = '';
           }
//获得焦点需要把文本框颜色变深
            this.style.color = '#333';
       }
       //注册事件 失去焦点事件 onblur
       text.onblur = function(){
           if (this.value === ''){
               this.value = '手机';
           }
//失去焦点后变浅
           this.style.color = '#999';
       }
   </script>
</body>

 4.4 修改样式属性 

element.style 行内样式操作

element.className 类名样式操作(会覆盖原先的类名)

<style>
       div{
           width: 100px;
           height: 100px;
           background-color: skyblue;
       }
       .change{
           background-color: pink;
           color: #fff;
           font-size: 25px;
           margin-top: 100px;
        }
    </style> 
</head>
<body>
  <div>文本</div>
   <script>
     var test = document.querySelector('div');
     test.onclick = function(){
         //使用element.style获得修改元素样式(样式少 功能简单的情况)
         //this.style.color = 'fff';
         //通过className更改元素样式 适合样式多或者复杂的情况
         this.className = 'change';
         //如果原来元素也有类名不想被覆盖 可以用多类名选择器
         //this.className = '原来类名 change'
     }
   </script>
</body>

4.5 排他思想

先排除其他人 在设置自己样式

<body>
 <button>按钮1</button>
 <button>按钮2</button>
 <button>按钮3</button>
 <button>按钮4</button>
 <script>
     //获取所有元素
     var btns = document.getElementsByTagName('button');
     //btns得到的是伪元素,btns[i]是里面的每一个元素
     for (var i = 0;i < btns.length;i++){
         btns[i].onclick = function(){
             //先把所有的按钮背景颜色去掉
             for(var i = 0;i < btns.length;i++){
             btns[i].style.backgroundColor = '';
             }
             //再把当前的元素背景颜色改成pink
             this.style.backgroundColor = 'pink';
         }
     }
 </script>
</body>

案例:全选框

<style>
        *{
            margin: 0;
            padding: 0;
        }
       .wrapper{
           width: 300px;
           margin: 100px auto;
       }
       table{
           border-collapse: collapse;
           border-spacing: 0;
           border: 1px solid #c0c0c0;
           width: 300px;
       }
       th,
        td {
            border: 1px solid #d0d0d0;
            color: #404060;
            padding: 10px;
        }
       th{
           background-color: #09c;
           font: bold 16px '微软雅黑';
           color: #fff;
       }
       td{
           font-size: 14px;
       }
       tbody tr{
           background-color: #f0f0f0;
       }
       tbody tr:hover{
           cursor: pointer;
           background-color: #fafafa;
       }
    </style>
</head>
<body>
    <div class="wrapper">
<table>
    <thead>
        <tr>
            <th>
                <input type="checkbox" id="j_cbAll">
            </th>
            <th>商品</th>
            <th>价钱</th>
        </tr>
    </thead>
    <tbody id="j_tb">
        <tr>
            <td>
                <input type="checkbox">
            </td>
            <td>ipone8</td>
            <td>8000</td>
        </tr>
        <tr>
            <td>
                <input type="checkbox">
            </td>
            <td>ipad Pro</td>
            <td>5000</td>
        </tr>
        <tr>
            <td>
                <input type="checkbox">
            </td>
            <td>ipad air</td>
            <td>2000</td>
        </tr>
        <tr>
            <td>
                <input type="checkbox">
            </td>
            <td>apple watch</td>
            <td>2000</td>
        </tr>
    </tbody>
</table>
</div>
</body>
<script>
  var j_cbAll = document.getElementById('j_cbAll');
  var j_tbs = document.getElementById('j_tb').getElementsByTagName('input');
  j_cbAll.onclick = function(){
      for (var i = 0;i < j_tbs.length;i++){
          j_tbs[i].checked = this.checked;
      }
  
  for (var i = 0;i < j_tbs.length;i++){
      j_tbs[i].onclick = function(){
          var flag = true;
          for (var i = 0;i < j_tbs.length;i++){
              if(!j_tbs[i].checked){
                  flag =false;
                  break;
              }
          }
      }
      j_cbAll.checked = flag;
  }
}
</script>

4.6 自定义属性的操作

  • 获取属性值

element.属性   获取内置属性值(元素本身自带的属性)

element.getAttribute('属性')   主要获得自定义的属性 

<body>
    <div id="demo" index="1"></div>
    <script>
        var div = document.querySelector('div');
        //获取元素的属性值 element.属性
        console.log(div.id);
        //element.getAttribute('属性')
        console.log(div.getAttribute('id'));
        console.log(div.getAttribute('index')); //自定义属性 1
    </script>
</body>
  • 设置属性值

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

  • 移除属性

element.removeAttribute('属性');

 //设置div和index的值
       var div = document.querySelector('div');
       div.id ='test';
       div.setAttribute('index','2');
       //移除index
       div.removeAttribute('index');

案例:tab栏切换

<style>
        *{
            margin: 0;
            padding: 0;
        }
        .tab{
            width: 978px;
            margin: 100px auto;
        }
        li{
            list-style-type: none;
        }
        .tablist{
            height: 39px;
            border: 1px solid #ccc;
            background-color: #f1f1f1;
        }
        .tablist li{
            float: left;
            height: 39px;
            line-height: 39px;
            padding: 0 20px;
            text-align: center;
            cursor: pointer;
        }
        .tablist .current{
            background-color: #c81623;
            color: #fff;
        }

        .item{
            display: none;
        }
    </style>
</head>
<body>
   <div class="tab">
       <div class="tablist">
           <ul>
               <li class="current">商品介绍</li>
               <li>规格与包装</li>
               <li>售后保障</li>
               <li>商品评价(50000)</li>
               <li>社区</li>
           </ul>
       </div>
       <div class="tabcon">
           <div class="item" style="display:block;">
               商品介绍模块内容
           </div>
           <div class="item">
            规格与包装模块内容
        </div>
        <div class="item">
            售后保障模块内容
        </div>
        <div class="item">
            商品评价50000模块内容
        </div>
        <div class="item">
            手机社区模块内容
        </div>
       </div>
   </div>
   <script>
       //1、上面的部分,点击某一个,当前颜色会红色,其余不变
       var tablist = document.querySelector('.tablist');
       var lis = tablist.querySelectorAll('li');
       var items = document.querySelectorAll('.item');
       //for循环绑定点击事件
       for (var i = 0;i < lis.length;i++){
           //开始给5个小li设置索引号
           lis[i].setAttribute('index',i);
           lis[i].onclick = function(){
               //干掉所有人 其余的li清除class这个类
               for (var i = 0;i < lis.length;i++){
                   lis[i].className = '';
               }
               this.className = 'current'; //留下自己
               //下面的显示内容模块
               var index = this.getAttribute('index');
               console.log(index);
               
               for (var i = 0;i < items.length;i++){
                   items[i].style.display = 'none';
               }
               items[index].style.display = 'block';
           }
       }
   </script>
</body>

4.7 H5自定义属性

为了保存并使用数据,有些数据可以保存到页面中而不用保存到数据库中

4.7.1 设置H5自定义属性

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

<body>
        <div data-index="1"></div>
        <!-- 或者使用js设置 -->
        <script>
            var div = document.querySelector('div');
            div.setAttribute('date-time',2);
        </script>
    </body>

 4.7.2 获取H5自定义属性


兼容性获取 element.getAttribute('data-index')

H5新增element.dataset.index或者element.datase['index'] ie 11开始使用 只能获取data-开头的

         <div data-index="1" data-list-name="keke"></div>
        <!-- 或者使用js设置 -->
        <script>
            var div = document.querySelector('div');
            div.setAttribute('date-time',2);
            
            console.log(div.getAttribute('data-index'));
            console.log(div.dataset.index);
            console.log(div.dataset['index']);
            //如果获取的单词比较长 采用驼峰命名法
            console.log(div.dataset.listName);
        </script>

5.节点操作

节点操作获取元素节点,较为简单,逻辑性强,利用父子节点关系获取元素

 5.1 节点概述

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

5.2 节点操作

5.2.1 父节点 parentNode

         <div class="demo">
            <div class="box">
                <span class="erweima">×</span>
            </div>
        </div>
    
        <script>
            // 1. 父节点 parentNode
            var erweima = document.querySelector('.erweima');
            // var box = document.querySelector('.box');
            // 得到的是离元素最近的父级节点(亲爸爸) 如果找不到父节点就返回为 null
            console.log(erweima.parentNode);
        </script>

5.2.2 子节点

parentNode.childNodes(标准)

返回的是所有的子节点 包含元素节点 文本节点等

parentNode.children(非标准)

<body>
       <ul>
           <li>得到li</li>
           <li>得到li</li>
           <li>得到li</li>
           <li>得到li</li>
           <li>得到li</li>
       </ul>
       <ol>
           <li>ol里面的li</li>
           <li>ol里面的li</li>
           <li>ol里面的li</li>
           <li>ol里面的li</li>
       </ol>
       <script>
           //这种方法较麻烦
           var ul = document.querySelector('ul');
           for (var i = 0;i < ul.childNodes.length;i++){
               if(ul.childNodes[i].nodeType == 1){
                   //ul.childNodes[i]是元素节点
                console.log(ul.childNodes[i]);
               }
           }
           //children获取所有的子元素节点 也是实际开发常用的
           console.log(ul.children);
       </script>
    </body>

 获取第一个元素和最后一个元素

<body>
       <ol>
           <li>ol里面的li 1</li>
           <li>ol里面的li 2</li>
           <li>ol里面的li 3</li>
           <li>ol里面的li 4</li>
       </ol>
       <script>
           var ol = document.querySelector('ol');
           //有文本节点和元素节点
           console.log(ol.firstChild);
           console.log(ol.lastChild);
           //有兼容性 
           console.log(ol.firstElementChild);
           console.log(ol.lastElementChild);
           //实际开发的写法 既没有兼容性问题又返回第一个子元素
           console.log(ol.children[0]);
           console.log(ol.children[ol.children.length - 1]);
       </script>
    </body>

新浪下拉菜单案例

<style>
        *{
            margin: 0;
            padding: 0;
        }
        li{
            list-style-type: none;
        }
        a{
            text-decoration: none;

        }
        .nav{
            margin: 100px;
        }
        .nav>li{
            position: relative;
            float: left;
            width:80px;
            height:41px;
            text-align: center;
        }
        .nav li a{
            display: block;
            width: 100%;
            height: 100%;
            line-height: 41px;
            color:#333;
        }
        .nav>li>a:hover{
            background-color: #eee;
        }
        .nav ul{
            display: none;
            position: absolute;
            top:41px;
            left: 0;
            width: 100%;
            border-left: 1px solid #fecc5b;
            border-right: 1px solid #fecc5b;
        }
        .nav ul li{
            border-bottom: 1px solid #fecc5b;
        }
        .nav ul li a:hover{
            background-color: #fff5da;
        }
    </style>
    <body>
       <ul class="nav">
           <li>
               <a href="#">微博</a>
               <ul>
                   <li>
                       <a href="">私信</a>
                   </li>
                   <li>
                       <a href="">评论</a>
                   </li>
                   <li>
                       <a href="">@我</a>
                   </li>
               </ul>
           </li>
           <li>
            <a href="#">博客</a>
            <ul>
                <li>
                    <a href="">博客评论</a>
                </li>
                <li>
                    <a href="">未读提醒</a>
                </li>
            </ul>
        </li>
        <li>
            <a href="#">邮箱</a>
            <ul>
                <li>
                    <a href="">免费邮箱</a>
                </li>
                <li>
                    <a href="">VIP邮箱</a>
                </li>
                <li>
                    <a href="">企业邮箱</a>
                </li>
            </ul>
        </li>
       </ul>
       <script>
           var nav = document.querySelector('.nav');
           var lis = nav.children; //得到4个li

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

5.2.3 兄弟节点

node.nextSibling 下一个兄弟节点 包含元素节点或者文本节点等

node.nextElementSibling 得到下一个兄弟元素节点,找不到则返回null

node.previousElementSibling 返回当前元素上一个兄弟节点 找不到就返回null

以上要考虑兼容性问题

<div>我是div</div>
    <div>测试兄弟节点</div>
    <script>
        var div = document.querySelector('div');
        console.log(div.nextSibling); //#text
        console.log(div.nextElementSibling); //<div>测试兄弟节点</div>
        console.log(div.previousElementSibling); //null
    </script>

5.2.4  创建和添加节点

想让页面添加一个新的元素 必须先创建元素在添加元素
创建节点 document.createElement('tagName')

添加节点 node.appendChild(child) 将一个节点添加到指定父节点的子节点列表末尾

node.insertBefore(child,指定元素) 将一个节点添加到指定父节点的指定子节点前面

<div>
        <ul>123</ul>
    </div>
    <script>
        var li = document.createElement('li');
        //添加节点中 node是父级 child是子级(要先获取父级
        var ul = document.querySelector('ul');
        ul.appendChild(li);
        //添加到指定子节点的前面
        var lili = createElement('li');
        ul.insertBefore(lili,ul.children[0]);
    </script>

 简单发布留言板案例

<style>
        li{
            width: 80px;
            background-color: blue;
        }
    </style>
<body>
    <textarea name="" id="" cols="30" rows="10">123</textarea>
    <button>发布</button>
    <ul>  
    </ul>
    <script>
        var btn = document.querySelector('button');
        var text = document.querySelector('textarea');
        var ul = document.querySelector('ul');
        btn.onclick = function(){
            if(text.value=''){
                alert('你还没有输入内容');
                return false;
            }else{
            //创建元素
            var li = document.createElement('li');
            li.innerHTML = text.value;
            //添加元素
            ul.insertBefore(li,ul.children[0]);

            }
        }
    </script>
</body>

5.2.5 删除节点

node.removeChild(child)从DOM中删除一个子节点 返回删除的节点

 <button>删除</button>
    <ul>
        <li>熊大</li>
        <li>熊二</li>
        <li>光头强</li>
    </ul>
    <script>
        var btn = document.querySelector('button');
        var ul = document.querySelector('ul');
        btn.onclick = function(){
            if(ul.children.length == 0){
                this.disabled = true; //禁用这个按钮
            }else{
                ul.removeChild(ul.children[0]);
            }
        }
    </script>

删除留言案例
 

<style>
        li{
            width:200px;
            background-color: pink;
            margin: 10px 0;
        }
        li a{
            float: right;
        }
    </style>
    </head>
<body>
     <!-- 
分析:当我们把文本域里面的值赋值给li的时候,多添加一个删除的链接
需要把所有的链接获取过来 当我们点击当前链接时候,删除当前的链接所在的li
阻止链接跳转需要添加JavaScript:void(0);或者JavaScript:;
    
    -->
     <textarea name="" id="" cols="30" rows="10"></textarea>
     <button>发布</button>
     <ul></ul>
     <script>
         //获取元素
         var btn = document.querySelector('button');
         var text = document.querySelector('textarea');
         var ul = document.querySelector('ul');
         //注册事件
         btn.onclick = function(){
             if (text.value == ''){
                 alert('您没有输入内容');
                 return false;
             }else{
                 var li = document.createElement('li');
                 li.innerHTML = text.value + "<a href='javascript:;'>删除</a>";
                 ul.insertBefore(li,ul.children[0]);
                 //删除元素
                 var as = document.querySelectorAll('a');
                 for (var i = 0;i < as.length;i++){
                     as[i].onclick = function(){
                         ul.removeChild(this.parentNode);
                     }
                 }
             }

         }
     </script>
</body>

5.2.6  复制节点 

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

注:如果括号里为空或者是false 则是浅拷贝 即只克隆复制节点本身 不克隆里面的子节点

相反 如果括号里为true 则是深拷贝 克隆复制节点本身 也克隆里面的子节点

<body>
     <ul>
         <li>1</li>
         <li>2</li>
         <li>3</li>
     </ul>
     <script>
         var ul = document.querySelector('ul');
         var lili = ul.children[0].cloneNode(true);
         //克隆后要记得添加节点
         ul.appendChild(lili);
     </script>
</body>

创建学生表格(动态)

<style>
      *{
          margin: 0;
          padding: 0;
      } 
      table{
          width: 500px;
          margin: 100px auto;
          border-collapse: collapse;
          text-align: center;
      }
      td,
      th{
          border: 1px solid #333;
      }
      thead tr{
          height: 40px;
          background-color: #ccc;
      }
    </style>
    </head>
<body>
     <table>
         <thead>
             <tr>
                 <th>姓名</th>
                 <th>项目</th>
                 <th>成绩</th>
                 <th>操作</th>
             </tr>
         </thead>
         <tbody>
             
         </tbody>
     </table>
     <script>
         //用对象准备学生的数据
         var datas = [{
             name:'魏无限',
             subject:'javascript',
             score:100
         },
         {
             name:'红利',
             subject:'javascript',
             score:100
         },{
             name:'傅恒',
             subject:'java',
             score:89
         },
        ]
        //创建行 有几个人创建几行
        var tbody = document.querySelector('tbody');
        for (var i = 0;i < datas.length;i++){
            var tr = document.createElement('tr');
            tbody.appendChild(tr);
            //行里面创建单元格 数量取决于对象里的属性个数
            for (var k in datas[i]){
                var td = document.createElement('td');
                // 把对象里面的属性值data[i][k]给td
                td.innerHTML = datas[i][k];
                tr.appendChild(td);
            }
            //创建有删除两个字的行
            var td = document.createElement('td');
            td.innerHTML = '<a href= "javascript:;">删除</a>';
            tr.appendChild(td);
        }
        //删除操作
        var as = document.querySelectorAll('a');
        for(var i = 0;i < as.length;i++){
            as[i].onclick = function(){
                //点击a 删除所在的行(链接的爸爸的爸爸)所有内容
                tbody.removeChild(this.parentNode.parentNode);
            }
        }
     </script>
</body>

5.2.7 三种动态创建元素区别

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

6.DOM重点总结

DOM 文档对象模型 是W3C组织推荐的处理可扩展标记语言的标准编程接口

通过这些DOM接口可以改变网页的内容、结构和样式

  1. 对于JavaScript,为了能够使JavaScript操作HTML,JavaScript就有了一套自己的dom编程接口
  2. 对于HTML,dom使得html形成一颗dom树,包含文档、元素、节点
  3. 我们通过DOM元素返回的是一个对象(object),所以称为文档对象模型

主要针对于元素的操作,主要有创建、增、删、改、查、属性操作、事件操作。

6.1 创建

  • document.write
  • innerHTML
  • createElement 

6.2 增

  • appendChild
  • insertBefore

 6.3 删

removeChild

6.4 改

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

修改元素属性:src、href、title等

修改普通元素内容:innerHTML、innerText

修改表单元素:value、type、disabled等

修改元素样式:style、className

6.5 查

DOM提供的API方法:getElementById、getElementsByTagName古老方法不太推荐

H5提供的新方法:querySelector、querySelectorAll提倡

利用节点操作获取元素:父(parentNode)子(children)兄(previousElementSibling、nextELementSibling)提倡

6.6 属性操作

主要针对自定义属性

  • setAttribute:设置dom的属性值
  • getAttribute:得到dom的属性值
  • removeAttribute移除属性

6.7 事件操作

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

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

 7.事件高级

7.1 注册事件

给元素添加事件,称为注册事件或者绑定事件

7.1.1 注册事件两种方式

注册事件有两种方式:传统方式和方法监听注册方式

传统注册方式

利用on开头的事件onclick

特点:注册事件的唯一性 同一个元素同一个事件只能设置一个处理函数,最后注册的处理函数将会覆盖前面注册的处理函数.

 方法监听注册方式

  • W3C标准 推荐方式 (IE9之前的IE不支持此方法 )
  • addEventListener()它是一个方法
  • 特点:同一个元素同一个事件可以注册多个监听器
  • 按注册顺序依次执行

7.1.2 addEventListener 事件监听方式

eventTarget.addEventListener(type,listener[,useCapture])

  • type:事件类型字符串,比如click、mouseover(注意这里不要带on)
  •  listener:事件处理函数,事件发生时,会调用该监听函数
  • useCapture:可选参数 是一个布尔值 默认为false 

 7.1.3 attachEvent 事件监听方式

eventTarget.attachEvent(eventNameWithOn,callback)

  • eventNameWithOn:事件类型字符串,比如onclick、onmouseover,这里要带on
  • callback :事件处理函数,当目标触发事件时回调函数被调用
<body>
        <button>传统注册方式</button>
        <button>方法监听注册方式</button>
        <button>IE9之前的版本</button>
        <script>
            var btns = document.querySelectorAll('button');
            //传统注册方式
            btns[0].onclick = function(){
                alert('hello');
            }
            btn[0].onclick = function(){
                alert('say hi');
            }
            //事件监听注册事件 addEventListener 
            // (1) 里面的事件类型是字符串 必定加引号 而且不带on
            // (2) 同一个元素 同一个事件可以添加多个侦听器(事件处理程序)
            btns[1].addEventListener('click',function(){
                alert(22);
            })
            btns[1].addEventListener('click',function(){
                alert(32);
            })
            //attachEvent ie9之前的版本支持
            btns[2].attchEvent('onclick',function(){
                alert(9);
            })
        </script>
    </body>

7.1.4 注册事件兼容性解决方案 

如我们之前一样 利用一个函数把它们封装起来 先照顾大多数浏览器 在处理特殊浏览器 在工作中 上面几种几乎就能解决需求

7.2 删除事件

7.2.1 删除事件的方式

传统注册方式

eventTarget.onclick = null;

方法监听注册方式

eventTarget.removeEventListener(type,listener[,useCapture]);

eventTarget.detachEvent(eventNameWithOn,callback);

<style>
        div{
            width: 200px;
            height: 100px;
            background-color: pink;
            margin: 10px 0;
        }
    </style>
    </head>
    <body>
        <div>1</div> 
        <div>2</div> 
        <div>3</div> 
        <script>
            var divs = document.querySelectorAll('div');
            divs[0].onclick = function(){
                alert(11);
                //传统方式删除事件
                divs[0].onclick = null;
            }
            //removeEventListener 删除事件
            divs[1].addEventListener('click',fn) //里面的fn不需要加小括号
            function fn(){
                alert(22);
            divs[1].removeEventListener('click',fn);
            }
            //detachEvent(eventNameWithOn,callback)删除事件
            divs[2].attachEvent('onclick',fn1);
            function fn1(){
                alert(33);
                div[2].detachEvent('onclick',fn1);
            }
        </script>
    </body>

7.3  DOM事件流

事件流描述的是从页面中接收事件的顺序。

事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即DOM事件流

DOM事件流分为3个阶段:

  • 捕获阶段:事件开始时由最具体的元素接收,然后逐级向上传播到DOM最顶层节点的过程
  • 当前目标阶段
  • 冒泡阶段:从DOM最顶层节点开始,然后逐级向下传播到最具体的元素接收的过程。 

注: 

  1.  js代码中只能执行捕获或者冒泡其中的一个阶段。
  2. onclick和attachEvent只能得到冒泡阶段
  3. addEventListener(type,listener[,useCapture])第三个参数如果是true,表示在事件捕获阶段调用事件处理程序;如果是false(不写默认是false),表示在事件冒泡阶段调用事件处理程序。
  4. 实际开发中,很少使用事件捕获,更关注事件冒泡
  5. 有些事件是没有冒泡的,比如onblur、onfocus、onmouseover、onmouseleave
  6. 事件冒泡有时候会带来麻烦,有时候又会帮助很巧妙的做某些事件。

7.4 事件对象

 eventTarget.onclick = function(event){}

eventTarget.addEventListener('click',function(event)){}

//event 就是一个事件对象 

<div>1</div> 
        <script>
           var div = document.querySelector('div');
           div.onclick = function(event){
               console.log(event);
           }
           div.addEventListener('click',function(event)){
               console.log(event);
           }
           /* 
           1、event 就是一个事件对象 写到我们侦听函数的小括号里面 当形参来看
           2、事件对象只有有了事件才会存在,它是系统给我们自动创建的,不需要我们传递参数
           3、事件对象是我们事件的一系列相关数据的集合 跟事件内容相关的 比如鼠标点击里面
就包含了鼠标的相关信息 鼠标坐标……如果是键盘事件就包含的键盘事件的信息 比如 判断用户按了哪个键
           4、这个事件对象我们可以自己命名 比如event、evt、e
           5、事件对象也有兼容性问题 ie678 通过window.event 兼容性的写法 e = e || window.event
           */
        </script>

 7.5 常见事件对象属性和方法

7.5.1 e.target  

e.target 返回的是触发事件的对象(元素)

还有一个e.srcElement(返回触发事件的对象) 要考虑兼容性 这里不举例了

this返回的是绑定事件的对象(元素)

<div>123</div>
        <ul>
            <li>abc</li>
            <li>abc</li>
            <li>abc</li>
        </ul>
        <script>
            var div = document.querySelector('div');
            div.addEventListener('click',function(e){
                console.log(e.target); //<div>123</div>
                console.log(this);  //<div>123</div>
            })
            var ul = document.querySelector('ul');
            ul.addEventListener('click',function(e){
                //this指向ul  因为我们给ul绑定了事件
                console.log(this);
                //e.target 指向我们点击的那个对象 谁触发了这个事件 我们点击的是li
                console.log(e.target);
            })
        </script>

7.5.2 返回事件类型及阻止默认行为

e.type

<div>123</div>
        <a href="#">链接</a>
        <script>
            //1、返回事件类型
            var div = document.querySelector('div');
            div.addEventListener('click',fn);
            div.addEventListener('mouseover',fn);
            function fn(e){
                console.log(e.type);
            }
            //2、阻止默认行为 让链接不跳转 或者让按钮不提交
            var a = document.querySelector('a');
            a.addEventListener('click',function(e){
                e.preventDefault();  //dom标准写法
            })

            //传统的注册方式
            a.onclick = function(e){
                //普通浏览器 
                e.preventDefault();
                //低版本浏览器 returnValue 属性
                e.returValue;
                //也可以利用return false 没有兼容性问题 但return后面的代码不执行了 而且只限于传统模式
                return false;
                alert(11); //不执行
            }
        </script>

7.5.3 阻止事件冒泡

e.stopPropagation()     //利用事件对象里面的stopPropagation()方法

e.cancelBubble = true;  //利用事件对象cancelBubble属性

<style>
       .father{
           overflow: hidden;
           width: 300px;
           height: 300px;
           margin: 100px auto;
           background-color: pink;
           text-align: center;
       }
       .son{
           width: 200px;
           height: 200px;
           background-color: skyblue;
           margin: 50px;
           line-height: 200px;
           color: black;
       }
    </style>
    </head>
    <body>
        <div class="father">
            <div class="son">son儿子</div>
        </div>
        <script>
            var son = document.querySelector('.son');
            son.addEventListener('click',function(e){
                alert('son');
                e.stopPropagation(); //stop 停止 propagation 传播
                e.cancelBubble = true; //非标准cancel 取消 bubble 泡泡
            },false);

            var father = document.querySelector('.father');
            father.addEventListener('click',function(){
                alert('father');
            },father);
            document.addEventListener('click',function(){
                alert('document');
            })
        </script>
    </body>

 7.5.4 事件委托

原理:不是给每个子节点单独设置事件监听器,而是事件监听器设置在其父节点上,然后利用冒泡原理影响设置每个子节点。

作用:只操作了一次DOM,提高了程序的性能

<ul>
            <li>事件委托ul</li>
            <li>事件委托ul</li>
            <li>事件委托ul</li>
            <li>事件委托ul</li>
        </ul>
        <script>
            var ul = document.querySelector('ul');
            ul.addEventListener('click',function(){
                alert('每个li委托给ul');
            })
        </script>

8.常用鼠标事件

8.1 禁止选中文字和禁用右键菜单

contextmenu //禁用右键菜单 用于程序员取消默认的上下文菜单

selectstart    //禁止鼠标选中

 <body>
        不可以被复制的一段话
        <script>
            document.addEventListener('contextmenu',function(e){
                e.preventDefault();
            })
            //selectstart  开始选中
            document.addEventListener('selectstart',function(e){
                e.preventDefault();
            })
        </script>
    </body>

8.2 鼠标事件对象(MouseEvent)

鼠标对象事件说明
e.clientX返回鼠标相对于浏览器窗口可视区的x坐标
e.clientY返回鼠标相对于浏览器窗口可视区的y坐标
e.pageX返回鼠标相对于文档页面的x坐标
e.pageY返回鼠标相对于文档页面的y坐标
e.screenX返回鼠标相对于电脑屏幕的x坐标
e.sreenY返回鼠标相对于电脑屏幕的y坐标

  

<script>
document.addEventListener('click',function(e){
    console.log(e.pagex);
</script>

 8.2.1 跟随鼠标的小图案

案例分析:

  1. 鼠标不断的移动,使用鼠标移动事件:mousemove
  2. 在页面中移动,给document注册事件
  3. 图片要移动距离,而且不占位置,我们使用绝对定位即可

核心:每次鼠标移动,都要获得最新的鼠标坐标,把这个x和y坐标作为图片的top和left值就可以移动图片。

<style>
      img{
          position: absolute;
          top:2px;
      }
    </style>
    </head>
    <body>
       <img src="umges/angel.gif" alt="">
       <script>
           var pic = document.querySelector('img');
           document.addEventListener('mousemove',function(e){
               var x = e.pageX;
               var y = e.pageY;
               console.log('x坐标是'+x,'y坐标是'+y);
               //不要忘记给left和top添加px单位
               pic.style.left = x - 50 +'px';
               pic.style.top = y - 40 + 'px';
           })
       </script>
    </body>

9.常用的键盘事件

9.1 常用键盘事件

键盘事件触发条件
onkeyup某个键盘按键被松开时触发
onkeydown某个键盘按键被按下时触发
onkeypress某个键盘按钮被按下时触发(但它不识别功能键 比如ctrl shift箭头等)
<script>
           //传统方式
          /*  document.onkeyup = function(){
               console.log('弹起了');
           } */
           document.addEventListener('keyup',function(){
               console.log('弹起了');
           })
           document.addEventListener('keydown',function(){
               console.log('按下了down');
           })
           document.addEventListener('keypress',function(){
               console.log('按下了press');
           })
           // 三个事件的执行顺序  keydown -- keypress -- keyup
       </script>

9.2 键盘事件对象

键盘事件对象中的keyCode属性可以得到相应键的ASCII码值

<script>
     // 1. 我们的keyup 和keydown事件不区分字母大小写  a 和 A 得到的都是65
    // 2. 我们的keypress 事件 区分字母大小写  a  97 和 A 得到的是65
           document.addEventListener('keyup',function(e){
               console.log('up:' + e.keyCode);
           })
           //利用keyCode返回的ASCII码可以判断用户按下了哪个键
           if (e.keyCode === 65){
               alert('你按下的是a键');
           }else{
               alert('你没有按a键');
           }
           document.addEventListener('keypress',function(e){
               console.log('press:' + e.keyCode);
           })
       </script>

案例:模拟京东输入内容

<input type="text">
       <script>
           /* 
           核心思路:判断用户是否按下了s键,如果按下,就把光标定位到搜索框里面
           使用键盘事件对象里面的keyCode判断用户按下的是否是s键
           搜索框获得焦点:focus()方法
           */
           var search = document.querySelector('input');
           document.addEventListener('keyup',function(e){
               console.log(e.keyCode);
           })
           if (e.keyCode === 83){
               search.focus();
           }
       </script>

案例:模拟京东快递单号查询案例

<style>
      *{
          margin: 0;
          padding: 0;
      }
      .search{
          position: relative;
          width: 178px;
          margin: 100px;

        }
        .con{
            position: absolute;
            display: none;
            top:-40px;
            width: 171px;
            border: 1px solid rgba(0,0,0,.2);
            box-shadow: 0 2px 4px rgba(0,0,0,.2);
            padding: 5px 0;
            font-size: 18px;
            line-height: 20px;
            color: #333;
        }
        .con::before{
            content: '';
            width: 0;
            height: 0;
            position: absolute;
            top:28px;
            border: 8px solid #000;
            border-style: solid dashed dashed;
            border-color: #fff transparent transparent;
        }
    </style>
    </head>
    <body>
        <!-- 
            案例分析:快递单号输入内容时,上面的大号字体盒子(con)显示
            表单检测用户输入:给表单添加键盘事件
            同时把快递单号里面的值(value)获取过来赋值给con盒子(innerText)做内容
            如果快递单号里面为空,则隐藏大号字体盒子(con)
         -->
       <div class="search">
           <div class="con">123</div>
           <input type="text" placeholder="请输入你的快递单号" class="jd">
       </div>
       <script>
           var con = document.querySelector('.con');
           var jd_input = document.querySelector('.jd');
           jd_input.addEventListener('keyup',function(){
               if(this.value == ''){
                   con.style.display = 'none';
               }else{
                   con.style.display = 'block';
                   con.innerText = this.value;
               }
           })
           //当我们失去焦点 就隐藏这个con盒子
           jd_input.addEventListener('blur',function(){
               con.style.display = 'none';
           })
           jd_input.addEventListener('focus',function(){
               if (this.value !== ''){
                   con.style.display = 'block';
               }
           })
       </script>
    </body>

10、BOM

10.1 BOM

BOM即浏览器对象模型,它提供了独立于内容而与浏览器窗口进行交互的对象,其核心对象是window

DOMBOM
文档对象模型浏览器对象模型
把文档当成一个对象来看把浏览器当成一个对象来看
顶级对象是document顶级对象是window
主要学习的是操作页面元素学习的是浏览器窗口交互的一些对象
W3C标准规范浏览器厂商在各自浏览器上定义的 兼容性差

10.2 window对象的常见事件

是浏览器的顶级对象,具有双重角色

  1. 是JS访问浏览器窗口的一个接口
  2. 是一个全局对象。定义在全局作用域的变量、函数都会变成window对象的属性和方法,在调用时可以省略window,如前面学习的alert()、prompt()等都属于window对象方法(注:window下的一个特殊属性window.name)

 10.2.1 窗口加载事件

window.onload = function(){}

window.addEventListener("load",function(){});

window.onload是窗口加载事件,当文档内容完全加载完成会触发该事件(包括图像、脚本文件、css文件等),就调用的处理函数。

注:

  1. 有了window.onload就可以把JS代码写到页面元素的上方,因为onload是等页面内容全部加载完毕,再去执行处理函数。
  2. window.onload传统注册事件方式只能写一次,如果有多个,会以最后一个window.onload为准。
  3. 如果使用addEventListener则没有限制 

document.addEventListener('DOMContentLoaded',function(){}) 

//DOM加载完毕后,不包含图片,flash css等就可以执行 加载速度比load更快些

<script>
        window.addEventListener('load',function(){
            var btn = document.querySelector('button');
            btn.addEventListener('click',function(){
                alert('点击了我');
            })
        })
        window.addEventListener('load',function(){
            alert(22);
        })
        document.addEventListener('DOMContentLoaded',function(){
            alert(33);
        })
    </script>
    </head>
    <body>
        <button>点击</button>
    </body>

10.2.2 调整窗口大小

window.onresize = function(){}  //是调整窗口大小加载事件,当触发时就调用的处理函数

window.addEventListener("resize",function(){});

 注:

  1. 主要窗口大小发生像素变化,就会触发这个事件。
  2. 经常利用这个事件完成响应式布局。window.innerWidth当前屏幕的宽度
<style>
        div{
            width: 300px;
            height: 400px;
            background-color: pink;
        }
    </style>
    </head>
    <body>
        <script>
            window.addEventListener('load',function(){
                var div = document.querySelector('div');
                window.addEventListener('resize',function(){
                    console.log(window.innerWidth);
                    console.log('变化了');
                    if (window.innerWidth < 100){
                        div.style.display = 'none';
                    }else {
                        div.style.display = 'block';
                    }
                })
            })
        </script>
        <div></div>
    </body>

 10.3 定时器

10.3.1 setTimeout() 定时器

window.setTimeout(调用函数,[延迟的毫秒数]); //用于设置定时器,该定时器在定时器到期后执行调用函数

  1.  window可以省略
  2. 这个调用函数可以直接写函数,或者写函数名或者采用字符串‘函数名()’三种形式 第三种不推荐
  3. 延迟的毫秒数可以省略默认为0,如果写,必须是毫秒
  4. 因为定时器可能有很多,所以我们经常给定时器赋值一个标识符。
       <script>
            setTimeout(function(){
                console.log('时间到!');
            },2000);  //方法一
            function callback(){
                console.log('over');
            }
            setTimeout(callback,3000); //方法二
            
            setTimeout('callback()',2500);  //方法三
        </script>

 setTimeout()这个调用函数我们也称为回调函数callback

简单理解:像之前学的element.onclick = function(){}或者element.addEventListener("click",fn);里面的函数也是回调函数。上一件事干完,在回头调用这个函数

案例:5秒自动关闭广告

<body>
        <img src="./ad.jpg" alt="" class="ad">
        <script>
           var ad = document.querySelector('.ad');
           setTimeout(function(){
               ad.style.display = 'none';
           },5000)
        </script>
    </body>

10.3.2 停止setTimeout()定时器

window.clearTimeout(timeoutID) 

//window可省略 通过这个方法取消了先前通过的调用setTimeout()建立的定时器

 <button>停止计数器</button>
        <script>
            var btn = document.querySelector('button');
            var timer = setTimeout(function(){
                console.log('快离开 爆炸了');
            },5000);
            btn.addEventListener('click',function(){
                clearTimeout(timer);
            })
        </script>

10.3.3 setInterval()定时器

window.setInterval(回调函数,[间隔的毫秒数]);  //语法规范等和setTimeout(一样)

重复调用这个函数,每隔这个时间,就去调用一次回调函数。

        <script>
           setInterval(function(){
               console.log('你说得对');
           },1000)
        </script>

案例:京东倒计时

<style>
        *{
            margin: 0;
            padding: 0;
        }
       div{
           margin: 200px;
       }
       span{
           display: inline-block;
           width: 40px;
           height: 40px;
           background-color: black;
           font-size: 20px;
           color: #fff;
           text-align: center;
           line-height: 40px;
       }
    </style>
    </head>
    <body>
        <div>
            <span class="hour">1</span>
            <span class="minute">2</span>
            <span class="second">3</span>
        </div>
        <script>
          //获取元素
          var hour = document.querySelector('.hour'); //小时的黑色盒子
          var minute = document.querySelector('.minute'); //分钟的盒子
          var second = document.querySelector('.second'); //秒数的盒子
          var inputTime = +new Date('2022-5-5 22:00:00');
          countDown(); //先调用一次 防止第一次刷新有空白
          //开启定时器
          setInterval(countDown,1000);
          function countDown() {
            var nowTime = +new Date(); // 返回的是当前时间总的毫秒数
            var times = (inputTime - nowTime) / 1000; // times是剩余时间总的秒数 
            var h = parseInt(times / 60 / 60 % 24); //时
            h = h < 10 ? '0' + h : h;
            hour.innerHTML = h; // 把剩余的小时给 小时黑色盒子
            var m = parseInt(times / 60 % 60); // 分
            m = m < 10 ? '0' + m : m;
            minute.innerHTML = m;
            var s = parseInt(times % 60); // 当前的秒
            s = s < 10 ? '0' + s : s;
            second.innerHTML = s;
        }

        </script>
    </body>

10.3.4 清除setInterval定时器

window.clearInterval(interval ID);   //和clearTimeout()方法注意事项差不多

      <button class="begin">开启定时器</button>
      <div class="stop">清除定时器</div>
      <script>
          var begin = document.querySelector('.begin');
          var stop = document.querySelector('.stop');
          var timer = null; // 一定要放在外面 全局变量 null是一个空对象
          begin.addEventListener('clcik',function(){
              timer = setInterval(function(){
                console.log('hello world!')
              },1000)
          })
          stop.addEventListener('click',function(){
              clearInterval(timer);
          })
      </script>

案例:发送短信

<body>
<!-- 案例分析: 按钮点击之后,会禁用 disabled 为true 
2、同时按钮里面的内容会变化,注意 button 里面的内容通过 innerHTML修改
3、里面秒数是有变化的,因此需要用到定时器
4、定义一个变量,在定时器里面,不断递减
5、如果变量为0 说明到了时间,我们需要停止定时器,并且复原按钮初始状态-->
手机号码: <input type="number"><button>发送</button>
<script>
    var btn = document.querySelector('button');
    var time = 10;
    btn.addEventListener('click',function(){
        btn.disabled = true;
        var timer = setInterval(function(){
        if(time == 0 ){
            clearInterval(timer);
            btn.disabled = false;
            btn.innerHTML = '发送';
            time = 3;
        }else{
            btn.innerHTML = '还剩下'+ time +'秒';
            time--;
        }
        },1000)
    })
</script>
    </body>

10.3.5 this指向问题

this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,一般情况下this的最终指向的是那个调用它的对象

  1. 全局作用域或者普通函数中this指向全局对象window( 注意定时器里面的this指向window)
  2. 方法调用中谁调用this指向谁
  3. 构造函数中this指向构造函数的实例
// this 指向问题 一般情况下this的最终指向的是那个调用它的对象
// 1. 全局作用域或者普通函数中this指向全局对象window( 注意定时器里面的this指向window)
        console.log(this);
        function fn() {
            console.log(this);
        }
        window.fn();
        window.setTimeout(function() {
            console.log(this);
        }, 1000);
        // 2. 方法调用中谁调用this指向谁
        var o = {
            sayHi: function() {
                console.log(this); // this指向的是 o 这个对象
            }
        }
        o.sayHi();
        var btn = document.querySelector('button');
        // btn.onclick = function() {
        //     console.log(this); // this指向的是btn这个按钮对象
        // }
        btn.addEventListener('click', function() {
                console.log(this); // this指向的是btn这个按钮对象
            })
            // 3. 构造函数中this指向构造函数的实例
        function Fun() {
            console.log(this); // this 指向的是fun 实例对象
        }
        var fun = new Fun();

 11.JS执行队列

 11.1 JS是单线程

JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。

为了解决这个问题,HTML5提出web worker标准,允许JavaScript脚本创建多个线程,于是出现了同步和异步。

11.2 同步与异步

  • 同步:前一个任务结束后再去执行后一个任务,程序的执行顺序与任务的排列顺序是一致的
  • 异步:你在做一件事情时,因为这个事情花费很长时间,在做这件事的同时,还可以处理别的事情

本质区别:这条流水线上各个流程的执行顺序不同

同步任务:JS的异步任务是通过回调函数实现的。

一般而言,异步任务有三种类型:

  1. 普通事件:如click,resize等
  2. 资源加载:如load、error等
  3. 定时器:包括setInterval、setTimeout等

异步任务相关回调函数添加至任务队列中

11.3 JS执行机制

  1. 先执行栈中的同步任务
  2. 当有异步任务时,提交给对应的异步进程处理 。异步任务(回调函数)放入任务队列中
  3. 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行。

 由于主线程不断的重复获得任务、执行任务、在获取任务、在执行,所有这种机制被称为事件循环(event loop)

 12.location对象

12.1 什么是location对象

location属性用于获取或者设置窗体的URL,并且可以解析URL。因为这个属性返回的是一个对象,所以我们也称它为location对象。

URL:统一资源定位符,是互联网上标准资源的地址。

URL语法格式为:protocol://host[:port]/path/[?query]#fragment

query:参数 以键值对的形式 通过&符号分隔开

fragment:片段 #后面内容 常见于链接 锚点

12.2 location常用对象属性

location对象属性返回值
location.href获取 或者设置整个URL
location.host返回主机(域名)
location.port返回端口号
location.pathname返回路径
location.search返回参数
location.hash返回片段 #后面内容 常见于链接 锚点

 案例:5秒后跳转页面

 <body>
        <button>点击</button>
        <div></div>
<script>
   var btn = document.querySelector('button');
   var div = document.querySelector('div');
   //点击跳转页面
   btn.addEventListener('click',function(){
    location.href = 'http://www.baidu.com';
   })
   //倒计时5秒跳转页面
   var timer = 5;
   setInterval(function(){
       if(timer == 0){
           location.href = 'http://www.baidu.com';
       }else{
           div.innerHTML = '您将在' +timer+ '秒后跳转';
           timer--;
       }
   })
</script>
    </body>

案例:获取URL参数

<body>
<!--分析:1、第一个登录页面 里面可以提交表单 action提交到index.html页面
2、第二个页面,可以使用第一个页面的参数,实现一个数据不同页面之间的传递
3、第二个页面之所以可以使用第一个页面数据,是利用URL里面的location.search参数
4、在第二个页面中,需要提取参数  -->
<form action="index.html">
    用户名:<input type="text" name="uname" id="">
    <input type="submit" value="登录">
</form>
    </body>
 <div></div>
    <script>
        //1、先去掉?substr('起始位置',截取几个字符);
        var params = location.search.substr(1);
        //2、利用=把字符串分割成数组 split('=')
        var arr = params.split('=');
        console.log(arr); //["uname","输入的内容"]
        var div = document.querySelector('div');
        //3、写入数据
        div.innerHTML = arr[1] + '欢迎你';
    </script>

12.3 location对象方法

location对象方法返回值
location.assign()跟href一样,可跳转页面 (记录历史 可后退)
location.replace()替换当前页面,不记录历史 无法后退页面
location.reload()重新加载页面,相当于刷新按钮或者F5 如果参数为true  强制刷新 ctrl+F5
    <button>点击</button>
    <script>
        var btn = document.querySelector('button');
        btn.addEventListener('click',function(){
            location.assign('http://www.baidu.com');
            location.replace('http://www.baidu.com');
            location.reload();//参数为true 强制刷新
        })
    </script>

13.navigation对象


navigation对象包含有关浏览器的信息,它有很多属性,我们最常用的是userAgent,该属性可以返回用客户机发送服务器的user-agent头部的值。

14.history对象

与浏览器历史记录进行交互,该对象包含用户(在浏览器窗口中)访问过的URL。

history对象方法作用
back()可以后退功能
forward()前进功能
go(参数)前进后退功能 参数如果是1 前进1个页面 如果是-1 后退一个页面
<body>
    <a href="try.html">点击我去往列表页</a>
    <button>前进</button>
    <script>
       var btn = document.querySelector('button');
       btn.addEventListener('click',function(){
           history.go(1);
       })
    </script>
</body>
<body>
        <a href="index.html">点击我去列表</a>
        <button>后退</button>
        <script>
            var btn = document.querySelector('button');
            btn.addEventListener('click',function(){
                history.go(-1);
            })
        </script>
    </body>

15.PC端网页特效

15.1 元素偏移量offset系列

使用offset系列相关属性可以动态的得到该元素的位置(偏移)、大小等

  • 获得元素距离带有定位父元素的位置
  • 获得元素自身的大小(宽度高度)
  • 注意:返回数值不带单位
offset系列属性作用
element.offsetParent返回作为该元素带有定位的父级元素 如父级都没有定位则返回body
element.offsetTop返回元素相对带有定位元素上方的偏移
element.offsetLeft相对带有定位父元素左边框的偏移
element.offsetWidth返回自身包括padding 边框 内容区的高度 返回数值无单位
element.offsetHeight同上
 <style>
        *{
            margin: 0;
            padding: 0;
        }
        .father{
            width: 200px;
            height: 200px;
            background-color: pink;
            margin: 150px;
        }
        .son{
            width: 100px;
            height: 100px;
            background-color: purple;
            margin-left: 45px; 
        }
       .w{
            width:200px;
            height: 200px;
            background-color: skyblue;
            margin: 0 auto 200px;
            padding: 10px;
            border:15px solid red;
        } 
    </style>
    </head>
    <body>
        <div class="father">
            <div class="son"></div>
        </div>
        <div class="w"></div>
        <script>
            var father = document.querySelector('.father');
            var son = document.querySelector('.son');
//可以得到元素的偏移 位置 返回的不带单元的数值
            console.log(father.offsetTop); 
            console.log(father.offsetLeft);
//可以得到元素的大小 宽度和高度 是包含padding + border + width
            var w = document.querySelector('.w');
            console.log(w.offsetWidth);
            console.log(w.offsetHeight);
          //返回带有定位的父亲
            console.log(son.offsetParent);
            console.log(son.parentNode); //返回的是最近一级的父亲 亲爸爸 不管有没有定位
        </script>
    </body>

15.1.1 offset和style区别

offsetstyle
可以得到任意样式表中的样式值只能得到行内样式表中的样式值
获得的数值没有单位获得的数值有单位
offsetWidth包含padding+border+width不包含padding和border的值
offsetWidth等属性是只读属性 只能读取不能获值是可读写属性 可以获取也可以赋值
想要获取元素大小位置 用offset更合适给元素更改值 用style
   <style>
        .box{
            width: 200px;
            height: 200px;
            background-color: skyblue;
            padding: 10px;
        }
    </style>
    </head>
    <body>
        <div class="box" style="width:200px"></div>
        <script>
            var box = document.querySelector('.box');
            console.log(box.offsetWidth);  //220
            console.log(box.style.width);   //200px
            box.style.width = '300px'; //可修改
        </script>
    </body>

案例:获取鼠标移动坐标

<style>
        .box{
            width: 300px;
            height: 300px;
            background-color: pink;
            margin: 200px;
        }
    </style>
    </head>
    <body>
<!-- 1、首先得到鼠标在页面中的坐标( e.pageX, e.pageY)
2、其次得到盒子在页面中的距离(box.offsetLeft, box.offsetTop)
3、用鼠标距离页面的坐标减去盒子在页面中的距离, 得到 鼠标在盒子内的坐标 
4、mousemove 鼠标移动  -->
        <div class="box"></div>
        <script>
            var box = document.querySelector('.box');
        box.addEventListener('mousemove',function(e){
            var x = e.pageX - this.offsetLeft;
            var y = e.pageY - this.offsetTop;
            this.innerHTML = 'x坐标是' + x + 'y坐标是' + y;
        }
        )
        </script>
    </body>

案例:模态框拖拽

<style>
        .login-header {
            width: 100%;
            text-align: center;
            height: 30px;
            font-size: 24px;
            line-height: 30px;
        }
        *{
            padding: 0px;
            margin: 0px;
        }
        
        .login {
            display: none;
            width: 512px;
            height: 280px;
            position: fixed;
            border: #ebebeb solid 1px;
            left: 50%;
            top: 50%;
            background: #ffffff;
            box-shadow: 0px 0px 20px #ddd;
            z-index: 9999;
            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;
        }
        
        .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 {
            display: none;
            width: 100%;
            height: 100%;
            position: fixed;
            top: 0px;
            left: 0px;
            background: rgba(0, 0, 0, .3);
        }
        
        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:;">点击,弹出登录框</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>
        // 1. 获取元素
        var login = document.querySelector('.login');
        var mask = document.querySelector('.login-bg');
        var link = document.querySelector('#link');
        var closeBtn = document.querySelector('#closeBtn');
        var title = document.querySelector('#title');
        // 2. 点击弹出层这个链接 link  让mask 和login 显示出来
        link.addEventListener('click', function() {
                mask.style.display = 'block';
                login.style.display = 'block';
            })
            // 3. 点击 closeBtn 就隐藏 mask 和 login 
        closeBtn.addEventListener('click', function() {
                mask.style.display = 'none';
                login.style.display = 'none';
            })
            // 4. 开始拖拽
            // (1) 当我们鼠标按下, 就获得鼠标在盒子内的坐标
        title.addEventListener('mousedown', function(e) {
            var x = e.pageX - login.offsetLeft;
            var y = e.pageY - login.offsetTop;
            // (2) 鼠标移动的时候,把鼠标在页面中的坐标,减去 鼠标在盒子内的坐标就是模态框的left和top值
            document.addEventListener('mousemove', move)
            function move(e) {
                login.style.left = e.pageX - x + 'px';
                login.style.top = e.pageY - y + 'px';
            }
            // (3) 鼠标弹起,就让鼠标移动事件移除
            document.addEventListener('mouseup', function() {
                document.removeEventListener('mousemove', move);
            })
        })
    </script>
</body>

15.2 元素可视区client系列

获取元素可视区的相关信息 通过client系列的相关属性可以动态的 得到该元素的边框大小、元素大小等

client系列属性作用
element.clientTop返回元素上边框的大小
element.clientLeft返回元素左边框的大小
element.clientWidth返回自身包括padding、内容区的宽度,不含边框,返回数值不带单位
element.clientHeight同上

15.2.1 淘宝flexble.js源码分析

立即执行函数(function(){})()  或者 (function(){}()) 不需要调用,立马能够自己执行

主要作用:创建一个独立的作用域 里面所有的变量都是局部变量 不会有命名冲突的情况

<script>
        (function(){
            console.log(1);
        })();
        //也可以传递参数
        (function(a,b){
            console.log(a + b);
        })(2,3);  //这个小括号可以看成调用函数 5
        //第二种写法 (function(){}());
        (function sum(a,b){
            console.log(a + b);
            var sum = 10; //局部变量
        }(2,3));
    </script>
(function flexible(window, document) {
    // 获取的html 的根元素
    var docEl = document.documentElement
        // dpr 物理像素比
    var dpr = window.devicePixelRatio || 1

    // adjust body font size  设置我们body 的字体大小
    function setBodyFontSize() {
        // 如果页面中有body 这个元素 就设置body的字体大小
        if (document.body) {
            document.body.style.fontSize = (12 * dpr) + 'px'
        } else {
            // 如果页面中没有body 这个元素,则等着 我们页面主要的DOM元素加载完毕再去设置body
            // 的字体大小
            document.addEventListener('DOMContentLoaded', setBodyFontSize)
        }
    }
    setBodyFontSize();

    // set 1rem = viewWidth / 10    设置我们html 元素的文字大小
    function setRemUnit() {
        var rem = docEl.clientWidth / 10
        docEl.style.fontSize = rem + 'px'
    }

    setRemUnit()

    // reset rem unit on page resize  当我们页面尺寸大小发生变化的时候,要重新设置下rem 的大小
    window.addEventListener('resize', setRemUnit)
        // pageshow 是我们重新加载页面触发的事件
    window.addEventListener('pageshow', function(e) {
        // e.persisted 返回的是true 就是说如果这个页面是从缓存取过来的页面,也需要从新计算一下rem 的大小
        if (e.persisted) {
            setRemUnit()
        }
    })

    // detect 0.5px supports  有些移动端的浏览器不支持0.5像素的写法
    if (dpr >= 2) {
        var fakeBody = document.createElement('body')
        var testElement = document.createElement('div')
        testElement.style.border = '.5px solid transparent'
        fakeBody.appendChild(testElement)
        docEl.appendChild(fakeBody)
        if (testElement.offsetHeight === 1) {
            docEl.classList.add('hairlines')
        }
        docEl.removeChild(fakeBody)
    }
}(window, document))

15.3 元素滚动sroll系列

使用这个可以动态的得到元素的大小、滚动距离等

scroll系列属性作用
element.scrollTop返回被卷上去的上侧距离,返回数值不带单位
element.scrollLeft返回被卷上去的左侧距离,返回数值不带单位
element.scrollWidth返回自身实际的宽度,不含边框,返回数值不带单位
element.scrollHeight返回自身实际的高度,不含边框,不带单位
 <style>
      div{
        width: 100px;
        height: 200px;
        background-color: skyblue;
        padding: 10px;
        border: 5px solid red;
        overflow: auto;
      }
    </style>
</head>
<body>
  <div>测试一下测试一下测试一下测试一下测试一下测试一下测试一下测试一下测试一下测试一下测试一下测试一下测试一下测试一下测试一下测试一下测试一下测试一下测试一下测试一下测试一下测试一下</div>
  <script>
    var div = document.querySelector('div');
    console.log(div.scrollHeight);  //392 实际高度
    console.log(div.clientHeight);  //220 加padding
    //scroll滚动事件当我们滚动条发生变化时会触发
    div.addEventListener('scroll',function(){
      console.log(div.scrollTop);
    })
  </script>
</body>

 案例:淘宝固定侧边栏

页面被卷去的头部写法:

  1. 声明了DTD,可以使用document.documentElement.scrollTop
  2. 未声明DTD,使用document.body.scrollTop
  3. 兼容性新方法 window.pageYOffset和window.pageXOffset
<style>
      .slider-bar{
        position: absolute;
        left: 50%;
        top:300px;
        margin-left: 500px;
        width: 45px;
        height: 130px;
        background-color: pink;
      }
      .w{
        width: 1100px;
        margin: 10px auto;
      }
      .header{
        height: 150px;
        background-color: skyblue;
      }
      .banner{
        height: 250px;
        background-color: red;
      }
      .main{
        height: 1000px;
        background-color: antiquewhite;
      }
      span{
        display: none;
        position:absolute;
        bottom: 0;
      }
    </style>
</head>
<body>
 <div class="slider-bar">
   <span class="goBack">返回顶部</span>
 </div>
 <div class="header w">头部区域</div>
 <div class="banner w">banner区域</div>
 <div class="main w">主体区域</div>
  <script>  
  //1、获取元素
  var sliderbar = document.querySelector('.slider-bar');
  var banner = document.querySelector('.banner');
  //banner.offsetTop 就是被卷去头部的大小 要写在滚动的外面
  var bannerTop = banner.offsetTop;
  //3、侧边固定后应该变化的数值
  var sliderbarTop = sliderbar.offsetTop - bannerTop;
 //4、获取main主体元素
  var main = document.querySelector('.main');
  var goBack = document.querySelector('.goBack');
  var mainTop = main.offsetTop;
  //2、页面滚动事件
  document.addEventListener('scroll',function(){
    //window.pageYOffset 页面被卷去的头部
    if (window.pageYOffset >= bannerTop){
      sliderbar.style.position = 'fixed';
      sliderbar.style.top = sliderbarTop + 'px';
    }else{
      sliderbar.style.position = 'absolute';
      sliderbar.style.top = '300px';
    }
    //5、当页面滚动到main时 显示goback
    if (window.pageYOffset >= mainTop){
      goBack.style.display = 'block';
    }else{
      goBack.style.position = 'none';
    }
  })
  </script>
</body>

返回顶部 

window.scroll(x,y)             //滚动窗口至文档中的特定位置  不带px

15.4 三大系列总结

三大系列对比作用常用于
element.offsetWidth自身包括padding边框、内容区的宽度、不带单位

获取元素位置 offsetLeft

offsetTop

element.clientWidth自身包括padding、内容区的宽度、不含边框、不带单位获取元素大小 clientWidth clientHeight
element.scrollWidth自身实际的宽度、不含边框、不带单位获取滚动距离 scrollTop scrollLeft

15.5 mouseenter和mouseover的区别

  • 当鼠标移动到元素上时会触发mouseenter事件
  • mouseover鼠标经过自身盒子会触发,经过子盒子还会触发。mouseenter只会经过自身盒子触发
  • mouseenter不会冒泡 跟它搭配 鼠标离开mouseleave同样不会冒泡
<style>
      .father{
        width: 300px;
        height: 300px;
        background-color: skyblue;
      }
      .son{
        width: 200px;
        height: 200px;
        background-color: pink;
      }
    </style>
</head>
<body>
  <div class="father">
    <div class="son"></div>
  </div>
  <script>
    var father = document.querySelector('.father');
    var son = document.querySelector('.son');
    
    father.addEventListener('mouseover',function(){
      console.log(11);  //鼠标经过son也打印
    })
    /* 
    father.addEventListener('mouseenter',function(){
      console.log(11);
    })
    */
  </script>

15.6 动画封装效果

15.6.1 动画实现原理

  1. 获得盒子当前位置
  2. 让盒子在当前位置加上1个移动距离
  3.  利用定时器不断重复这个操作
  4. 加一个结束定时器的条件

 注意此元素需要添加定位, 才能使用element.style.left

<style>
     div{
       position: absolute;
       left:0;
       width: 100px;
       height: 100px;
       background-color: skyblue;
     }
    </style>
</head>
<body>
<div></div>
</body>
<script>
  var div = document.querySelector('div');
  var timer = setInterval(function(){
    if (div.offsetLeft >= 400){
      clearInterval(timer);
    }
    div.style.left = div.offsetLeft + 5 + 'px';
  },30)
</script>

15.6.2 简单动画函数封装

<style>
     div{
       position: absolute;
       left:0;
       width: 100px;
       height: 100px;
       background-color: skyblue;
     }
     span{
       position: absolute;
       top:150px;
       display: block;
       left: 0;
       width: 200px;
       height: 200px;
       background-color: #24e;

     }
    </style>
</head>
<body>
<div></div>
<span>移动</span>
<button>点击移动走</button>  //定义一个按钮
</body>
<script>
  //obj 目标对象 target 目标位置
  function animate(obj,target){
//2、解决方案:先清除之前的定时器,只保留当前的一个定时器
   clearInterval(obj.timer);
//obj.timer 给不同的元素指定了不同的定时器 节省空间
    obj.timer = setInterval(function(){
    if (obj.offsetLeft >= target){
      clearInterval(obj.timer);
    }
    obj.style.left = obj.offsetLeft + 1 + 'px';
  },30)
  }
  var div = document.querySelector('div');
  var span = document.querySelector('span');
  var button = documnet.querySelector('button');  
  //调用函数
  animate(div,300);
 //1、如果不断点击按钮,元素的速度会越来越快,因为开启了太多计数器
  btn.addEventListener('click',function(){
    animate(span,300);
  })
</script>

15.6.3 缓动动画+多个目标值间移动

缓动动画就是让元素运动速度有所变化,最常见的是让速度慢慢停下来

  1. 让盒子每次移动的距离慢慢变小, 速度就会慢慢落下来。
  2. 核心算法:(目标值 - 现在的位置) / 10 做为每次移动的距离 步;当我们点击按钮时,判断步长是正还是负值,如果是正值,则步长往大取整,如果是负值,往小取整
  3. 停止的条件是: 让当前盒子位置等于目标位置就停止定时器
<style>
     span{
       position: absolute;
       top:150px;
       display: block;
       left: 0;
       width: 200px;
       height: 200px;
       background-color: #24e;
     }
    </style>
</head>
<body>
<span>移动</span>
<button class="btn500">移动500</button>
<button class="btn800">移动800</button>
</body>
<script>
  //obj 目标对象 target 目标位置
  function animate(obj,target){
   clearInterval(obj.timer);
    obj.timer = setInterval(function(){
      //步长写在计数器里面 不要出现小数问题
      var step = (target - obj.offsetLeft) / 10;
      step = step > 0 ? Math.ceil(step) : Math.floor(step);
      if (obj.offsetLeft == target){
      clearInterval(obj.timer);
    }
    obj.style.left = obj.offsetLeft + step + 'px';
  },15)
  }
  var span = document.querySelector('span');
  var btn500 = document.querySelector('.btn500');
  var btn800 = document.querySelector('.btn800')
  //调用函数
  btn500.addEventListener('click',function(){
    animate(span,500);
  })
  btn800.addEventListener('click',function(){
    animate(span,800);
  })
</script>

15.6.4 缓动动画添加回调函数

原理:函数可以作为一个参数。将这个函数作为参数传到另一个函数里面,当那个函数执行完之后,再执行传过去的这个函数,这个过程就叫做回调。

回调函数写的位置:定时器结束的位置。(代码续上节)

function animate(obj,target,callback){
    //相当于 callback = function(){}
   clearInterval(obj.timer);
    obj.timer = setInterval(function(){
      //步长写在计数器里面 不要出现小数问题
      var step = (target - obj.offsetLeft) / 10;
      step = step > 0 ? Math.ceil(step) : Math.floor(step);
      if (obj.offsetLeft == target){
      clearInterval(obj.timer);
      //写在定时器结束里面
      if (callback){
        callback();
      }
    }
 btn800.addEventListener('click',function(){
    animate(span,800,function(){
      span.style.backgroundColor = 'red';
    });
  })

15.6.5 动画函数封装及使用

 实现当鼠标经过sliderbar就会让con这个盒子滑动到左侧

当鼠标离开sliderbar就会让con这个盒子滑动到右侧

<style>
     .sliderbar{
       position: fixed;
       right: 0;
       bottom: 100px;
       width: 40px;
       height: 40px;
       text-align: center;
       line-height: 40px;
       cursor: pointer;
       color: #fff;
     }
     .con{
       position: absolute;
       left: 0;
       top:0;
       width: 200px;
       height: 40px;
       background-color: skyblue;
       z-index: -1;
     }
    </style>
    <script src="animate.js"></script>
</head>
<body>
  <div class="sliderbar">
    <span><-</span>
    <div class="con">问题反馈</div>
  </div>
  <script>
    //获取元素
    var sliderbar = document.querySelector('.sliderbar');
    var con = document.querySelector('.con');
    sliderbar.addEventListener('mouseenter',function(){
      animate(con,-160,function(){
        sliderbar.children[0].innerHTML = '->';
      });
    })
    sliderbar.addEventListener('mouseleave',function(){
      animate(con,0,function(){
      silderbar.childer[0].innerHTML = '<-';
    });
    })
  </script>
</body>
//obj 目标对象 target 目标位置
function animate(obj,target,callback){
    //相当于 callback = function(){}
   clearInterval(obj.timer);
    obj.timer = setInterval(function(){
      //步长写在计数器里面 不要出现小数问题
      var step = (target - obj.offsetLeft) / 10;
      step = step > 0 ? Math.ceil(step) : Math.floor(step);
      if (obj.offsetLeft == target){
      clearInterval(obj.timer);
      //写在定时器结束里面
      if (callback){
        callback();
      }
    }
    obj.style.left = obj.offsetLeft + step + 'px';
  },15);
  }

带有动画的返回顶部

把我们之前学过的封装动画中左右改为垂直即可

 <style>
      .slider-bar{
      position: absolute;
      left: 50%;
      top:300px;
      margin-left: 550px;
      width: 45px;
      height: 130px;
      background-color: pink;
    }
    .w{
      width: 1100px;
      margin: 10px auto;
    }
    .header{
      height: 150px;
      background-color: skyblue;
    }
    .banner{
      height: 250px;
      background-color: red;
    }
    .main{
      height: 1000px;
      background-color: antiquewhite;
    }
    span{
      display: none;
      position:absolute;
      bottom: 0;
    }
    </style>
</head>
<body>
<div class="slider-bar">
 <span class="goBack">返回顶部</span>
</div>
<div class="header w">头部区域</div>
<div class="banner w">banner区域</div>
<div class="main w">主体区域</div>
<script>  
//1、获取元素
var sliderbar = document.querySelector('.slider-bar');
var banner = document.querySelector('.banner');
//banner.offsetTop 就是被卷去头部的大小 要写在滚动的外面
var bannerTop = banner.offsetTop;
//3、侧边固定后应该变化的数值
var sliderbarTop = sliderbar.offsetTop - bannerTop;
//4、获取main主体元素
var main = document.querySelector('.main');
var goBack = document.querySelector('.goBack');
var mainTop = main.offsetTop;
//2、页面滚动事件
document.addEventListener('scroll',function(){
  //window.pageYOffset 页面被卷去的头部
  if (window.pageYOffset >= bannerTop){
    sliderbar.style.position = 'fixed';
    sliderbar.style.top = sliderbarTop + 'px';
  }else{
    sliderbar.style.position = 'absolute';
    sliderbar.style.top = '300px';
  }
  //5、当页面滚动到main时 显示goback
  if (window.pageYOffset >= mainTop){
    goBack.style.display = 'block';
  }else{
    goBack.style.position = 'none';
  }
});
//当点击了返回顶部模块,就让窗口滚动到页面的最上方
goBack.addEventListener('click',function(){
  //因为是窗口滚动,所以对象是window
  animate(window,0)
  });
  // 动画函数
  function animate(obj, target, callback) {
      // console.log(callback);  callback = function() {}  调用的时候 callback()
     // 先清除以前的定时器,只保留当前的一个定时器执行
      clearInterval(obj.timer);
      obj.timer = setInterval(function() {
      // 步长值写到定时器的里面
      // 把我们步长值改为整数 不要出现小数的问题
      // var step = Math.ceil((target - obj.offsetLeft) / 10);
      var step = (target - window.pageYOffset) / 10;
      step = step > 0 ? Math.ceil(step) : Math.floor(step);
      if (window.pageYOffset == target) {
     // 停止动画 本质是停止定时器
        clearInterval(obj.timer);
        // 回调函数写到定时器结束里面
        // if (callback) {
       // 调用函数
       // callback();
         // }
       callback && callback();
      }
      // 把每次加1 这个步长值改为一个慢慢变小的值  步长公式:(目标值 - 现在的位置) / 10
       // obj.style.left = window.pageYOffset + step + 'px';
        window.scroll(0, window.pageYOffset + step);
           }, 15);
        }
</script>
</body>

15.6.6 节流阀

防止轮播图按钮连续点击造成播放过快

目的:当上一个函数动画内容执行完毕,再去执行下一个函数动画,让事件无法连续触发。

核心思路:利用回调函数,添加一个变量控制,锁住函数和解锁函数。

  1. 开始设置一个变量 var flag = true;
  2. if(flag){flag = false;do something}   关闭水龙头
  3. 利用回调函数动画执行完毕,flag = true 打开水龙头

15.6.7 常见网页特效案例

筋斗云

案例分析:

  1. 利用动画函数做动画效果
  2. 原先筋斗云的起始位置为0
  3. 鼠标经过某个小li,把当前小li的offsetLeft位置作为目标值即可
  4. 鼠标离开某个小li,就把目标值设为0

html+css

<style>
      *{
        margin: 0;
        padding: 0;
      }
      body{
        background-color: black;
      }
      ul{
        list-style: none;
      }
      .c-nav{
        width: 900px;
        height: 42px;
        background: #fff url(./images/rss.png) no-repeat right center;
        margin: 100px auto;
        border-radius: 5px;
        position: relative;
      }
      .c-nav ul{
        position: absolute;
      }
      .c-nav li{
        float: left;
        width: 83px;
        text-align: center;
        line-height: 42px;
      }
      .c-nav li a{
        color: #333;
        text-decoration: none;
        display: inline-block;
        height: 42px;
      }
      .c-nav li a:hover{
        color: white;
      }
      .c-nav li.current a{
        color: #0dff1d;
      }
      .cloud{
        position: absolute;
        left:0;
        top:0;
        width:83px;
        height: 42px;
        background: url(images/cloud.gif) no-repeat;
      }
    </style>
    <script src="./animate.js"></script>
    <script>
      window.addEventListener('load',function(){
        //1、获取事件
        var cloud = document.querySelector('.cloud');
        var c_nav = document.querySelector('.c-nav');
        var lis = c_nav.querySelectorAll('li');
        //2、给所有的小li绑定事件
        var current = 0;  //筋斗云的起始位置
        for (var i = 0;i < lis.length;i++){
          //鼠标经过把当前li的位置作为目标值
          lis[i].addEventListener('mouseenter',function(){
            animate(cloud,this.offsetLeft);
          })
          //鼠标离开就复原0
          lis[i].addEventListener('mouseleave',function(){
            animate(cloud,current);
          });
          //当点击鼠标,就把当前位置作为目标值
          lis[i].addEventListener('click',function(){
            current = this.offsetLeft;
          });
          //鼠标离开回到起始的位置
          
        }
      })
    </script>
</head>
<body>
  <div class="c-nav">
    <span class="cloud"></span>
    <ul>
      <li class="current"><a href="#">首页新闻</a></li>
      <li><a href="#">师资力量</a></li>
      <li><a href="#">活动策划</a></li>
      <li><a href="#">企业文化</a></li>
      <li><a href="#">招聘信息</a></li>
      <li><a href="#">公司简介</a></li>
      <li><a href="#">上海校区</a></li>
      <li><a href="#">报名咨询</a></li>
    </ul>
  </div>
</body>

 js部分

function animate(obj, target, callback) {
    // console.log(callback);  callback = function() {}  调用的时候 callback()

    // 先清除以前的定时器,只保留当前的一个定时器执行
    clearInterval(obj.timer);
    obj.timer = setInterval(function() {
        // 步长值写到定时器的里面
        // 把我们步长值改为整数 不要出现小数的问题
        // var step = Math.ceil((target - obj.offsetLeft) / 10);
        var step = (target - obj.offsetLeft) / 10;
        step = step > 0 ? Math.ceil(step) : Math.floor(step);
        if (obj.offsetLeft == target) {
            // 停止动画 本质是停止定时器
            clearInterval(obj.timer);
            // 回调函数写到定时器结束里面
            // if (callback) {
            //     // 调用函数
            //     callback();
            // }
            callback && callback();
        }
        // 把每次加1 这个步长值改为一个慢慢变小的值  步长公式:(目标值 - 现在的位置) / 10
        obj.style.left = obj.offsetLeft + step + 'px';

    }, 15);
}

16、移动端网页特效

16.1 触屏事件

移动端兼容性较好,不用考虑js的兼容性问题

16.1.1 常见的触屏事件

触屏touch事件说明
touchstart手指触摸到一个DOM元素时触发
touchmove手指在一个DOM元素上滑动时触发
touchend手指从一个DOM元素上移开时触发
<style>
      div{
        width: 500px;
        height: 500px;
        margin: 0 auto;
        background-color: pink;
      }
    </style>
</head>
<body>
  <div></div>
  <script>
    //获取元素
    //手指触摸DOM事件
    var div = document.querySelector('div');
    div.addEventListener('touchstart',function(){
      console.log('摸了一次');
    })
    //手指在DOM移动事件
    div.addEventListener('touchmove',function(){
      console.log('继续摸');
    })
    //手指离开DOM元素事件
    div.addEventListener('touchend',function(){
      console.log('要离开了');
    })
  </script>

16.1.2 触摸事件对象(TouchEvent)

touchevent是一类描述手指在触摸平面的状态变化的事件。这类事件用于描述一个或多个触点,使开发者可以检测触点的移动,触点的增加和减少等

常见的三个对象列表:

触摸列表说明
touches正在触摸屏幕的所有手指的一个列表
targetTouches正在触摸当前DOM元素上的手指的一个列表
changedTouches手指状态发生了改变的列表,从无到有,从有到无变化

区别:如果侦听的是一个DOM元素,touches和targetTouches是一样的

当我们离开屏幕时,就没有了touches和targetTouches列表 但会有changedTouches

16.1.3 移动端拖动元素

拖动元素需要当前手指的坐标值 可以使用targetTouches[0]里面的pageX和pageY

移动端拖动的原理:手指移动中,计算出手指移动的距离。用盒子原来的位置+手指移动的距离

手指移动的距离:手指滑动中的位置 - 手指刚开始触摸的位置

触摸元素touchstart:获取手指初始坐标,同时获得盒子原来的位置

移动手指touchmove:计算手指的滑动距离,并且移动盒子

离开手指touchend

注:手指移动也会触发滚动屏幕所以这里要阻止屏幕的滚动 e.preventDefault();

<style>
      div{
        position: absolute;
        width: 500px;
        height: 500px;
        margin: 0 auto;
        background-color: pink;
      }
    </style>
</head>
<body>
  <div></div>
  <script>
    var div = document.querySelector('div');
    var startX = 0;  //获得手指初始坐标
    var startY = 0;
    var x = 0; // 获得盒子原来的位置
    var y = 0;
    div.addEventListener('touchstart',function(e){
      //获取手指初始坐标
      startX = e.targetTouches[0].pageX;
      startY = e.targetTouches[0].pageY;
      x = this.offsetLeft;
      y = this.offsetTop;
    });
    div.addEventListener('touchmove',function(e){
      //计算手指的移动距离
      var moveX = e.targetTouches[0].pageX - startX;
      var moveY = e.targetTouches[0].pageY - startY;
      //移动盒子 原来的位置 + 手指移动的位置
      this.style.left = x + moveX + 'px';
      this.style.top = y + moveY + 'px';
      e.preventDefault(); // 阻止屏幕滚动的默认行为
    });
  </script>
</body>

16.2移动端常见开发插件

16.2.1 click延时解决方案

移动端click事件会有300ms的延时,原因是移动端屏幕双击会缩放页面

  • 用户缩放
  • 封装tap(一次只能解决一个元素)
  • 使用fastclick插件

16.2.2 插件(fastclick)

JS插件就是js文件 一般是为了解决某个问题而专门存在,其功能单一,并且比较小。

fastclick插件解决300ms延迟。使用延时  GitHub官网地址:https://github.com/ftlabs/fastclick

使用方法:

  1. 引入插件
  2. 使用下面这种语法格式去引用:
    if ('addEventListener' in document) {
    	document.addEventListener('DOMContentLoaded', function() {
    		FastClick.attach(document.body);
    	}, false);
    }
  3. 正常使用
var div = document.querySelector('div');
div.addEventListener('click',function(){
    alert(11);
})

16.2.3 Swiper插件

网址:Swiper中文网-轮播图幻灯片js插件,H5页面前端开发

  1. 引入相关文件 (先引入swiper.js,在引入自己写的)
  2. 按照语法规范(html结构 不要更改里面的结构和类名、css样式、js引进 )
  3. 也可以根据官网提示进行参数的更改

16.2.4 其他移动端常见插件

superslide :http://www.superslide2.com/

iscroll:https://github.com/cubiq/iscroll

使用插件总结

  1. 确认插件实现的功能
  2. 去官网查看使用说明
  3. 下载插件
  4. 打开demo实例文件,查看需要引入的相关文件,并且引入
  5. 复制demo实例文件中的结构html,样式css以及js代码

16.2.5 视频插件zy.media.js

不同的视频格式文件,我们可以通过source解决。

但是外观样式,还有暂停,播放,全屏等功能我们只能自己写代码解决。

这个时候我们可以使用插件方式来制作。

16.3 移动端常用开发框架

一套架构,会基于自身的特点向用户提供一套较为完整的解决方案。框架的控制权在框架本身,使用者要按照框架所规定的某种规范进行开发。

插件一般是为解决某个问题而专门存在,其功能单一,并且比较小。

前端常用框架有Bootstrap、Vue、Angular、React等。既能开发PC端,也可以开发移动端

常用移动端插件有swiper、superslide、iscroll等

16.3.1 Bootstrap

是一个简洁、直观、强悍的前端开发框架,让web开发更迅速、简单。

使用步骤:

  1. 引入相关js文件
  2. 复制HTML结构
  3. 修改对应样式
  4. 修改相应JS参数

16.3.2 本地存储导读

sessionStorage

  • 本地存储特效
  • 数据存储在用户浏览器中
  • 设置、读取方便、甚至页面刷新不丢失数据
  • 容量较大,sessionStroage约5M、localStorage约20M
  • 只能存储字符串,可以将对象JSON.stringify()编码后存储

window.sessionStorage

  1. 生命周期为关闭浏览器窗口
  2. 在同一个窗口(页面)下数据可以共享
  3. 以键值对的形式存储使用

存储数据

sessionStorage.setltem(key,value)

获取数据

sessionStorage.getltem(key)

删除数据

sessionStorage.removeltem(key)

删除所有数据

sessionStorage.clear()

<body>
    <input type="text">
    <button class="set">存储数据</button>
    <button class="get">获取数据</button>
    <button class="remove">删除数据</button>
    <button class="del">清空所有数据</button>
    <script>
        console.log(localStorage.getItem('username'));

        var ipt = document.querySelector('input');
        var set = document.querySelector('.set');
        var get = document.querySelector('.get');
        var remove = document.querySelector('.remove');
        var del = document.querySelector('.del');
        set.addEventListener('click', function() {
            // 当我们点击了之后,就可以把表单里面的值存储起来
            var val = ipt.value;
            sessionStorage.setItem('uname', val);
            sessionStorage.setItem('pwd', val);
        });
        get.addEventListener('click', function() {
            // 当我们点击了之后,就可以把表单里面的值获取过来
            console.log(sessionStorage.getItem('uname'));

        });
        remove.addEventListener('click', function() {
            // 
            sessionStorage.removeItem('uname');

        });
        del.addEventListener('click', function() {
            // 当我们点击了之后,清除所有的
            sessionStorage.clear();
        });
    </script>
</body>

window.localStorage

  1. 生命周期永久生效,除非手动删除 否则关闭页面也会存在
  2. 可以多窗口共享(同一个浏览器可以共享)
  3. 以键值对的形式存储使用

存储数据

localStorage.setltem(key,value)

获取数据

localStorage.getltem(key)

删除数据

localStorage.removeltem(key)

删除所有数据

localStorage.clear()

<body>
    <input type="text">
    <button class="set">存储数据</button>
    <button class="get">获取数据</button>
    <button class="remove">删除数据</button>
    <button class="del">清空所有数据</button>
    <script>
        console.log(localStorage.getItem('username'));

        var ipt = document.querySelector('input');
        var set = document.querySelector('.set');
        var get = document.querySelector('.get');
        var remove = document.querySelector('.remove');
        var del = document.querySelector('.del');
        set.addEventListener('click', function() {
            // 当我们点击了之后,就可以把表单里面的值存储起来
            var val = ipt.value;
            localStorage.setItem('uname', val);
            sessionStorage.setItem('pwd', val);
        });  //关闭页面依然会有
        get.addEventListener('click', function() {
            // 当我们点击了之后,就可以把表单里面的值获取过来
            console.log(localStorage.getItem('uname'));

        });
        remove.addEventListener('click', function() {
            // 
            localStorage.removeItem('uname');

        });
        del.addEventListener('click', function() {
            // 当我们点击了之后,清除所有的
            localStorage.clear();
        });
    </script>
</body>

案例:记住用户名

  1. 把数据存起来,用到本地存储
  2. 关闭页面,也可以显示用户名(localStorage)
  3. 打开页面 先判断是否有这个用户名,如果有,就在表单里显示用户名,并且勾选复选框
  4. 当复选框发生改变时change事件
  5. 如果勾选,就存储,否则移除
<input type="text" id="username">
    <input type="checkbox" name="" id="remember">记住用户名
    <script>
        var username = document.querySelector('#username');
        var remember = document.querySelector('#remember');
        if(localStorage.getItem('username')){
            username.value = localStorage.getItem('username');
            remember.checked = true;
        }
        remember.addEventListener('change',function(){
            if(this.checked){
                localStorage.getItem('username',username.value)
            }else{
                localStorage.removeItem('username');
            }
        })
    </script>

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值