DOM

66 阅读11分钟

01-认识DOM

JavaScript是一门编程语言,它的运行环境有两个,一个是浏览器,一个是node,前面我们学的JS必于ECMAScript中的语法,浏览器的JS有三部分组成:

  • ECMAScript
  • DOM
  • BOM image.png

01-document对象

    <!-- document 文档  html文档 -->
<script>
    console.log(document); // document代表整个文档
    console.log("------------");
    console.log(document.documentElement); // 得到html元素
    console.log("------------");
    console.log(document.body); // body元素
    console.log("------------");
    console.log(document.head); // head元素
    console.log("------------");
    console.log(document.doctype); // 文档声明
</script>

02-节点介绍

    // 元素节点
    // 文本节点
    // 属性节点   // 在DOM树上没有属性节点
    // 注释节点
    // 换行节点
    // 节点就是对象,操作节点就是操作对象

03-节点关系

节点关系一

  • 获取到一个节点(Node)后,可以根据这个节点去获取其他的节点,我们称之为节点之间的关系

  • 父节点:parentNode

  • 前兄弟节点:previousSibling

  • 后兄弟节点:nextSibling

  • 子节点:childNodes

  • 第一个子节点:firstChild

  • 最后一个子节点:lastChild image.png

    <!-- 我是一个注释 -->
    我是一个文本
    <div class="box">
         我是一个孤独的DIV
    </div>
    <ul>
        <li>One</li>
        <li>Two</li>
        <li>Three</li>
    </ul>
    <script>
       // 获取body的元素节点
       let bodyEle = document.body;
       console.log(bodyEle.firstChild);  // 子  #text 换行节点
       console.log(bodyEle.firstChild.nextSibling); // 兄弟 注释
       console.log(bodyEle.parentNode); // 父 html
    </script>
    // 通过节点关系去获取某些节点,非常麻烦,因为需要考虑换行节点和注释节点
    

节点关系二

  • 获取到一个节点(Node)后,可以根据这个节点去获取其他的节点,我们称之为节点之间的关系

  • 父节点:parentElement

  • 前兄弟节点:previousElementSibling

  • 后兄弟节点:nextElementSibling

  • 子节点:children

  • 第一个子节点:firstElementChild

  • 最后一个子节点:lastElementChild

    <body>
       <!-- 我是一个注释 -->
       我是一个文本
       <div class="box">
           我是一个孤独的DIV
       </div>
       <ul>
           <li>One</li>
           <li>Two</li>
           <li>Three</li>
       </ul>
      <script>
           let bodyEle = document.body;
           console.log(bodyEle.firstElementChild);
           console.log(bodyEle.firstElementChild.nextElementSibling);
           // 得到一个伪数组
           console.log(bodyEle.firstElementChild.nextElementSibling.children);
      </script>
    </body>
    

02-获取元素节点的方法

image.png

    // ID是唯一的,getElementById获取的是唯一元素
    // 即使ID值一样,也只会得到第1个元素
    // console.log(document.getElementById("box1")); // 拿box1

    // 伪数组
    // let divs = document.getElementsByTagName("div"); // 通过标签名
    // console.log(divs);
    // 判断一个数据是否是真数组
    // console.log(Array.isArray(divs));  // false
    // console.log(Array.from(divs));  // 把伪数组转成真数组
    // console.log(Array.isArray(Array.from(divs))); // true
    // console.log(divs[0]);  // 第一个div
    // console.log(divs[1]);  // 第二个div

    // querySelectorAll里面放选择器    选择器和css选择器是一样的
    // let divs = document.querySelectorAll("#box1");  // 所有box1的选择器
    // console.log(divs);
    // console.log(Array.isArray(divs)); // false
    // console.log(Array.from(divs)); // 变成真数组

    let li = document.querySelector("li") // 第一个li
    console.log(li);

03-元素节点的属性

结构:下同

image.png

01-nodeType

  • commentNode.nodeType 8
  • textNode.nodeType 3
  • elementNode.nodeType 1

02-nodeName和tagName

  • nodeName:获取node节点的名字;
  • tagName:获取元素的标签名词;

tagName 和 nodeName 之间有什么不同呢?

  • tagName 属性仅适用于 Element 节点;
  • 对于元素来说,nodeName与 tagName 的意义相同,所以使用哪一个都是可以的;

03-innerHTML

    // 获取div标签中的内容
    console.log(divNode.innerHTML);  // <h2>...</h2>  <p>...</p>
    // 设置div标签中的内容
    divNode.innerHTML = "<strong>我是一个Strong标签</strong>"

