JavaScript中Dom0713

JavaScript中Dom

1.基本概念

1.1 什么是window?
  • window:是一个全局对象, 代表浏览器中一个打开的窗口, 每个窗口都是一个window对象(整个窗口哦)
1.2 什么是document?
  • document是window的一个属性, 这个属性是一个对象
  • document: 代表当前窗口中的整个网页
  • document对象保存了网页上所有的内容, 通过document对象就可以操作网页上的内容

1.3 什么是DOM?

  • DOM 定义了访问和操作 HTML文档(网页)的标准方法,所以学习DOM就是学习如何通过document对象操作网页上的内容

2.获取dom元素==(注意理解节点的概念)==

在JavaScript中HTML标签也称之为DOM元素,使用document的时候前面不用加window

2.1通过id获取
  • document.getElementById("box");
  • 由于id不可以重复, 所以找到了就会将找到的标签包装成一个对象返回给我们, 找不到就返回Null
  • 注意点: DOM操作返回的是一个对象, 这个对象是宿主类型对象(浏览器提供的对象)
2.2 通过class名称获取
  • document.getElementsByClassName("father");
  • 由于class可以重复, 所以找到了就返回一个存储了标签对象的数组, 找不到就返回一个空数组
2.3 通过name名称获取
  • <form>
            <input type="text" name="test">
            <input type="password" name="test">
    </form>
    let oDivs = document.getElementsByName("test");
    console.log(oDivs);
    
  • 由于name可以重复, 所以找到了就返回一个存储了标签对象的数组, 找不到就返回一个空数组

  • getElementsByName 在不同的浏览器其中工作方式不同。在IE和Opera中, getElementsByName() 方法还会返回那些 id 为指定值的元素

2.4 通过标签名称获取
  • document.getElementsByTagName("div");
  • 由于标签名称可以重复, 所以找到了就返回一个存储了标签对象的数组, 找不到就返回一个空数组
2.5 通过选择器获取(重点)
  • let oDiv = document.querySelector(".box");
  • querySelector只会返回根据指定选择器找到的第一个元素
2.6 通过选择器获取2(重点)
  • let oDivs = document.querySelectorAll(".father");
  • querySelectorAll会返回指定选择器找到的所有元素
2.7 获取元素中所有子元素或子节点
  • 首先了解一下什么是节点

    • DOM对象(document), 这个对象以树的形式保存了界面上所有的内容,HTML页面每一部分都是由节点(标签(元素),文本,属性)
    • 节点树.png
  • let oDiv = document.querySelector("div");
    // children属性获取到的是指定元素中所有的子元素
    console.log(oDiv.children);
    // childNodes属性获取到的是指定元素中所有的节点
     console.log(oDiv.childNodes);
    for(let node of oDiv.childNodes){
       console.log(node.nodeType); 
        if(node.nodeType === Node.ELEMENT_NODE){  //判断节点类型
            console.log(node);
        }
    }
    
2.8获取指定节点第一个或最后一个子节点或子元素
  • let oDiv = document.querySelector("div");
    //第一个子节点
    console.log(oDiv.firstChild);
    //获取指定元素中的第一个子元素
    console.log(oDiv.firstElementChild);
    
    //获取指定节点中最后一个子节点
    console.log(oDiv.lastChild);
    // 获取指定元素中最后一个子元素
    console.log(oDiv.lastElementChild);
    
2.9 获取子元素获取父节点或度元素
  • let item = document.querySelector(".item");
    //其实获取的一样,只不过由于火狐浏览器以前不支持parentNode,所以才有了两个
    console.log(item.parentElement);
    console.log(item.parentNode);
    let parentEle = item.parentElement || item.parentNode;//兼容性写法
    console.log(parentEle);
    
2.10 获取相邻上一个节点或元素/下一个节点或元素
  • //获取相邻上一个节点
    console.log(item.previousSibling);
    //获取相邻上一个元素
    console.log(item.previousElementSibling);
    //获取相邻下一个节点
    console.log(item.nextSibling);
    //获取相邻下一个元素
    console.log(item.nextElementSibling);
    

3.节点增删改查

首先我们有这样一个结构

  • <div>
        <h1>我是标题</h1>
        <p>我是段落</p>
    </div>
    

然后我们创建一个节点

  • let oSpan = document.createElement("span");
    // console.log(oSpan);
    // console.log(typeof oSpan);  是一个对象类型
    

然后将这个节点添加进去

  • // 注意点: appendChild方法会将指定的元素添加到最后
    let oDiv = document.querySelector("div");
    oDiv.appendChild(oSpan)
    

