小白学前端07--dom

153 阅读15分钟

「这是我参与2022首次更文挑战的第7天,活动详情查看:2022首次更文挑战」。

前言

  • 本篇文章将代大家了解dom
  • 使用dom可以让我们方便的操作界面, 和获取页面元素, 是原生的js
  • dom是文档对象模型
  • 本文将会完成一个demo, 在一个框中显示鼠标移动的坐标

内容

DOM简介

  • 浏览器已经为我们提供文档节点 对象这个对象是window属性
  • 可以在页面中直接使用, 文档节点代表的是整个网页

    //获取到button对象

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

    //修改按钮的文字

    btn.innerHTML = "nihO";

  • 事件
    • 事件, 就是文档或浏览器窗口中发生的一些特定的交互瞬间.
    • javaScript与HTML之间的交互是通过事件实现的
    • 对于Web应用来说, 有下面这鞋代表性事件: 点击某个元素, 将鼠标移动至某个元素上方, 按下键盘上某个键,等等
    • 事件, 就是用户和浏览器之间的交互行为
    • 我们可以在事件对于的属性中设置一些js代码
      • 这样当事件被触发时, 这些代码将会执行
      • 这种写法我们称为叫做结构和行为耦合, 不方便维护,
      • 不推荐使用
    • 可以为按钮的对应事件绑定处理函数的形式来响应事件
      • 这样当事件被触发时, 其中对应的哈数将会被调用

        //获取对象

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

        btn.onclick = function(){

        };

  • 文档的加载
    • 浏览器在加载一个页面时, 是按照自上向下的顺序加载的
    • 读取到一行就运行一行, 如果将script标签写到页面的上边,
    • 在代码执行时, 页面还没有加载, 页面没有加载DOM对象也没有加载
      • 会导致无法获取到DOM对象
    • 将js代码编写到页面的下部就是为了, 可以在页面加载完毕以后再执行js代码
    • onload事件会在整个页面加载完成之后才触发
      • 该事件对应的响应函数将会在页面加载完成之后执行
      • 这样可以确保我们的代码执行时所有的DOM对象已经加载完毕了

        为window绑定一个onlaoad事件

        window.onload = function(){

        }

  • dom查询
    • 获取元素节点

      • 通过document对象调用
        1. getElementById()
          • 通过id属性获取一个元素节点对象
        2. getElementsByTagName()
          • 通过标签名获取一组元素节点对象
        3. getElementsByName
          • 通过name属性获取一组元素节点对象
    • innerHTML用于获取元素内部的HTML代码
      • 对于自结束, 标签这个属性没有意义
    • innerText
      • 该属性可以获取到元素内部的文本内容
      • 它和innerHTML类似, 不同的是它会自动将html去除
    • 如果需要读取元素节点属性
      • 直接使用元素.属性名
        • 例子: 元素.id 元素.name 元素.value
        • 注意: class属性不能采用这种方式.
          • 读取class属性时需要使用 元素.className

    - 获取元素节点的子节点

    • 通过具体的元素节点调用
      1. getElementsByTagName()
        • 方法, 返回当前节点的指定标签后代节点
      2. childNodes
        • 属性, 表示当前节点的所有子节点
        • childNodes属性会获取包括文本节点在内的所有节点
        • 根据DOM标签空白也会当成文本节点
        • 注意: 在IE8及以下的浏览器中, 不会将空白文本当撑字节点
      3. firstChild
        • 属性, 表示当前节点的第一个子节点(包括空白文本)
      4. lastChild
        • 属性, 表示当前节点的最后一个子节点
      5. children
        • 属性可以可以获取当前元素的所有子元素
      6. firstElementChild
        • 获取当前元素的第一个子元素
        • 支持IE8及以下的浏览器
        • 如果需要兼容他们尽量不要使用
  • 获取父节点和兄弟节点

  • 通过节点的调用
    1. parentNode
      • 属性, 表示当前节点的父节点
    2. previousSibling
      • 属性, 表示当前节点的前一个兄弟节点(也可能获取到空白的文本)
    3. nexrSibling
      • 属性, 表示当前节点的后一个兄弟节点
  • dom查询的剩余方法
    • 获取body标签

      var body = document.getElementsByTagName("body")[0];

    • 在document中有一个属性body, 它保存的是body的引用

      var body = document.body;

    • document.documentElement保存的是html根标签
    • document.all 表示页面中的所有元素
    • 根据class属性值查询一组元素节点对象
      • document.getElementByClassName()可以根据class属性值获取一组元素节点对象
      • 但是该方法不支持IE8及以下的浏览器

        var box1 = document.getElementByClassName("box1");

    • document.querySelector()
      • 需要一个选择器的字符串作为参数, 可以根据一个CSS选择器来查询一个元素节点对象
      • 虽然IE8中没有getElementByClassName()但是可以用document.querySelector()来代替
      • 使用该方法总会返回唯一的一个元素, 如果满足条件的元素有多个, 那么它只会返回第一个

        document.querySelector(".box, div");

    • document.querySelectorAll()
      • 该方法和document.querySelector()用法类似, 不同的是它会将符合条件的元素封装到一个数组中返回
      • 即使符合条件的元素只有一个, 它也会返回数组