04-outerHTML

    // 获取div标签中的内容  outerHTML相比innerHTML来说,带上自己本身div
    console.log(divNode.outerHTML); // <div> <h2>...</h2>  <p>...</p> </div>

    // 对于设置来说,使用innerHTML多一点
    divNode.outerHTML = "<strong>我是一个Strong标签</strong>"

05-textContent

    // 只能获取文本节点
    console.log(divNode.textContent); // 我是一个H2  我是内容

    //  设置也是针对文本节点,如果写了标签,也不会解析
    divNode.textContent = "<strong>我是一个Strong标签</strong>";

06-nodeValue

  • 用于获取非元素节点的文本内容

    // 获取注释节点中的内容
    console.log(commentNode.nodeValue); // 我是一个注释
    // 获取文本节点中的内容
    console.log(textNode.nodeValue); // 我是文本
    // 元素节点的nodeValue是null
    console.log(divNode.nodeValue);  // null
    

07-hidden

  • 用于设置元素隐藏

    <button id="btn">Toggle</button>  // Toggle切换
    
    <div class="box" style="color: red;">
            Hello DOM~
    </div>
    
    <script>
    let btn = document.getElementById("btn");
    let div = document.getElementsByTagName("div")[0];
    
    // console.log("start...");
    
    // 给btn绑定点击事件  btn叫事件源    click叫点击事件(事件类型)
    // function(){} 事件处理程序,也叫监听器
    // 事件绑定是异步任务,是宏任务
    btn.onclick = function(){
        // console.log("click...");
        // 隐藏div
        // div.style.display = "none";  // 隐藏方式一
        // div.hidden = true; // 隐藏方式二
    
        // if(div.hidden === false){
        //     div.hidden = true;
        // }else{
        //     div.hidden = false;
        // }
    
        // 经典
        div.hidden = !div.hidden
    }
    // console.log("end...");
    </script>
    

04-创建和挂载节点

  • 先创建后挂载 创建节点:
  • createElement 创建元素节点
  • createTextNode 创建文本节点
  • createComment 创建注释节点
  • createAttribute 创建属性节点

挂载节点:

  • node.append(...nodes or strings) —— 在 node 末尾 插入节点或字符串,

  • node.prepend(...nodes or strings) —— 在 node 开头 插入节点或字符串,

  • node.before(...nodes or strings) —— 在 node 前面 插入节点或字符串,

  • node.after(...nodes or strings) —— 在 node 后面 插入节点或字符串,

  • node.replaceWith(...nodes or strings) —— 将 node 替换为给定的节点或字符串。 image.png

    <div class="father">
       <div class="son">
            SON
       </div>
    </div>
    
    <script>
      // 创建对象
      let father = document.querySelector(".father");
    
      // 使用innerHTML也可以添加元素,不推荐
      // father.innerHTML = "<h2>我是一个H2标签</h2>";
    
      let h2Ele = document.createElement("h2"); // 添加元素
      h2Ele.textContent = "我是H2标签"; // 给H2添加文本节点
      console.log(h2Ele);  // 页面中没有h2
    
      // 需要把h2挂载到DOM树上
      father.append(h2Ele);
      // father.prepend(h2Ele)
      // father.before(h2Ele)
      // father.after(h2Ele)
      // father.replaceWith(h2Ele)
    
      let spanEle = document.createElement("span");
      spanEle.textContent = "我是一个孤独的span";
    
      h2Ele.append(spanEle)
    </script>      
    

05-删除、替换、克隆节点

结构:

<div class="father">
    <div class="son">
        son
    </div>
</div>

01-删除节点

  • removeChild 只有父元素才有资格删除一个子元素

  • remove 移除元素我们可以调用元素本身的remove方法:

    // 想要做什么事,先把节点拿到
    let father = document.querySelector(".father");
    let son = document.querySelector(".son");
    
    // son.remove, 自己移除自己
    // son.remove();
    
    // 父也有能力去移除它里面的元素
    father.removeChild(son)
    

02-替换节点

  • replaceChild

    let father = document.querySelector(".father");
    let son = document.querySelector(".son");
    
    let pEle = document.createElement("p");
    pEle.textContent = "GrandSon";
    
    father.replaceChild(pEle,son) // 用pEle去替换son
    

03-克隆节点

  • 可以传入一个Boolean类型的值,来决定是否是深度克隆;

  • 深度克隆会克隆对应元素的子元素,否则不会;

    let father = document.querySelector(".father");
    let son = document.querySelector(".son");
    
    // 默认是浅copy  只copy一个节点,内部的其它节点不会copy
    // let newFather = father.cloneNode();
    // console.log(newFather);
    
    // 如果传递一个true表示深copy   如果不传或传一个false表示浅copy
    let newFather = father.cloneNode(true);
    console.log(newFather);
    