那么如果我想添加在指定的地方怎么办

  • let oDiv = document.querySelector("div");
    let oH1 = document.querySelector("h1");
    let oP = document.querySelector("p");
    oDiv.insertBefore(oSpan, oH1); //我就把oSpan插到了oH1前面
    oDiv.insertBefore(oSpan, oP);
    
    

该怎么删除呢

  • // 注意点: 在js中如果想要删除某一个元素, 只能通过对应的父元素来删除,元素是不能够自杀的
    console.log(oSpan.parentNode);
    oSpan.parentNode.removeChild(oSpan);
    oDiv.parentNode.removeChild(oDiv);
    
    

再看一下如何克隆一个节点

  • // 注意点: cloneNode方法默认不会克隆子元素, 如果想克隆子元素需要传递一个true
    let oDiv = document.querySelector("div");
    let newDiv =  oDiv.cloneNode();//只克隆自己,不可隆子元素
    let newDiv =  oDiv.cloneNode(true);
    console.log(newDiv);
    
    

4.元素属性的增删改查

4.1 基本概念
  • 无论是通过document创建还是查询出来的标签,系统都会将元素包装成一个对象返回给我们,系统在包装这个对象的时候会自动将元素的属性都包装到这个对象中,所以只要拿到这个对象就可以拿到标签属性,操作标签属性.
4.2 如何获取元素属性
  • <img src="images/1.jpg" alt="我是alt222" title="我是title" nj="666">
    let oImg = document.querySelector("img");
    console.log(oImg.alt);
    console.log(oImg.getAttribute("alt"));
    //注意点: 通过对象.属性名称的方式无法获取到自定义属性的取值
    //通过getAttribute方法可以获取到自定义属性的取值
    console.log(oImg.nj);//获取不到
    console.log(oImg.getAttribute("nj"))
    
    
4.3 如何修改元素属性
  • oImg.title = "新的title";
    oImg.setAttribute("title", "新的title222"); //给属性名为title的内容设为新的title222
     //注意点和获取元素属性一样
    oImg.nj = "123";
    oImg.setAttribute("nj", "123");
    
    
4.4 如何新增元素属性
  • // 注意点: setAttribute方法如果属性不存在就是新增, 如果属性存在就是修改
    oImg.setAttribute("it666", "itzb");
    
    
4.5 如何删除元素属性
  • oImg.removeAttribute("alt");
    // 注意点和获取元素属性一样
    oImg.nj = "";
    oImg.removeAttribute("nj");
    
    

5.元素内容操作

5.1 获取元素内容
  • /*
    1.innerHTML获取的内容包含标签, innerText/textContent获取的内容不包含标签
    2.innerHTML/textContent获取的内容不会去除两端的空格, innerText获取的内容会去除两端的空格
    */
    let oDiv = document.querySelector("div");
    console.log(oDiv.innerHTML);
    console.log(oDiv.innerText);
    console.log(oDiv.textContent);
    
    
5.2 设置元素内容
  • /*
       特点:
       无论通过innerHTML/innerText/textContent设置内容, 新的内容都会覆盖原有的内容
       区别:
       如果通过innerHTML设置数据, 数据中包含标签, 会转换成标签之后再添加
       如果通过innerText/textContent设置数据, 数据中包含标签, 不会转换成标签, 会当做一个字符串直接设置
    */
     let oDiv = document.querySelector("div");
    // oDiv.innerHTML = "123";
    // oDiv.innerText = "456";
    // oDiv.textContent = "789";
    //  oDiv.innerHTML = "<span>我是span</span>";
    //  oDiv.innerText = "<span>我是span</span>";
    //  oDiv.textContent = "<span>我是span</span>";
    
     setText(oDiv, "www.it666.com");
    //为了解决兼容性问题,企业开发中textContent和innerText是结合起来使用的
     function setText(obj, text) {
         if("textContent" in obj){   //如果有这个属性
             obj.textContent = text;
         }else{
             obj.innerText = text;
         }
     }
    
    

6.元素样式操作

6.1 设置元素样式
  • <style>
         .box{
             width: 200px;
             height: 200px;
             background-color: red;
         }
    </style>
    let oDiv = document.querySelector("div");
    // 第一种方式
    // 注意点: 由于class在JS中是一个关键字, 所以叫做className
    oDiv.className = "box";
    // 第二种方式
    // 注意点: 过去CSS中通过-连接的样式, 在JS中都是驼峰命名
    // 注意点: 通过JS添加的样式都是行内样式, 会覆盖掉同名的CSS样式
    oDiv.style.width = "300px";
    oDiv.style.height = "300px";
    oDiv.style.backgroundColor = "blue";
    
    