dom增删改

  • document.createElement()
    • 可以用于创建一个元素节点对象
    • 它需要一个标签名作为参数, 将会根据改标签名创建元素节点对象.
    • 并将创建好的对象作为返回值

      var li = document.createElement("li");

  • document.createTextNode()
    • 可以用来创建一个文本节点对象
    • 需要一个文本内容作为参数, 将会根据该内容创建文本节点, 并将新的节点返回

      var text = document.createTextNode("广州");

  • appendChild()
    • 向一个父节点中添加一个新的子节点
    • 用法: 父节点.appendChild(子节点);
  • insertBefore()
    • 可以在指定的子节点前插入新的子节点
    • 语法:
      • 父节点.insertBefore(新节点, 旧节点);
  • replaceChild()
    • 可以使用指定的子节点替换已有的子节点
    • 语法: 父节点.replaceChild(新节点, 旧节点);
  • removeChild()
    • 可以删除一个子节点
    • 语法: 父节点.removeChild(子节点);

      city.removeChild(bj);

  • 使用innerHTML也可以完成DOM的增删改的相关操作
  • 一般我们会两种方式结合使用

- 使用dom操作css

  • 通过JS修改元素的样式

    语法: 元素.style样式名 = 样式值

    box1.style.width = "100px";

  • 注意: 如果CSS的样式名中含有-,

    这种名称再JS中是不合法的比如background-color

    需要将这种样式名修改为驼峰命名法

    去掉-, 然后将-后的字母大写 如: backgroundColor

  • 我们通过style属性设置的样式都是内联样式
    • 而内联样式有较高的优先级, 所以通过JS修改的样式往往会立即显示
    • 但是如果在样式中写了!important, 则此时样式会有最高的优先级
      • 即使通过JS也不能覆盖该样式此时将会导致JS样式修改失败, 所以尽量不要为样式添加!important
  • 读取box1的样式
    • 语法: 元素.style.样式名
    • 通过style属性设置和读取的都是内联样式
    • 无法读取样式表中的样式
  • 获取元素当前显示的样式
    • 语法: 元素.currentStyle.样式名

    • 它可以用来读取当前元素正在显示的样式

      alert(box1.currentStyle.width);

    • 如果当前元素没有设置该样式, 则获取它的默认值

      currentStyle只有IE浏览器支持, 其他的浏览器都不支持

  • 在其他的浏览器中可以使用
    • getComputedStyle()这个方法来获取当前元素的样式
    • 这个方法是window的方法, 可以直接 使用
    • 需要两个参数
      • 第一个: 要获取的样式
      • 第二个: 可以传递一个伪元素, 一般都传null

        getComputedStyle(box1, null);

    • 该方法会返回一个对象, 对象中封装了当前元素对应的样式
      • 可以通过对象.样式名来读取样式
      • 如果获取的样式没有设置, 则会获取到真实的值,而不是默认的值
      • 比如: 没有设置width, 它不会获取到auto, 而是一个长度
      • 但是该方法不支持IE8及以下的浏览器
      • 通过currentStyle和getComputedStyle()获取到的样式都是只读的