06-属性节点操作

01-属性节点操作之atrribute

attribute的分类(标准、非标准)

  • 标准的attribute:某些attribute属性是标准的,比如id、class、href、type、value等;
  • 非标准的attribute:某些attribute属性是自定义的,比如abc、age、height等;
  • attrbute是HTML标签中的属性

对于所有的attribute访问都支持如下的方法:

  • elem.hasAttribute(name) — 检查特性是否存在。

  • elem.getAttribute(name) — 获取这个特性值。

  • elem.setAttribute(name, value) — 设置这个特性值。

  • elem.removeAttribute(name) — 移除这个特性。

  • attributes:attr对象的集合,具有name、value属性;

    <div class="father" title="hehe" score="88" id="box">
        <div class="son" abc="abc" age="18">
              son
       </div>
    </div>
    
    <script>
      // 先拿到节点
      let boxEle = document.querySelector("#box"); // 所有的盒子
      // 可以判断标准的、也可以判断非标准的
      // console.log(boxEle.hasAttribute("class")); // true
      // console.log(boxEle.hasAttribute("title"));
      // console.log(boxEle.hasAttribute("name")); // 标准
      // console.log(boxEle.hasAttribute("score")); // 非标准
    
      // 通过Attribute的属性名,获取属性值
      // console.log(boxEle.getAttribute("class"));
    
      // 设置Attribute
      // boxEle.setAttribute("adress","bj")
    
      // 删除Attribute
      // boxEle.removeAttribute("title")
    
      // 得到所有的Attribute
      console.log(boxEle.attributes);
    </script>
    

02-属性节点操作之property## (打点拿属性)

  • property是对象obj里的属性(name,age)

       let obj = {
             name:"wc",  
             age:18
         }
    

在大多数情况下,它们是相互作用的

  • 改变property,通过attribute获取的值,会随着改变;

  • 通过attribute操作修改,property的值会随着改变;

  • 建议使用property 默认情况下有类型

    <div class="father" title="hehe" score="88" id="box">
         <div class="son" abc="abc" age="18">
             son
         </div>
    </div>
    
    <input type="text" class="ipt" value="我是默认值">
    
    <hr>
    
    <!-- checked="checked" -->
    <input type="checkbox" class="ipt2">
    
    <script>
      let boxEle = document.querySelector("#box");
      let iptEle = document.querySelector(".ipt")
      let iptEle2 = document.querySelector(".ipt2")
    
      // obj是对象  对象是属性(property)的无序集合
      // 所谓的property就是对象的中的属性
      // let obj = {
      //     name:"wc",  
      //     age:18
      // }
    
      // boxEle.setAttribute("title","xixi"); // 改变了attribute
      // console.log(boxEle.title);  // property
    
      // boxEle.title = "heihei";  // 改变了propety
      // console.log(boxEle.getAttribute("title"));
    
      // iptEle.setAttribute("value","lala")   
      // iptEle.value = "hehe"
    
      // 建议使用property
      console.log(iptEle2.checked);  // 默认情况下有类型
      console.log(iptEle2.getAttribute("checked")); // null
    </script>      
    
  • 通过property设置数据时,优先级是高于attribute

    <div class="father" id="abc" title="hehe" age="18" score="88">
         我是一个DVI
    </div>
    <!-- <input type="checkbox" checked="checked"> -->
    <input type="checkbox" checked>
    
    <hr>
    <input type="text" class="account">
    <button class="btn">设置input的值</button>
    
    <script>
      // 拿div
      let divEle = document.getElementById("abc");
      console.log(divEle.getAttribute("id"));
      console.log(divEle.getAttribute("age"));
      console.log("-----------------");
      console.log(divEle.id);  // property
      // 对于非标准的attribute 是没有 对应的property
      console.log(divEle.age);  // undefined
    
      console.log("-------------------");
    
      let iptEle = document.getElementsByTagName("input")[0]; // 拿到input框 
      // if(iptEle.getAttribute("checked")){  // attrbute
      //     console.log("处于选中状态");
      // }
      if(iptEle.checked){   // property
          console.log("处于选中状态");
      }
      console.log(typeof iptEle.checked);  // 类型boolean
    
      console.log("-------------------");
    
      let iptEle2 = document.getElementsByTagName("input")[1];  // 通过标签名拿输入框
      let btnEle = document.getElementsByClassName("btn")[0];  // 通过class拿<button>
    
      btnEle.onclick = function(){
          // 通过property设置数据时,优先级是高于attribute
          iptEle2.value = "66666"; // 通过property来设置
    
          iptEle2.setAttribute("value",88888); // 通过attribute来设置
      }
    </script>
    