6.2 获取元素样式
  • let oDiv = document.querySelector("div");
    oDiv.style.width = "300px";
    // 注意点: 通过style属性只能过去到行内样式的属性值, 获取不到CSS设置的属性值
    console.log(oDiv.style.width);
    // 注意点: 如果想获取到CSS设置的属性值, 必须通过getComputedStyle方法来获取
    // getComputedStyle方法接收一个参数, 这个参数就是要获取的元素对象
    // getComputedStyle方法返回一个对象, 这个对象中就保存了CSS设置的样式和属性值
    let style = window.getComputedStyle(oDiv);
    console.log(style.width);
    
    

7. 点击事件

如何给元素绑定事件?

  • 在JavaScript中所有的HTML标签都可以添加事件,元素.事件名称 = function(){},当对应事件被触发时候就会自动执行function中的代码.

  • <button>我是按钮</button>
    <a href="http://www.it666.com">我是a标签</a>
    
    
    let oBtn = document.querySelector("button");
    oBtn.onclick = function () {
        alert("按钮被点击了");
    }
    // 注意点: 如果给元素添加了和系统同名的事件, 我们添加的事件不会覆盖系统添加的事件,只不过会先执行我们添加的,在执行系统的.想要覆盖就写一句return false
    let oA = document.querySelector("a");
    oA.onclick = function () {
        alert("a标签被点击了");
        // 以下代码的含义: 用我们添加的事件覆盖掉系统同名的事件
        return false;
    }
    
    

8. 定时器

8.1重复执行的定时器
  • setInterval

  • //有两个按钮
    <button id="start">开始</button>
    <button id="close">结束</button>
    //定时器会返回一个唯一的标识,我们用id来接收,方便关闭
    let startBtn = document.querySelector("#start");
    let id = null;
    startBtn.onclick = function () {
        id = setInterval(function () {
            console.log("随便写点");
        }, 1000);
    }
    let closeBtn = document.querySelector("#close");
    closeBtn.onclick = function () {
        clearInterval(id);
    }
    
    
8.2 只执行一次的
  • setTimeout

  • let startBtn = document.querySelector("#start");
    let closeBtn = document.querySelector("#close");
    let id = null;
    startBtn.onclick = function () {
        id = window.setTimeout(function () {
            console.log("随便写点");
        }, 5000);
    }
    closeBtn.onclick = function () {
        clearTimeout(id);
    }
    
    

9.做两个小游戏

9.1 消除鬼魂

9.2贪吃蛇

10.移入移出事件

  • // 1.移入事件
    oDiv.onmouseover = function () {
        console.log("移入事件");
    }
    // 注意点: 对于初学者来说, 为了避免未知的一些BUG, 建议使用onmouseenter
    oDiv.onmouseenter = function () {
        console.log("移入事件");
    }
     
    // 2.移出事件
    oDiv.onmouseout = function () {
        console.log("移出事件");
    }
    // 注意点: 对于初学者来说, 为了避免未知的一些BUG, 建议使用onmouseleave
    oDiv.onmouseleave = function () {
        console.log("移出事件");
    }
    // 3.移动事件
    oDiv.onmousemove = function () {
        console.log("移动事件");
    }
     
    
    
  • 移入移出表单 商品展示

11 表单事件(注意实时获取修改后的数据)

  • let oInput = document.querySelector("input");
       // 1.监听input获取焦点
        oInput.onfocus = function () {
            console.log("获取到了焦点");
        }
       // 2.监听input失去焦点
        oInput.onblur = function () {
            console.log("失去了焦点");
        }
       // 3.监听input内容改变
       // 注意点: onchange事件只有表单失去焦点的时候, 才能拿到修改之后的数据
       oInput.onchange = function () {
           console.log(this.value);
       }
       // oninput事件可以时时获取到用户修改之后的数据, 只要用户修改了数据就会调用(执行)
       // 注意点: oninput事件只有在IE9以及IE9以上的浏览器才能使用
       // 在IE9以下, 如果想时时的获取到用户修改之后的数据, 可以通过onpropertychange事件来实现
       // 通过代码修改value不会触发input事件
       oInput.oninput = function () {
           console.log(this.value);
      }
     
    
    
   
* 表单校验   以及输入框有内容才能点击按钮

   * 让当前提交按钮不能点击用disabled ,当不能点击时,disabled为true

#### 12 tab选项卡