其他样式的相关属性

  1. clientWidth
  2. clientHeight
    • 这两个属性可以获取元素的可见宽度和高度
    • 这些属性都是不带px的, 返回的都是一个数字 可以直接进行计算
    • 会获取元素的高度和宽度, 包括内容区和内边距
    • 这些属性都是只读的, 不能修改
  3. offsetWidth
  4. offsetHeight
    • 获取元素的整个的宽度和高度, 包括内容区, 内边距和边框
  5. offsetParent
    • 可以用来获取当前元素的定位父元素
    • 会获取到离当前元素最近的开启了定位的祖先元素
    • 如果所有的祖先元素都没有开启定位则返回body
  6. offsetLeft
    • 当前元素相对于其定位元素的水平偏移量
  7. offsetTop
    • 当前元素相对于其定位元素的垂直偏移量
  8. scrollWidth
  9. scrollHeight
    • 可以获取元素整个滚动区域的高度
  10. scrollLeft
    • 可以获取水平滚动条滚动的距离
  11. scrollTop
    • 可以获取垂直滚动条滚动的距离
  • 获取滚动条滚动的距离
    • chrome认为浏览器的滚动条是body的, 可以通过body.scrollTop来获取
    • 火狐等浏览器认为浏览器的滚动条是html的

      //兼容的写法

      var st = document.body.scrollTop || document.documentElement.scrollTop;

      ​ var sl = document.body.scrollLeft || document.documentElement.scrollLeft;

  • 当满足scrollHeight - scrollTop == clientHeight;
  • 说明垂直滚动条滚动到底了
  • 当满足scrollWidth - scrollLeft == clientWidth;
  • 说明水平滚动条滚动到底了
  1. onscroll
    • 该事件会在元素的滚动条滚动时触发
  2. disabled属性可以设置一个元素是否禁用
    • 如果设置为true, 则元素禁用
    • 如果设置为false, 则元素可用

事件对象

  • 当事件的响应函数被触发时, 浏览器每次都会将一个事件对象作为实参传递进响应函数
    • 在事件对象中封装了当前事件相关的一切信息, 比如: 鼠标的坐标, 键盘哪个按键被按下, 鼠标滚轮滚动的方向
    • 在IE8中, 响应函数被触发时, 浏览器不会传递事件对象
      • 在IE8及以下的浏览器中, 是将事件对象作为window对象的属性保存的
        //处理兼容性问题
        if(!event){
           event = window.event;
        }
        //第二种
        event = event || window.event;
        
  1. onmousemove
    • 该事件将会在鼠标在元素中移动时被触发
  2. clientX可以获取鼠标指针的水平坐标
  3. clientY可以获取鼠标指针的垂直坐标
    • clientX和clientY
      • 用于获取鼠标在当前的可见窗口的坐标
      • div的偏移量, 是相对于整个页面的
  4. pageX和pageY可以获取鼠标相对于当前页面的坐标
    • 但是这两个属性在IE8中不支持, 所以如果需要兼容IE8, 则不要使用
  5. target
    • event中的target表示的触发事件的对象
  • 拖拽box1

    ​ - 拖拽的流程

​ 6. 当鼠标在被拖拽的元素上按下时, 开始拖拽onmousedown

​ 7. 当鼠标移动时被拖拽元素跟随鼠标移动onmousemove

  1. 当鼠标松开时, 被拖拽元素固定在当前位置onmouseup