04-H5中的自定义属性

  • 以data-打头的,叫html5中的自定义属性

    <div class="father" age="18" data-score="88" data-address="bj">
           我是一个孤独的DIV
    </div>
    
    <script>
      // 拿属性
      let boxEle = document.querySelector(".father");
      console.log(boxEle.dataset); // 收集所有的自定义属性   data开头的
      console.log(boxEle.dataset.score);
      console.log(boxEle.dataset.address);
    </script>
    

07-动态操作样式

01-操作class和style

元素的class attribute,对应的property并非叫class,而是className

     <style>
         .active{
            color: red;
            font-size: 30px;
            background-color: gold;
         }
    </style>
</head>
<body>
    <div class="box">
       我是一个box
    </div>
    <script>
       let boxEle = document.querySelector(".box");

      boxEle.onclick = function(){
          // 1)操作行内样式 style
          // boxEle.style.color = "red";
          // boxEle.style.fontSize = "30px";
          // boxEle.style.backgroundColor = "gold";

          // 2)操作class类 
          // class的attribute 对应的 property是 className
          boxEle.className = "active"
      }
  </script>

02-操作class

     <style>
        .active{
             color: red;
             font-size: 30px;
             background-color: gold;
        }
    </style>
</head>
<body>
   <div class="box">
      我是box
   </div>
   <button>切换</button>

   <script>
      let boxEle = document.querySelector(".box");
      // 把人家本身的覆盖掉了
      // boxEle.className = "active"

      // boxEle.classList.add("active")  // 添加一个类
      // boxEle.classList.remove("box")  // 添加/移除类

      // 点击
      let btn = document.getElementsByTagName("button")[0];
      btn.onclick = function(){
          // boxEle.classList.add("active")

          // boxEle.classList.toggle("active")  // 没有就添加类,有就移除类

          console.log(boxEle.classList.contains("box"));  // 判断有没有包含某个类  返回true和false
      }
   </script>

03-操作stlye

  • 对于内联样式,是可以通过style.*的方式读取到的

  • 对于style、css文件中的样式,是读取不到的

  • 通过getComputedStyle的全局函数来实现

    <div class="box" style="background-color: gold; color: white;">
        我是一个孤独的DIV
    </div>
    <script>
       let boxEle = document.querySelector(".box");
       // 获取
       console.log(boxEle.style.backgroundColor);
       console.log(boxEle.style.color);
    
       // 设置
       // boxEle.style.fontSize = "30px"
       // boxEle.style.color = "red"
    
       // 使用cssText 了解
       boxEle.style.cssText = "background-color: pink; font-size:30px; color:red;";
     </script>
    

08-深入事件

事件 + 事件的反馈 = 前端交互(前端的核心)

01-鼠标事件

  • onclick 点击事件 = onmousedown + onmouseup
  • ondbclick 双击事件
  • onmousedown 鼠标按下去
  • onmouseup 鼠标抬起来
  • onmouseover 鼠标移到某元素上面
  • onmouseout 鼠标离开某元素上面
  • onmousemove 鼠标在元素上面的移动

02-键盘事件

  • onkeydown 键盘按下去
  • onkeyup 键盘抬起来
  • onkeypress 键盘抬住

keydown与keypress区别:

  • keydown按任意键都触发;keypress按字符键(1234..)才触发,像alt、ctrl、shift等不会触发

03-加载事件

  • load 加载 浏览器不仅加载完成了 HTML,还加载完成了所有外部资源,像图片、css。 事件源是window

  • unload 加载 退出页面 此事件不好捕获到 也没有什么用

  • DOMContentLoaded:浏览器已完全加载 HTML,并构建了 DOM 树,但像 img 和样式表之类的外部资源可能尚未加载 完成。

    <script>
      // 当网页中所有的资源全部加载完毕,会触发load事件
      // 事件源是window
      // window.onload = function () {
      //     let btn = document.getElementById("btn");
      //     let img = document.getElementsByTagName("img")[0];
      //     console.log(btn);
      //     console.log(img);  // 可以加载所有资源,图片、css
      // }
    
      // DOMContentLoaded 当HTMLDOM元素加载完毕就触发
      // 不会等到页面中其它的资源加载完毕
      window.addEventListener("DOMContentLoaded",function(){
          let btn = document.getElementById("btn");
          console.log(btn);
      })
    </script>
    <body>
      <button id="btn">登录</button>
      <img src="https://img1.baidu.com/it/u=592570905,1313515675&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500" alt="">
    </body>
    