* ```JavaScript
  <body>
  <div class="box">
      <ul class="top">
          <li class="current">新闻</li>
          <li>视频</li>
          <li>音乐</li>
          <li>军事</li>
          <li>财经</li>
      </ul>
      <div class="bottom">
          <div class="content">新闻的内容</div>
          <div class="content">视频的内容</div>
          <div class="content">音乐的内容</div>
          <div class="content">军事的内容</div>
          <div class="content">财经的内容</div>
      </div>
  </div>
  <script>
      // 1.拿到需要操作的元素
      let oItems = document.querySelectorAll("li");
      let oDivs = document.querySelectorAll(".content");
      // 定义变量保存上一次设置的索引
      let previousIndex = 0;
      // 2.给所有的选项卡添加点击事件
      for(let i = 0; i < oItems.length; i++){
          let item = oItems[i];
          // item.setAttribute("index", i);
          item.onclick = function() {
              // 清空上一次对应的元素对应的样式
              let preItem = oItems[previousIndex];
              preItem.className = "";
              let preDiv = oDivs[previousIndex];
              preDiv.style.display = "none";
  
              // 设置当前这一次的元素对应的样式
              // let index = this.getAttribute("index");
              this.className = "current";
              let oDiv = oDivs[i];
              oDiv.style.display = "block";
  
              // 保存当前这一次的索引
              previousIndex = i;
          };
      }
  </script>
  </body>
  

  • 在上面的代码中,为什么i直接就能用,为什么用let定义的for循环就行,var就不行,我们先引出闭包的概念

  • 12.1 闭包
    • /*
      1.什么是闭包(closure)?
      闭包是一种特殊的函数。
      
      2.如何生成一个闭包?
      当一个内部函数引用了外部函数的数据(变量/函数)时, 那么内部的函数就是闭包
      所以只要满足"是函数嵌套"、"内部函数引用外部函数数据"
      
      3.闭包特点:
      只要闭包还在使用外部函数的数据, 那么外部的数据就一直不会被释放
      也就是说可以延长外部函数数据的生命周期
      
      4.闭包注意点:
      当后续不需要使用闭包时候, 一定要手动将闭包设置为null, 否则会出现内存泄漏
      */
      function test() {
          var i = 666; // 局部变量
      } // 只要代码执行到了大括号结束, i这个变量就会自动释放
      console.log(i); // i is not defined
      //下面开启闭包
      function test() {
          var i = 666;
          // 由于demo函数满足闭包的两个条件, 所以demo函数就是闭包
          return function demo() {
              console.log(i);
          }
      }
      let fn = test();
      fn(); // 666
      
      
12.2 循环索引同步(var的情况)
  • /*
    默认情况下是顺序结构, 代码会从上之下的执行, 前面的没执行完后面的不能执行
    默认情况下通过var定义的变量, 只要不是定义在函数中都是全局变量
    */
    
    for(var i = 0; i < 3; i++){ // 0 1 2 3
        function test() {
            console.log(i); // 3
        }
    }
    // console.log(i); // 3
    test();
    //想要输出当前索引,有如下方式
    for(var i = 0; i < 3; i++){ // 0 1 2 3
        function test() {
            console.log(i); // 0 1 2
        }
        test();
    }
     
    for(var i = 0; i < 3; i++){ // 0 1 2 3
        (function test() {
            console.log(i); // 0 1 2
        })();
    }
     
    for(var i = 0; i < 3; i++){ // 0 1 2 3
        // function test(index) { // var index = i;
        //     console.log(index); // 0 1 2
        // }
        // test(i);  可以简写为如下
        (function test(index) {
            console.log(index); // 0 1 2
        })(i);
    }
     
    
    
  • 做一个练习,定义三个按钮,点击每一个输出对应的索引

    • //不能同步,因为等循环执行完才执行函数
      let oBtns = document.querySelectorAll("button");
      for(var i = 0; i < oBtns.length; i++){
          let btn = oBtns[i];
          btn.onclick = function () {
              console.log(i); // 3
          }
      }
      
      
    • //能同步
      let oBtns = document.querySelectorAll("button");
      for(var i = 0; i < oBtns.length; i++) {
          let btn = oBtns[i];
          (function test(index) { // var index = i;
              // console.log(index); // 0 1 2
            // 注意点: onclick对应的方法由于满足了闭包的条件, 所以onclick对应的方法也是一个闭包
             //所以index不会被立即释放,我们就能访问了
              btn.onclick = function () {
                  console.log(index);
              }
          })(i);
      }
      
      
    • 看一下上面那个函数执行完在内存里面的情况

    • 循环索引同步.png
12.2 循环索引同步(let的情况)
  • /*let list = [];
     // 这里的i是局部变量
     // 注意点: 由于i是局部变量, 所以每次执行完循环体都会重新定义一个i变量
     for(let i = 0; i < 3; i++){ // 0 1 2 3
         let fn = function test() {
             console.log(i); // 3
         }
         list.push(fn);
     }
     // console.log(i); // i is not defined
     // 以下会依次输出1,2,3
     list[0]();
     list[1]();
     list[2]();
     */
     for(let i = 0; i < 3; i++){ // 0 1 2 3
         // 注意点: 在ES6中由于{}是块级作用域, 所以只要在块级作用域中定义了一个函数
         //         并且这个函数中用到了块级作用域中的数据, 那么这个函数就是闭包
         function test() {
             console.log(i); 
         }
     }
     test(); // 2
    
    
    
  • 上面的代码内存中情况如下

  • 循环索引同步let.png

​ 这就是为什么通过let定义在for循环中后通过点击事件可以取得当前的i值,因为是闭包

12.3 排他思想
  • /*
    1.什么是排它思想?
    清除其它非选中元素的样式, 只设置当前选中元素样式
    */
    let oItems = document.querySelectorAll("li");
    let previousIndex = 0;
    
     // es6之后
    for(let i = 0; i < oItems.length; i++){
        let item = oItems[i];
        item.onclick = function () {
            // 排它
            // for(let j = 0; j < oItems.length; j++){
            //     let li = oItems[j];
            //     li.className = "";
            // }
            // 通过一个索引实现排他
            let preItem = oItems[previousIndex];
            preItem.className = "";
    
            this.className = "current";
            previousIndex = i;
        }
    }
    //es6之前
    for(var i = 0; i < oItems.length; i++) {
        var item = oItems[i];
        (function (index) {
            item.onclick = function () {
                // 排它
                var preItem = oItems[previousIndex];
                preItem.className = "";
    
                this.className = "current";
                previousIndex = index;
            }
        })(i);
    }
    
    

13 时钟对象

  • // 1.获取当前时间
       let date = new Date();
       console.log(date);
       // 2.获取当前时间距离1970年1月1日(世界标准时间)起的毫秒
        console.log(Date.now());
        let date = new Date();
        console.log(date.valueOf());
     
       // 3.创建指定时间
        let date1 = new Date("2019-11-11 09:08:07");
        console.log(date1);
       // 注意点: 在创建指定时间的时候, 如果月份是单独传入的, 那么会多一个月
       let date2 = new Date(2019, 10, 11, 9, 8, 7);
        console.log(date2);
     
       // 4.获取指定时间年月日时分秒
       let date = new Date();
       console.log(date);
       console.log(date.getFullYear());
       // 注意点; 通过getMonth方法获取到的月份会少一个月
        console.log(date.getMonth() + 1);
        console.log(date.getDate());
        console.log(date.getHours());
        console.log(date.getMinutes());
        console.log(date.getSeconds());
     
       // 5.时间格式化
       let date = new Date();
       let res = formartDate(date);
       console.log(res);
       // console.log(date);
       // 2019-4-19 18:17:06
       function formartDate(date) {
           return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}                        ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`;
       }
     
    
    
  • 获取时间差
  • // 1000毫秒 = 1秒   60秒 = 1分钟  60分钟 = 1小时  24小时 = 1天
    // 以下两个时间相差值 10天1小时29分40秒
    let curDate = new Date("2020-4-19 22:30:20");
    let remDate = new Date("2020-4-30 00:00:00");
    // 1.得到两个时间之间的差值(毫秒)
    let differTime = remDate - curDate;
    // let differTime = remDate.valueOf() - curDate.valueOf();
    // 2.得到两个时间之间的差值(秒)
    let differSecond = differTime / 1000;
    // 3.利用相差的总秒数 / 每一天的秒数 = 相差的天数
    let day = Math.floor(differSecond / (60 * 60 * 24));
    console.log(day);
    // 4.利用相差的总秒数 / 小时 % 24;
    let hour = Math.floor(differSecond / (60 * 60) % 24);
    console.log(hour);
    // 5.利用相差的总秒数 / 分钟 % 60;
    let minute = Math.floor(differSecond / 60 % 60);
    console.log(minute);
    // 6.利用相差的总秒数 % 秒数
    let second = differSecond % 60;
    console.log(second);
    
    
    
  • 秒杀效果 时钟效果