事件的冒泡(bubble)

  • 所谓的事件冒泡就是事件的向上传导, 当后代元素上的事件被触发时, 其祖先元素上的相同事件也会被触发
  • 在开发中大部分情况冒泡都是有用的, 如果不希望发生事件冒泡可以通过事件对象来取消冒泡

    //取消冒泡

    //可以将事件对象的cancelBubble设置为true, 即可取消冒泡

    event.cancelBubble = true;

  • 事件的委派
    • 指将事件统一绑定给元素的共同的祖先元素, 这样当后代元素, 上的事件触发时, 会一直冒泡到祖先元素
    • 我们希望只绑定一次事件, 即可应用到多个元素上 ,即使元素是后添加的
    • 我们可以尝试将其绑定给元素的共同的祖先元素
  • 事件的绑定
    • 使用 对象.事件 = 函数 的形式绑定响应函数
    • 它只能同时为一个元素的一个事件绑定一个响应函数
    • 不能绑定多个, 如果绑定了多个, 则后边会覆盖掉前边的
  • addEventListener()
    • 通过这个方法也可以为元素绑定响应函数
    • 参数:
      1. 事件的字符串, 不要on
      2. 回调函数, 当事件触发时, 该函数会被调用
      3. 是否在捕获阶段触发事件, 需要一个布尔值, 一般都传false

        btn01.addEventListenner("click", function(){}, false);

    • 使用addEventListener()可以同时为一个元素的相同事件同时绑定多个响应函数
    • 这样当事件被触发时, 响应函数, 将会按照函数的绑定顺序执行
    • 这个方法不支持IE8及以下的浏览器
  • attachEvent()
    • 在IE8中可以使用attachEvent()来绑定事件
    • 这个方法也可以同时为一个事件绑定多个处理函数
      • 不同的是他是后绑定先执行, 执行顺序和addEventListener()
    • 参数:
      1. 事件的字符串, 要on
      2. 回调函数
  • 定义一个函数, 用来为指定的元素绑定响应函数
    //addEventListener()中的this, 是绑定事件的对象
    //attachEvent()中的this, 是window
    //需要统一两个方法this
    /*
    参数:
        obj 要绑定事件的对象
        eventStr 事件的字符串(不要on)
        callback 回调函数
    
    */
    function bind(obj, eventStr, callback){
        
        if(obj.addEventListener){
            //大部分浏览器兼容的方式
            obj.addEventListener(eventstr,         callback, false);
        }else{
           //this是由谁调用方式决定
            //callback.call(obj)
            
            //IE8及以下
            
            obj.attachEvent(eventStr,             function(){
                //在匿名函数中掉用回调函数
                callback.call(obj);
            });
        }
        
    }
    
    
  • 事件的传播
    • 关于事件的传播网景公司和微软公司有不同的理解
    • 微软公司认为事件应该是由内向外传播, 也就是当事件触发时, 应该先触发当前元素上的事件, 然后再向当前元素的祖先元素上传播, 也就是说事件应该在冒泡阶段执行
    • 网景公司认为事件应该是由外向内传播的, 也就是当事件触发时, 应该先触发当前元素的最外层的祖先元素的事件, 然后再向内传播给后代元素
    • W3C综合了两个公司的方案, 将传播分成了三个阶段
      1. 捕获阶段
        • 在捕获阶段时从最外层的祖先元素, 向目标元素进行事件的捕获, 但是默认此时不会触发事件
      2. 目标阶段
        • 事件捕获到目标, 捕获结束开始在目标元素上触发事件
      3. 冒泡阶段
        • 事件从目标元素向它的祖先元素传递,依次触发祖先元素上的事件
        • 如果希望在捕获阶段就触发事件, 可以将addEventListener()的第三个参数设置为true
          • 一般情况下我们不会希望在捕获阶段触发事件, 所以这个参数一般都是false
        • IE8及以下的浏览器中没有捕获阶段
  • 拖拽

    • 当我们拖拽一个网页中的内容时, 浏览器会默认去搜索引擎中搜索内容,
    • 此时会导致拖拽功能的异常, 这个是浏览器提供的默认行为
    • 但是这招对IE8不起作用
    • IE: 当调用元素的setCapure()方法以后, 这个元素将会把下一次所有的鼠标按下相关的事件捕获到自身上
    • releaseCapture()取消捕获
    • setCapture()
      • 只有IE支持, 但是在火狐中调用不会报错