04-表单事件

  • focus: 获取焦点
  • blur: 失去焦点
  • change: 改变输入框中的内容,并失去焦点时触发
  • input:内部发生变化,立即触发,不等到失去焦点
  • submit: 当点击了提交按钮,会触发submit事件
  • reset: 当点击了重置按钮,会触发reset事件

05-其他事件

  • resize: 改变了浏览器窗口大小时,会触发resize事件
  • scroll: 页面滚动时,会触发scroll事件

06-HTML级别的事件绑定

可以在HTML开始标签上,去绑定事件,这种绑定基本不用

<!-- button叫事件源   click叫事件类型 -->
<!-- ()必须加,不加不行 -->
<button onclick="fn()">点我</button>
<script>
    function fn(){
        console.log("fn...");
    }
</script>

07-DOM0的事件绑定

DOM0事件绑定,就是给DOM元素的onxxxx属性赋值,是基于属性赋值的操作

     <style>
       .loading{
           background-color: #ccc;
           color: #424242;
           outline: 0px;
           border: none;
       }
   </style>
</head>
<body>
  <button id="btn">加载更多</button>
  <script>
      let btn = document.getElementById("btn");
      // onclick 本质就是属性 property
      // DOM0的事件绑定,是基于属性赋值的,不能绑定多个监听器
      // 绑定多个会打印出后边,因为后边的会覆盖前面
      // btn.onclick = function(){
      //     console.log("onclick1...");
      // }
      btn.onclick = function(){
          // console.log("onclick2...");
          this.textContent = "加载中...";
          // this.className = "loading";
          this.classList.add("loading"); // 加载load类

          let that = this; // that表示btn
          setTimeout(function(){
              console.log(this); // this表示window
              that.textContent = "加载更多"
              that.classList.remove("loading")
          },2000)
      }
  </script>
  
  
  <script>
    let btn = document.getElementById("btn");
    btn.onclick = function(){
        this.textContent = "加载中...";
        this.classList.add("loading");

        // 如果是箭头函数
        setTimeout(()=>{
            // 如果是箭头函数,this就正常了
            this.textContent = "加载更多"
            this.classList.remove("loading")
        },2000)
    }
</script>

08-DOM0的事件和HTML级别事件绑定的优先级

DOM0的事件绑定 优先级 高于HTML级别的事件绑定

      <style>
        .loading{
           background-color: #ccc;
           color: #424242;
           outline: 0px;
           border: none;
       }
    </style>
</head>
<body>
   <button id="btn" onclick="test()">加载更多</button>
   <script>
      function test(){
          console.log("HTML级别的事件绑定");
      }

      let btn = document.getElementById("btn");
      // DOM0的事件绑定 优先级 高于HTML级别的事件绑定
      btn.onclick = function(){
          console.log("DOM0的事件绑定");
      }
  </script>

09-DOM2的事件绑定

  • DOM2的事件绑定 是基于事件池的 可以绑定多个监听器

  • 两次点击事件,绑定同一个监听器。当点击时,监听器只出触发一次

         <style>
            .loading{
               background-color: #ccc;
              color: #424242;
              outline: 0px;
              border: none;
            }
        </style>
    </head>
    <body>
       <button id="btn">加载更多</button>
      
      <script>
         let btn = document.getElementById("btn");
         // DOM2的事件绑定   是基于事件池的   可以绑定多个监听器
         btn.addEventListener("click",function(){ // Event事件   Listener监听器
              // console.log("click1...");
             this.innerHTML = "加载中..."
             this.className = "loading";
             // 两秒之后加载更多   箭头函数
             setTimeout(()=>{
                 this.innerHTML = "加载更多"
                 this.className = "";
             },2000)
         })
         // btn.addEventListener("click",function(){
         //     console.log("click2...");
         // })
    </script>
    
    <script>
        let btn = document.getElementById("btn");
        // 能不能两次点击事件,绑定同一个监听器?
        // 答:可以的
        function fn(){
            console.log("我是一个监听器...");
        }
        // 表示两次点击事件,绑定同一个监听器
        // 当点击时,监听器只出触发一次
        // 一般情况下只会绑定一个
        btn.addEventListener("click",fn)
        btn.addEventListener("click",fn)
    </script>
    

10-DOM2的事件解绑

      <style>
         button{
           outline: none;
           border: none;
           width: 100px;
           height: 30px;
           background-color: red;
           color: #fff;
           text-align: center;
           line-height: 30px;
       }
       .got{
          background-color: #ddd;
       }
    </style>