14 匀速动画和缓动动画

14.1 匀速动画
  • function linearAnimation(ele, target) {
        //关掉定时器,防止定时器的叠加
       clearInterval(timerId);
       timerId = setInterval(function () {
           // 1.拿到元素当前的位置
           let begin = parseInt(ele.style.marginLeft) || 0;
           // 2.定义变量记录步长
           //         0  -  500 = -500    13
           //         500 -  200 = 300    -13
           let step = (begin - target) > 0 ? -13 : 13;
           // 3.计算新的位置
           begin += step;
           console.log(Math.abs(target - begin), Math.abs(step));
           //当由小往大走时,不够一个步长说明就快到终点了,往小同理
           if(Math.abs(target - begin) <= Math.abs(step)){
               clearInterval(timerId);
               begin = target;
           }
     
           // 4.重新设置元素的位置
           ele.style.marginLeft = begin + "px";
       }, 100);
    }
    
    
14.2 缓动动画
  • function easeAnimation(ele, target) {
       clearInterval(timerId);
       timerId = setInterval(function () {
           // 1.拿到元素当前的位置
           let begin = parseInt(ele.style.marginLeft) || 0;
           // 2.定义变量记录步长
           // 公式: (结束位置 - 开始位置) * 缓动系数(0 ~1)
           let step = (target - begin) * 0.3;
           console.log(step);
           // 3.计算新的位置
           begin += step;
           //结束条件
           if(Math.abs(Math.floor(step)) <= 1){
               clearInterval(timerId);
               begin = target;
           }
           // 4.重新设置元素的位置
           ele.style.marginLeft = begin + "px";
       }, 100);
    }
    
    

    无限自动轮播