滚轮的事件

  1. event.wheelDelta
    • event.wheelDelta 可以获取鼠标滚轮滚动的方向
    ​ - 向上滚是120向下滚是-120
  • wheelDelta这个值我们不看大小, 只看正负
  • wheelDelta这个属性火狐中不支持
  • 火狐中使用event.detail来获取滚动的方向
  • 向上滚是-3 向下滚是3

onmousewheel鼠标滚轮滚动的事件,会在滚轮滚动时触发

​ * 但是火狐不支持该属性

​ *

​ * 在火狐中需要使用DOMMouseScroll来绑定滚动事件

  • 当滚轮滚动时, 如果浏览器有滚动条, 滚动条会随之滚动
  • 这是浏览器的默认行为, 如果不希望发生, 则可以取消默认行为 return false;
  • 使用addEventListener()方法绑定响应函数, 取消默认行为时不能使用return false
    • 需要使用event.prevetDefault();
    • 但是IE8不支持event.preventDefault();这个玩意, 如果直接调用会报错

键盘事件

  1. onkeydown
    • 键盘被按下
    • 对于onkeydown来说如果一直按着某个键不松手, 则事件会一直触发
    • 当onkeydown连续触发时, 第一次和第二次之间的间隔会稍微长一点其他的会非常快
      • 这种设计是为了防止误操作的发生
    • 在文本框中输入内容, 属于onkeydown的默认行为
    • 如果在onkeydown中取消了默认行为, 则输入的内容不会出现在文本框中
  2. onkeyup
    • 键盘被松开
  • 键盘事件一般都会绑定给一些可以获取到焦点的对象, 或者是document
  1. keyCode
    • 可以通过keyCode来获取按键的编码
    • 通过它可以判断那个按键被按下
    • 除了keyCode, 事件对象中还提供了几个属性
    • altKey
    • ctrlKey
    • shiftKey
      • 这三个用来判断alt ctrl 和 shift是否被按下
      • 如果按下则返回true, 否则返回false
    • 判断y和ctrl是否同时被按下
      if(event.keyCode === 89 && event.ctrlKey  === 17){
          console.log("ctrl和y都被按下了");
      }
      

demo演示

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>2021年6月14日11:57:06</title>
    <style>

        #areaDiv {
            width: 500px;
            height: 100px;
            border: 1px solid #000;
            margin: 50px;
        }

        #showMsg {
            width: 500px;
            height: 50px;
            border: 1px solid #000;
            margin: 50px;

        }

    </style>
</head>
<body>
    
    <div id="areaDiv"></div>
    <div id="showMsg"></div>

    <script>

        /**
         *  当鼠标在 areDiv 中移动时, 在showMsg中来显示鼠标的坐标
         */
        //获取两个div
        var areaDiv = document.getElementById("areaDiv");
        var showMsg = document.getElementById("showMsg");

        /**
         *  onmousemove
         *   - 该事件会在鼠标在元素中移动时触发 
         * 
         *  事件对象
         *   - 当事件的响应函数被触发时, 浏览器每次都会将一个事件对象作为实参传递进响应函数
         *       在事件对象中封装了当前事件相关的一切信息, 比如: 鼠标的坐标 键盘哪个按键被按下 鼠标滚轮滚动的方向等等.....
         */
        areaDiv.onmousemove = function(event){
            
            /**
             *  在IE8中, 响应函数被触发时, 浏览器不会传递事件对象
             *    在IE8及以下的浏览器中, 是将事件作为window对象的属性保存的
             * 
             *  clientX可以获取鼠标指针的水平坐标
             *  clientY可以获取鼠标指针的垂直坐标 
             */
            // if(!event){
            //     event = widow.event;
            // }

            //解决事件对象的兼容性问题
            event = event || window.event;

            var x = window.event.clientX;
            var y = window.event.clientY;

            // console.log("x = " + x + "," + "y = " + y);


            //在showMsg中显示鼠标的坐标
            showMsg.innerHTML = "x = " + x + "," + "y = " + y;

        };

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

后记

  • js Dom在实际中用的可能比较少但是我们还是要会是用