</head>
<body>
   <button id="btn">领取优惠劵</button>
   <script>
       let btn = document.getElementById("btn");
       btn.onclick = function(){ // DOM0级别的事件绑定
           this.innerHTML = "已领取";
           this.className = "got" // 加上got类
           console.log("已领取");

          //  解绑
          this.onclick = null;  // 点一下,再点就点不动了
       }
  </script>

11-DOM的事件解绑

     <style>
       button {
          outline: none;
          border: none;
          width: 100px;
          height: 30px;
          background-color: red;
          color: #fff;
          text-align: center;
          line-height: 30px;
      }

      .got {
          background-color: #ddd;
      }
   </style>
</head>
<body>
   <button id="btn">领取优惠劵</button>
   <script>
      let btn = document.getElementById("btn");
      function fn(){
          this.innerHTML = "已领取";
          this.className = "got"
          console.log("已领取");

          // 监听器执行完毕后才可以解绑
          btn.removeEventListener("click",fn);
      }
      btn.addEventListener("click", fn)
</script> 
  • 第二种写法:

      <button id="btn">领取优惠劵</button>
      <script>
         let btn = document.getElementById("btn");
         btn.addEventListener("click", function fn() {
            this.innerHTML = "已领取";
            this.className = "got"
            console.log("已领取");
          
            // 监听器执行完毕后才可以解绑
            btn.removeEventListener("click", fn);
        })
    </script> 
    

12-事件传播

  • 1)从里向外传播 冒泡传播 需要掌握 默认的
  • 2)从外向里传播 捕获传播 基本上用不到

DOM0的事件绑定,只能冒泡,不能捕获

       <style>
          .wraper{
            width: 300px;
            height: 300px;
            background-color: green;
         }
         .outer{
            width: 200px;
            height: 200px;
            background-color: gold;
         }
        .inner{
            width: 100px;
            height: 100px;
            background-color: orchid;
         }
     </style>
  </head>
  <body>
     <div class="wraper">
        <div class="outer">
           <div class="inner"></div>
        </div>
    </div>
    <script>
       let wraper = document.getElementsByClassName("wraper")[0]; // getElements要加0
       let outer = document.getElementsByClassName("outer")[0];
       let inner = document.getElementsByClassName("inner")[0];

       // 点击inner,事件会传播,传播方向?
       //   1)从里向外传播   冒泡传播   需要掌握        默认的
       //   2)从外向里传播   捕获传播   基本上用不到

       // DOM0的事件绑定,只能冒泡,不能捕获
       wraper.onclick = function(){
          console.log("wraper...");
       }
       outer.onclick = function(){
          console.log("outer...");
       }
      inner.onclick = function(){
          console.log("inner...");
       }
  </script>
  • 使用DOM2的事件绑定 默认也是冒泡

  • addEventListener可以接收第三个参数

  • 如果第三个参数不写或为false表示冒泡

  • 如果第三个参数为true表示捕获

      // 结构同上
      使用DOM2的事件绑定  默认也是冒泡
      wraper.addEventListener("click",function(){
          console.log("wraper...");
      })
      outer.addEventListener("click",function(){
          console.log("outer...");
      })
      inner.addEventListener("click",function(){
          console.log("inner...");
      })
      
      addEventListener可以接收第三个参数
      如果第三个参数不写或为false表示冒泡
      如果第三个参数为true表示捕获
      wraper.addEventListener("click",function(){
          console.log("wraper...");
      },true)
      outer.addEventListener("click",function(){
          console.log("outer...");
      },true)
      inner.addEventListener("click",function(){
          console.log("inner...");
      },true)
    
  • 如果有捕获 也有 冒泡,先进行捕获,后进行冒泡

      wraper.addEventListener("click",function(){
          console.log("捕获 wraper...");
      },true)
      outer.addEventListener("click",function(){
          console.log("捕获 outer...");
      },true)
      inner.addEventListener("click",function(){
          console.log("目标 inner...");
      },true)
    
      wraper.addEventListener("click",function(){
          console.log("冒泡 wraper...");
      },false)
      outer.addEventListener("click",function(){
          console.log("冒泡 outer...");
      },false)
      inner.addEventListener("click",function(){
          console.log("目标 inner...");
      },false)
    