14.3 匀速动画的封装
  • 对前面代码的优化,可以实现多个动画一起执行

  • linearAnimation(oDiv, {"marginTop":500, "marginLeft": 300}, function () {
         alert("动画执行完毕之后执行的其它的操作");
     });
    
    function linearAnimation(ele, obj, fn) {
        clearInterval(timerId);
        timerId = setInterval(function () {
            // flag变量用于标记是否所有的属性都执行完了动画
            let flag = true;
            for(let key in obj){
                let attr = key;
                let target = obj[key];
    
                // 1.拿到元素当前的位置
                // let begin = parseInt(ele.style.width) || 0;
                let style = getComputedStyle(ele);
                // let begin = parseInt(style.width) || 0;
                let begin = parseInt(style[attr]) || 0;
                // 2.定义变量记录步长
                let step = (begin - target) > 0 ? -13 : 13;
                // 3.计算新的位置
                begin += step;
                if(Math.abs(target - begin) > Math.abs(step)){
                    flag = false;
                }else{
                    begin = target;
                }
                // 4.重新设置元素的位置
                // ele.style.width = begin + "px";
                ele.style[attr] = begin + "px";
            }
            if(flag){
                clearInterval(timerId);
                // if(fn){
                //     fn();
                // }
                fn && fn();
            }
        }, 100);
    
    

15 事件

15.1 添加事件的三种方式
  • /*
    方式一:
    1.通过onxxx的方式来添加
    注意点: 由于是给属性赋值, 所以后赋值的会覆盖先赋值
    */
    oBtn.onclick = function () {
        alert("666");
    }
    oBtn.onclick = function () {
        alert("777");
    }
    let obj = {};
    obj.say = function () {
        console.log("123");
    }
    obj.say = function () {
        console.log("456");
    }
    obj.say();
    
    
  • /*
     方式二:
     2.通过addEventListener方法添加
     注意点:
     1.事件名称不需要添加on
     2.后添加的不会覆盖先添加的  下面会输出两次
     3.只支持最新的浏览器IE9
     */
     oBtn.addEventListener("click", function () {
         alert("666");
     });
     oBtn.addEventListener("click", function () {
         alert("777");
     });
    
    
    
  • /*
     方式三
     3.通过attachEvent方法添加
     注意点:
     1.事件名称必须加上on
     2.后添加的不会覆盖先添加的
     3.只支持低版本的浏览器
     */
     oBtn.attachEvent("onclick", function () {
         alert("666");
     });
     oBtn.attachEvent("onclick", function () {
         alert("777");
     });
    
    
    
  • 以下为兼容写法

    • function addEvent(ele, name, fn) {
          if(ele.attachEvent){
              ele.attachEvent("on"+name, fn);
          }else{
              ele.addEventListener(name, fn);
          }
      }
      
      
15.2 事件对象以及阻止默认行为
  • /*
    1.什么是事件对象?
    事件对象就是一个系统自动创建的一个对象
    当注册的事件被触发的时候, 系统就会自动创建事件对象
    */
    /*
    2.事件对象的注意点:
    在高级版本的浏览器中, 会自动将事件对象传递给回到函数
    在低级版本的浏览器中, 不会自动将事件对象传递给回调函数
    在低级版本的浏览器中, 需要通过window.event来获取事件对象
     */
    let oA = document.querySelector("a");
     oA.onclick = function (event) {
         // 兼容性的写法
         event = event || window.event;
    
         alert("666");
         // 阻止默认行为
         return false; // 企业开发推荐
    
         // 注意点: preventDefault方法只支持高级版本的浏览器
         // event.preventDefault();
         // event.returnValue = false; // IE9以下的浏览器
     }
    
    
    
  • 事件的三个阶段
  • <body>
    <div class="father">
        <div class="son"></div>
    </div>
    <script>
        /*
        1.事件的三个阶段
        1.1.捕获阶段(从外向内的传递事件)
        1.2.当前目标阶段
        1.3.冒泡的阶段(从内向外的传递事件)
    
        2.注意点:
        三个阶段只有两个会被同时执行
        要么捕获和当前, 要么当前和冒泡
    
        3.为什么要么只能是捕获和当前, 要么只能是当前和冒泡?
        这是JS处理事件的历史问题
        早期各大浏览器厂商为占领市场, 以及对事件的理解不同
        后续W3C为了兼容, 将两种方式都纳入标准
        */
        /*
        1.如何设置事件到底是捕获还是冒泡?
        通过addEventListener方法, 这个方法接收三个参数
        第一个参数: 事件的名称
        第二个参数: 回调函数
        第三个参数: false冒泡  / true 捕获
    
        注意点:
        onXxx的属性, 不接收任何参数, 所以默认就是冒泡
        attachEvent方法, 只能接收两个参数, 所以默认就是冒泡
        */
        let oFDiv = document.querySelector(".father");
        let oSDiv = document.querySelector(".son");
        /*
        以下会先输出son,然后father
        oFDiv.addEventListener("click", function () {
            console.log("father");
        }, false);
        oSDiv.addEventListener("click", function () {
            console.log("son");
        }, false);
        */
        oFDiv.onclick = function () {
            console.log("father");
        }
        oSDiv.onclick = function () {
            console.log("son");
        }
        /*
        IE 6.0:
        div -> body -> html -> document
        其他浏览器:
        div -> body -> html -> document -> window
        注意:
        不是所有的事件都能冒泡,以下事件不冒泡:blur、focus、load、unload
        */
    </script>
    </body>
    
    
    
  • 下图可以很好地理解冒泡和捕获

-
捕获和冒泡.png
15.3 阻止事件冒泡
  • 由上面我们知道,当事件发生后,事件冒泡就是一个事件开始传播(从里到外或者从外向里)。为什么要传播呢?因为事件源本身(可能)并没有处理事件的能力,即处理事件的函数(方法)并未绑定在该事件源上。例如我们点击一个按钮时,就会产生一个click事件,但这个按钮本身可能不能处理这个事件,事件必须从这个按钮传播出去,从而到达能够处理这个事件的代码中(例如我们给按钮的onclick属性赋一个函数的名字,就是让这个函数去处理该按钮的click事件),或者按钮的父级绑定有事件函数,当该点击事件发生在按钮上,按钮本身并无处理事件函数,则传播到父级去处理。

  • 如何阻止

    • // 1.拿到需要操作的元素
      var oFDiv = document.getElementById("father");
      var oSDiv = document.getElementById("son");
      
      // 2.注册事件监听
      oFDiv.onclick = function () {
          console.log("father");
      }
      oSDiv.onclick = function (event) {
          event = event || window.event;
          // 注意点: stopPropagation方法只支持高级浏览器
          // event.stopPropagation();
          // event.cancelBubble = true; // 低级浏览器
          if(event.cancelBubble){
              event.cancelBubble = true;
          }else{
              event.stopPropagation();
          }
          console.log("son");
      }
      
      
15.4 移入移出事件区别
15.5 事件位置获取
  • /*
    offsetX/offsetY: 事件触发相对于当前元素自身的位置
    clientX/clientY: 事件触发相对于浏览器可视区域的位置
    注意点: 可视区域是不包括滚动出去的范围的
    pageX/pageY:     事件触发相对于整个网页的位置
    主要点: 整个网页包括滚动出去的范围的
    screenX/screenY: 事件触发相对于屏幕的位置(电脑屏幕)
    */
    
    

16 正则表达式

  • /*
     1.什么是正则表达式?
     正则表达式就是对字符串操作的一种逻辑公式
      
     2.正则表达式的作用?
     1.在字符串"查找"是否包含指定子串
     2.从字符串中"提取"指定子串
     3.对字符串中指定的内容进行"替换"
      */
     // 1.字符串查找
     /*
     let str = "123abc456";
     let index = str.indexOf("abc");
     let index = str.lastIndexOf("abc");
     let flag = str.includes("abc");
      */
     // 2.字符串提取
     /*
     let str = "123abc456";
     let startIndex = str.indexOf("a");
     console.log(str.substr(startIndex, "abc".length));
      */
     // 3.字符串替换
     /*
     let str = "123abc456";
     str.replace("abc", "it666");
      */
      
     // 1.利用正则表达式匹配(查找)
    
     let str = "123abc456";
     // 1.创建一个正则表达式对象
     // 2.指定匹配的规则
     // 注意点: 默认情况下在正则表达式中是区分大小写的,当给构造函数中传递一个i时,代表不区分大小写. 
     let reg = new RegExp("A", "i");
     let res = reg.test(str);
     console.log(res);
     
     
     let str = "abc2020-1-11def";
     // 通过构造函数创建正则表达式对象,注意转义字符
     // let reg = new RegExp("\\d{4}-\\d{1,2}-\\d{1,2}");
     // 通过字面量来创建正则表达式对象
     let reg = /\d{4}-\d{1,2}-\d{1,2}/;
     let res = reg.test(str);
     console.log(res);
      
      
     // 2.通过正则表达式提取符合规则的字符串
     
     let str = "abc2020-1-11def2019-11-11fdjsklf";
     // 注意点: 默认情况下在正则表达式中一旦匹配就会停止查找,后面写个g代表全局查找
     let reg = /\d{4}-\d{1,2}-\d{1,2}/g;
     let res = str.match(reg);
     console.log(res);
     console.log(res[0]);
     console.log(res[1]);
      
      
     // 3.通过正则表达式替换符合规则的字符串
     let str = "abc2020-1-11def2019-11-11fdjsklf";
     let reg = /\d{4}-\d{1,2}-\d{1,2}/g;
     let newStr = str.replace(reg, "it666");
     console.log(str);
     console.log(newStr);
    </script>
    /*
    常用正则表达式合集:
    验证数字:^[0-9]*$
    验证n位的数字:^\d{n}$
    验证至少n位数字:^\d{n,}$
    验证m-n位的数字:^\d{m,n}$
    验证零和非零开头的数字:^(0|[1-9][0-9]*)$
    验证有两位小数的正实数:^[0-9]+(.[0-9]{2})?$
    验证有1-3位小数的正实数:^[0-9]+(.[0-9]{1,3})?$
    验证非零的正整数:^\+?[1-9][0-9]*$
    验证非零的负整数:^\-[1-9][0-9]*$
    验证非负整数(正整数 + 0)  ^\d+$
    验证非正整数(负整数 + 0)  ^((-\d+)|(0+))$
    验证长度为3的字符:^.{3}$
    验证由26个英文字母组成的字符串:^[A-Za-z]+$
    验证由26个大写英文字母组成的字符串:^[A-Z]+$
    验证由26个小写英文字母组成的字符串:^[a-z]+$
    验证由数字和26个英文字母组成的字符串:^[A-Za-z0-9]+$
    验证由数字、26个英文字母或者下划线组成的字符串:^\w+$
    
    验证用户密码:^[a-zA-Z]\w{5,17}$ 正确格式为:以字母开头,长度在6-18之间,只能包含字符、数字和下划线。
    验证是否含有 ^%&',;=?$\" 等字符:[^%&',;=?$\x22]+
    验证汉字:^[\u4e00-\u9fa5],{0,}$
    验证Email地址:^\w+[-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$
    验证InternetURL:^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$ ;^[a-zA-z]+://(w+(-w+)*)(.(w+(-w+)*))*(?S*)?$
    验证电话号码:^(\d3,4|\d{3,4}-)?\d{7,8}$:--正确格式为:XXXX-XXXXXXX,XXXX-XXXXXXXX,XXX-XXXXXXX,XXX-XXXXXXXX,XXXXXXX,XXXXXXXX。
    验证身份证号(15位或18位数字):^\d{15}|\d{}18$
    验证一年的12个月:^(0?[1-9]|1[0-2])$ 正确格式为:“01”-“09”和“1”“12”
    验证一个月的31天:^((0?[1-9])|((1|2)[0-9])|30|31)$    正确格式为:01、09和1、31。
    整数:^-?\d+$
    非负浮点数(正浮点数 + 0):^\d+(\.\d+)?$
    正浮点数   ^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$
    非正浮点数(负浮点数 + 0) ^((-\d+(\.\d+)?)|(0+(\.0+)?))$
    负浮点数  ^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$
    浮点数  ^(-?\d+)(\.\d+)?$
    */
    
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,565评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,021评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,003评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,015评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,020评论 5 370
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,856评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,178评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,824评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,264评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,788评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,913评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,535评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,130评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,102评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,334评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,298评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,622评论 2 343

推荐阅读更多精彩内容