13-阻止事件冒泡

  • 通过事件对象来阻止冒泡

  • event对象中,有很多事件发生时的信息。浏览器帮你创建的,如:事件类型 鼠标点击的位置

    // 样式同 12-事件传播
    <script>
      let wraper = document.getElementsByClassName("wraper")[0];
      let outer = document.getElementsByClassName("outer")[0];
      let inner = document.getElementsByClassName("inner")[0];
    
    
      DOM2阻止事件冒泡
      wraper.addEventListener("click",function(event){ // 浏览器调的函数
            console.log("wraper...");
            console.log(event);
      })
      outer.addEventListener("click",function(){
            console.log("outer...");
      })
      inner.addEventListener("click",function(){
            console.log("inner...");
            // 阻止事件冒泡
            event.stopPropagation();
      })
    
    
      DOM0阻止事件冒泡
      wraper.onclick = function(event){
          console.log("wraper...");
      }
      outer.onclick = function(event){
          console.log("outer...");
      }
      inner.onclick = function(event){
          console.log("inner...");
          event.stopPropagation(); // stopPropagation阻止冒泡,写inner里  因为冒泡是从里往外
      }
    </script>
    

14-阻止默认事件

  • 鼠标右键默认事件
  • a标签默认事件
  • form表单默认事件

利用preventDefault或return false去阻止

  • DOM2的事件绑定,只能使用e.preventDefault();来阻止默认事件

    <!-- a标签有默认事件 -->
    <!-- 如果给a标签绑定了事件,绑定的事件也会触发 -->
    <a href="http://www.abc.com">我是一个孤独的A标签</a>
    
    <!-- form表单也有默认的提交事件  submit事件 -->
    <!-- action表示把表单提交到什么地方 -->
    <form action="http://www.abc.com">
        <input type="text">
        <br>
        <input type="submit">
    </form>
    
  • 默认的鼠标右键弹出菜单事件

    <script>
      // 事件源document  整个文档
      document.oncontextmenu = function(e){ // e就表示event
          console.log("点击鼠标右键~");
    
          // prevent阻止的意思   Default是默认的意思
          // e.preventDefault();
    
          // return false也可以阻止默认事件
          return false;
      }
    
  • a标签的默认事件

  • 如果给a标签绑定了事件,绑定的事件也会触发

      DOM0的事件绑定
      let a = document.getElementsByTagName("a")[0];
      // a.onclick = function(e){
      //     console.log("点击了a标签");
      //     // e.preventDefault();
      //     return false;  // 也可以阻止
      // }
    
      DOM2的事件绑定
      a.addEventListener("click",function(e){
          console.log("点击了a标签");
          // e.preventDefault();
          // 如果是DOM2的事件绑定,只能使用e.preventDefault();来阻止默认事件
          // return false;
      })
    
  • form表单默认事件

      let form = document.getElementsByTagName("form")[0];
      form.onsubmit = function(e){
          console.log("form submit ...");
          // e.preventDefault();
          return false;
      }
    </script>
    

15-事件委托

  • e.target.innerHTML

    <button id="btn">添加li</button>
    <ul>
       <li>1</li>
       <li>2</li>
       <li>3</li>
    </ul>
    <script>
       let btn = document.getElementById("btn");
       let ul = document.getElementsByTagName("ul")[0];
       let lis = document.getElementsByTagName("li");
      
       btn.onclick = function(){
          let li = document.createElement("li");
          li.innerHTML = lis.length + 1;  // 4567..
          ul.append(li) // 把li放在ul里的后面
       }
       
       // 这个for循环,只能给已经存在的li绑定点击事件
       // 新添加的li是不能绑定点击事件的
       for(let i=0; i<lis.length; i++){
           lis[i].onclick = function(){
              console.log(this.innerHTML); // 点li打印出li里的内容123
           }
       }
    </script>
    
    <script>
       let btn = document.getElementById("btn");
        let ul = document.getElementsByTagName("ul")[0];
      let lis = document.getElementsByTagName("li");
      
       // 给ul点击绑定事件
       // 点li也能触发ul的冒泡原理
       ul.onclick = function(e){
          // 如何知道你点击了哪一个li
          // e.target 表示你点击的目标
          // console.log(e);
          console.log(e.target.innerHTML);
       }
    
       btn.onclick = function(){
          let li = document.createElement("li");
          li.innerHTML = lis.length + 1;
          ul.append(li)
       }
    </script>
    

09-JS操作盒子模型

image.png js操作盒子模型:

  • 操作盒子模型的位置
  • 操作盒子模型的大小

不管是操作盒子模型的位置,还是盒子模型的大小,一共有13个api: 说明:需要记的没几个,三类:

  • client系列 width height top left
  • offset系列 width height top left parent
  • scroll系列 width height top left

01-clint家族

client系列:

  • clientWidth(可视区域宽度): 获取盒子的内容width+左右的padding
  • clientHeight(可视区域高度): 获取盒子的内容height+上下的padding
  • clientTop:获取盒子的上边框的粗细,了解
  • clientLeft:获取盒子的左边框的粗细,了解

获取一屏

  <script>
    // 获取一屏的宽度
    console.log(document.body.clientWidth);
    console.log(document.documentElement.clientWidth); // documentElement html元素

    // 获取一屏的高度
    console.log(document.body.clientHeight);
    console.log(document.documentElement.clientHeight);
</script>

可以得到多屏的高度

   console.log(document.body.clientHeight);

client系列的注意点:

  • 不能获取,不能设置,它们是只读的

  • 得到的是一个数值,没有单位

  • 得到的是一个整数,不是小数,及时测试量出来是小数,也会转化成整数

  • 不管你的内容是否溢出,得到的是可视区的大小

    <script>
      // clientHeight 不管你的内容是否溢出
      // 它一直是 内容的height + 上下的padding
      let div = document.getElementsByClassName("box")[0];
      console.log(div.clientWidth); // 内容加左右padding
      console.log(div.clientHeight);
    
      // 设置无用   只能获取,不能设置
      div.clientHeight = 666;
      // 获取
      console.log(div.clientHeight);
    </script>
    

02-offset家族

offset系列:

  • offsetWidth: box.offsetWidth 在clientWidth的基础上加了border 不常用

  • offsetHeight: box.offsetHeight 在clientHeight的基础上加了border 不常用

  • offsetTop: 获取一个绝对定位元素相对于参考点的上面的距离

  • offsetLeft: 获取一个绝对定位元素相对于参考点的左面的距离

  • offsetParent: 获取一个定位元素的参考点

    <script>
       let sonEle = document.getElementsByClassName("son")[0];
       // offsetTop和offsetLeft获取绝对定位元素相对于参考点的偏移量
       console.log(sonEle.offsetTop); // top: 50px
       console.log(sonEle.offsetLeft); // left: 60px
    
       // offsetParent 获取参考点元素
       console.log(sonEle.offsetParent); // father是参考点
    </script>
    

03-scroll家族

scrollWidth:

  • 在没有内容溢出(水平方向上的溢出)的情况下:scrollWidth == clientWidth
  • 在内容溢出的情况下:scrollWidth的值约等于真实内容的宽度
  • overflow属性会影响scrollWidth。 只能获取值,不能设置值

scrollHeight:

  • 在没有内容溢出的情况下:scrollHeight = clientHeight
  • 在内容溢出的情况下:scrollHeight的值约等于真实内容的高度,不同浏览器中得到的值可能不一样
  • overflow属性会影响scrollHeight。
  • 只能获取值,不能设置值

scrollTop:

  • 获取垂直滚动条滚动的距离(获取垂直滚动条卷去的高度)

  • 特殊:不只可以获取,也可以设置(可读可写) 重要

    <script>
      let box = document.getElementsByClassName("box")[0];
      // 绑定scroll事件
      box.onscroll = function(){
          // console.log("scroll"); // 滚动的时候触发scroll
          console.log("scrollTop(内容卷上去的高度):", box.scrollTop);
      }
    
      // 设置
      box.scrollTop = 600
     </script> 
    
  • 一张网页卷上去最大高度:scrollHeight - clientHeight

  • 获取当前页面卷上去高度

  • 1)document.body.scrollTop 2)document.documentElement.scrollTop

       <script>
      window.onscroll = function(){
          // 获取当前网页卷上去的高度
          console.log(document.documentElement.scrollTop);
          // 一张网页卷上去的最大高度
          console.log(document.documentElement.scrollHeight - document.documentElement.clientHeight);  // 整个内容的高度 - 一屏的高度
      }
       </script>
    

scrollLeft: 没什么用

  • 特殊:不只可以获取,也可以设置(可读可写)
  • 获取水平滚动条滚动的距离(获取水平滚动条卷去的距离) 基本上用不到

需要记的三个常用高度:

  • 获取一张网页可视区的高度:document.body.clientHeight
  • 获取一张网页真实的高度:document.body.scrollHeight
  • 获取一张网页卷去的高度:document.body.scrollTop

10-DOM操作案例练习

01过渡动画

  • transition
  • linear匀速

image.png

    <style>
        /* 默认样式 */
        .box{
            width: 200px;
            height: 200px;
            background-color: gold;

             /* 在默认样式上添加过渡动画 */
             /* 从200到400需要1s  linear匀速  */
             /* transition: width 1s linear, height 1s linear; */
        
             /* all表示只要有突变属性(针对数值),都有过渡动画 */
             /* 颜色和背景图片也有过渡动画 */
             transition: all 1s linear;
         }

         /* 突变样式 */
         .box:hover{
            width: 400px;
             height: 400px;
             background-color: skyblue;
         }
      </style>
  </head>
  <body>
     <div class="box"></div>