系统化学习前端之JavaScript(DOM)

前言

学习了 JavaScript,如何关联 HTML 和 CSS 呢?没错,DOM 就是干这个的。

DOM

Document Object Model,文档对象模型,专门操作网页内容的 API 标准。

DOM 操作页面内容

网页内容是由多个 DOM 元素以树结构组合而成,也称为 DOM tree。DOM 元素包含 HTML标签,标签属性,内容以及样式。简而言之,一组标签元素就是一个 DOM 元素,标签内可以有内容、属性,也可以有样式,如 <div></div>

DOM 操作页面内容就是 JavaScript 利用 DOM API 操作 HTML标签,内容以及样式(CSS)。

注意:很多文章会由 Node 节点介绍页面内容,其实 Node 节点和 DOM 元素是一致的,唯一的不同在于 Node 节点包含空白节点,即 Node 节点 = DOM 元素 + 空白节点。

DOM 元素新增

  1. 创建元素

    var elem = document.createElement('a'); // <a></a>
    

    document.createElement() 接收 HMTL 标签名作为参数,返回 HTML 标签元素,即 DOM 元素。

  2. 添加属性

    elem.href = 'https://www.baidu.com'
    

    elem 元素更应该看作是对象,通过 . 的方式设置添加属性。

  3. 添加内容

    elem.innerHTML = '百度'
    
    elem.textContent = '百度'
    

    elem.innerHTML 既可以添加文本内容,也可以添加标签元素,如 elem.innerHTML = '<span>百度</span>'elem.textContent 只能添加文本内容,添加标签元素无法解析成 HTML 标签。

    注意:elem.outerHTML 是指包含标签的文本字符串,如在 <span>百度</span>elem.outerHTML'<span>百度</span>',而 elem.innerHTML 只是 '百度'

  4. 添加样式

    elem.style.color = 'red'
    elem.style.fontSize = '20px'
    
    elem.style.cssText = 'color: red; font-size: 20px' // 复合写法
    

    elem.style.attr 的方式可以为标签元素添加样式,添加的样式为行内式。

  5. 挂载元素

    根据元素挂载位置不同,可以选择不同的 API 。

    • 末尾追加

      parent.appendChild(elem) // parent 为 elem 元素的父元素,即 elem 元素添加至 parent 元素内的末尾
      
    • 中间插入

      parent.insertBefore(elem, x) // elem 元素添加至 parent 元素内,且在 x 元素之前
      

      注意:可以通过 x.previousElementSiblingx.nextElementSibling 来选择插入 x 元素的上一个兄弟元素或下一个兄弟元素之前(x 元素之后)。

    • 替换

      parent.replaceChild(elem, x) // 在 parent 元素内,使用 elem 元素替换 x 元素
      

    注意:上述方式只能无法一次添加多个元素,多次添加的话,会造成 DOM 树多次重排,影响性能。

  6. 批量挂载

    使用文档片段的方式批量添加,将多个 DOM 元素添加至文档片段中,最后将文档片段添加至DOM tree 中。

    var frag = document.createDocumentFragment()
    
    frag.appendChild(elem1) // 添加 elem1 元素
    frag.appendChild(elem2) // 添加 elem2 元素
    
    parent.appendChild(frag)
    

DOM 元素删除

  1. 删除元素

    parent.removeChild(elem) // 从 parent 元素中删除 elem 元素。
    
  2. 删除元素属性

    • DOM 对象的方式

      elem.id=''
      
    • DOM API 的方式

      elem.removeAttribute('id')
      
  3. 删除元素内容

    elem.innerHTML = ''
    
    elem.textContent = ''
    
  4. 删除元素样式

    elem.style.color = ''
    elem.style.fontSize = ''
    
    elem.style.cssText = ''
    

DOM 元素修改

  1. 修改元素

    修改元素:先删除,再新增即可。

  2. 修改元素属性

    • DOM 对象的方式

      elem.className = 'wrapper'
      

      注意:class 属性在 DOM 对象方式下,需要变为 className,表单的 for 属性需要变为 hmlFor

    • DOM API 的方式

      elem.setAttribute('class', 'wrapper')
      

      注意:setAttribute() 第一个参数为属性名,第二个参数为属性值,属性名不需要改变。表单属性中的状态属性:disabled, checked, selected 只能通过 DOM 对象的方式修改,不能使用 DOM API方式。

  3. 修改元素内容

    elem.innerHTML = 'baidu'
    
    elem.textContent = 'baidu'
    
  4. 修改元素样式

    elem.style.color = 'blue'
    elem.style.fontSize = '16px'
    
    elem.style.cssText = 'color: blue; font-size: 16px;'
    

DOM 元素查询

  1. 查询元素

    • 直接查询元素

      document.documentElementhtml 元素。

      document.headhead 元素。

      document.bodybody 元素。

    • 关系查询

      elem.parentElementelem 的父元素。

      elem.childrenelem 的子元素,返回子元素构成的类数组对象。

      elem.previousElementSiblingelem 的前一个兄弟元素。

      elem.nextElementSiblingelem 的后一个兄弟元素。

    • DOM API 查询

      document.getElementById() 按 id 查询单个元素,返回最先找到的结果,如 document.getElementById('app')

      parent.getElementsByClassName() 按 class 查询,返回父元素下相同类名的动态结果集,如 parent.getElementsByClassName('wrapper')

      parent.getElementsByTagName() 按 标签名 查询,返回父元素下相同标签名的动态结果集,如 parent.getElementsByTagName('div')

      parent.querySelector() 按 选择器 查询单个元素,返回父元素下最先找到的结果,如 parent.querySelector('#app')

      parent.querySelectorAll() 按 选择器 查询,返回父元素下满足选择器的静态结果集,如 parent.querySelectorAll('.wrapper')

      注意:

      a. 只有 getElementById() 是仅能用过 document 调用的。

      b. querySelector()querySelectorAll() 参数是需要添加选择器标识的。

      c. getElementById()querySelector() 是查询单个元素的,均为非贪婪查询。

      d. 动态结果集是指查询完成以后,parent 继续新增元素,当元素满足时,结果集依然能够体现;静态不可以。

  2. 查询元素属性

    • DOM 对象的方式

      elem.title // 返回 undefined 表示属性不存在
      
    • DOM API 的方式

      var isHas = elem.hasAttribute('title') // 返回 bool 值,表示是否具有 title 属性
      
      var val = elem.getAttribute('title')
      
    • 自定义属性

      定义自定义属性

      <div data-count="3"></div>
      

      查询自定义属性

      elem.dataset.count // DOM 对象
      
      var val = elem.getAttribute('data-count') // DOM API
      

      修改自定义属性

      elem.dataset.count = 4 // DOM 对象
      
      elem.setAttribute('data-count', 4) // DOM API
      
  3. 查询元素内容

    elem.innerHTML
    
    elem.textContent
    
  4. 查询元素样式

    • 查询行内样式

      elem.style.color
      elem.style.fontSize
      
      elem.style.cssText
      
    • 查询计算后样式(实际应用样式)

      var style = getComputedStyle(elem)
      
      style.color // DOM 对象方式
      
      var value = style.getPropertyValue('color') // DOM API 方式
      

      注意:计算后样式只能读取,不能修改。

DOM 操作页面交互

页面交互是指用户或浏览器执行一些操作,触发 JavaScript 通过 DOM API 操作页面给予反馈。

DOM 操作页面交互就是 JavaScript 通过事件触发执行处理函数进行页面内容改变,达到交互目的。

事件流程

事件是由浏览器定义的触发机制,可以完成一个由人操作到计算机操作的过程。当用户执行一定的操作(人操作)时,事件会触发,执行事件回调函数进行处理操作(计算机操作)。

  1. 绑定事件

    • HTML 绑定

      <div onclick="handleClick()">点击事件</div>
      
      <script type="text/javascript">
          function handleClick() {
              console.log(123)
          }
      </script>
      

      注意: click 为事件名,handleClick 为事件回调函数。

    • DOM 对象绑定

      <div id="app">点击事件</div>
      
      <script type="text/javascript">
           var div = document.getElementById('app')
      
          div.onclick = function () {
              console.log(123)
          }
      </script>
      
    • DOM API 绑定

      <div id="app">点击事件</div>
      
      <script type="text/javascript">
          var div = document.getElementById('app')
      
          function handleClick() {
              console.log(123)
          }
      
          div.addEventListener('click', handleClick)
      </script>
      

    注意:HTML 方式和 DOM 对象方式都是 on + 事件名 的形式绑定事件,

  2. 移除事件

    • DOM 对象移除

      div.onclick = null
      
    • DOM API 移除

      div.removeEventListener('click', handleClick)
      
  3. 事件回调函数

    事件回调函数是一个特殊的函数,需要通过事件触发去调用,并且当不传参的时候,事件回调函数默认会传入一个事件对象作为参数。

    <div id="app">点击事件</div>
    
    <script type="text/javascript">
        var div = document.getElementById('app')
    
        function handleClick(event) {
            console.log(event) // PointerEvent
        }
    
        function fun(event) {
            console.log(event) // undefined
        }
    
        div.addEventListener('click', handleClick)
        fun()
    	</script>
    

    注意:PointerEvent 为事件对象的一种类型,事件对象还有其他类型,如 keyboradEvent, MouseEvent 等。事件对象是指包含事件过程所有详细信息的对象,包括触发事件的元素,元素的 x,y 位置信息等。

    事件对象的重要方法:

    • event.stopPropagation()

      中断嵌套元素的事件冒泡。低版本浏览器使用 window.event.cancelBubble = true

    • event.preventDefault()

      阻止元素的默认行为,如 a 标签的默认链接跳转行为。低版本浏览器使用 e.returnValue = false

    • event.stopImmediatePropagation()

      元素绑定事件有多个事件回调函数,默认事件触发会执行多个事件回调函数,stopImmediatePropagation 会阻止其他事件回调函数执行,只执行当前事件回调函数。

事件模型

嵌套元素的父、子元素均绑定事件,事件触发以后如何执行事件回调函数呢?

<style type="text/css">
    #app {
        width: 300px;
        height: 300px;
        border: 1px solid #ccc;
    }

    #wrapper {
        width: 100px;
        height: 100px;
        margin: 100px auto 0;
        background-color: skyblue;
    }
</style>

<div id="app">
    <div id="wrapper"></div>
</div>

<script type="text/javascript">
    var app = document.getElementById('app')
    var wrapper = document.getElementById('wrapper')

    app.onclick = function () {
        console.log('app')
    }

    wrapper.onclick = function () {
        console.log('wrapper')
    }

    // 点击 wrapper 执行结果为 wrapper app
</script>

嵌套元素的绑定事件涉及到了 JavaScript 的事件模型,事件模型包含三个阶段:捕获阶段、触发阶段、冒泡阶段。

  1. 捕获阶段

    JavaScript 执行以后,根据嵌套元素的嵌套层次,按照由外向内的捕获事件,并将事件回调函数添加至函数调用栈。

  2. 触发阶段

    事件触发,根据事件触发的元素,函数调用栈指针移动至对应事件回调函数,执行对应事件回调函数。

  3. 冒泡阶段

    执行完事件触发元素的事件回调函数后,函数调用栈执行会自动向栈底移动,依次执行到栈底的所有事件回调函数。

注意:
a. 在子元素的事件回调函数中,通过事件对象的方法 event.stopstopPropagation() 可以中断事件模型中的冒泡阶段 。

b. 在父元素的事件绑定中,通过elem.addEventListener() 的第三个参数可以改变冒泡顺序,前面两个参数分别为事件名和事件处理函数,第三个参数可以缺省,参数值是 boolean 类型,默认是 false,表示嵌套元素的事件回调函数由内向外依次执行,设置为 true,表示事件回调函数由外向内依次执行。

事件委托

事件委托利用了事件模型中的冒泡特性,采取将嵌套元素中子元素的事件绑定到父元素上,子元素触发事件会冒泡执行父元素的事件回调函数。

<div id="app">
    <div class="wrapper">1 号元素</div>
    <div class="wrapper">2 号元素</div>
    <div class="wrapper">3 号元素</div>
</div>

<script type="text/javascript">
    var app = document.getElementById('app')

    function handleClick (event) {
        console.log(event)
    }

    app.addEventListener('click', handleClick)
</script>

以上,点击任意一个子元素均会触发父元素的 click 事件,并执行事件回调函数,这种模式被称为事件委托。事件委托可以减少事件监听的个数,此外,也具备动态性(新增子元素也可以触发事件)。

注意:可以通过事件对象 event.target 获取触发事件的元素。

常见事件

  1. 点击事件

    click 鼠标单击时触发。

    dblclick 鼠标双击时触发。

    touch 移动端事件,触摸屏幕触发。

    touchstart 移动端事件,触摸按下时触发。

    touchmove 移动端事件,触摸移动时触发。

    touchend 移动端事件,触摸抬起时触发。

    注意:

    移动端重叠元素 A 和 B ,A 在 B 之上,A 绑定 touchstart 事件,B 绑定 click 事件,点击 A 以后,会触发 B 的 click 事件,这种现象被称点击穿透现象。

    造成点击穿透的原因: 移动端 touchstart 触发以后,300ms 内会自动触发一个 click 事件,延迟 300ms 是为了判断是否是双击放大功能。

    解决点击穿透的方法:

    1. 在移动端不要混合使用 touchstartclick 事件。

    2. 使用 event.preventDefault() 阻止 touchstart 的默认行为。

    3. 绑定 touchstart 事件的元素设置 pointer-events: auto; 属性。

    4. 设置延迟 300ms 后执行 touchstart 事件。

    5. 设置 touch-action:manipulation; 属性,取消延迟。

  2. 键盘事件

    keypress 键盘按键时触发。

    keydown 键盘按键按下时触发。

    keyup 键盘按键弹起时触发。

  3. 鼠标事件

    mouseentermouseover 鼠标移入时触发,mouseover 鼠标在移入目标元素嵌套的元素时会再次触发。

    mouseleavemouseout 鼠标移出时触发,mouseout 鼠标从嵌套的元素移出到目标元素时会再次触发。

    mousemove 鼠标移动时触发。

    mousedown 鼠标按下时触发。

    mouseup 鼠标弹起时触发。

    mousewheel 鼠标滚轮滚动时触发。

  4. 页面事件

    load 页面加载完成时触发。

    pagehide 页面隐藏时触发。

    pageshow 页面显示时触发。

    resize 页面缩放时触发。

    scroll 页面滚动时触发。

    注意:scroll 也可以监听区域内容滚动,需要设置 overflow: autooverflow: scroll

    popstate 页面回退时触发。

    注意:popstate 只能监听页面有刷新的后退操作,如 history.go(-1), history.back(),无法监听无刷新页面的操作,如 history.pushState(), history.replaceState(),可参考 BOM 中的 history 对象

  5. 动画事件

    animationstart 动画开始时触发。

    animationend 动画结束时触发。

    transitionstart 过渡开始时触发。

    transitionend 过渡结束时触发。

  6. 拖放事件

    参考 HTML5 拖放 API

  7. 表单事件

    input 表单元素 input 输入时触发。

    blur 表单元素 input 失焦时触发。

    focus 表单元素 input 聚焦时触发。

    change 表单元素 input 输入值改变时、select option改变时、radio、box选中改变时触发。

    submit 表单元素 form 提交时触发。

    reset 表单元素 form 重置时触发。

  8. 媒体事件

    参考 HTML5 音视频

自定义事件

  1. 定义事件对象

    var e = new Event('customClick') // customClick 为自定义事件名称
    
    var e = new CustomEvent('customClick', { detail: 'ming' })
    

    注意:CustomEvent() 可以通过 detail 属性传递数据,通过事件对象 event.detail 可以接受数据。

  2. 绑定事件对象

    var app = document.getElementById('app')
    
    function handleClick(event) {
        console.log(event)
    }
    
    app.addEventListener('customClick', handleClick)
    
  3. 触发事件对象

    app.dispatchEvent(e)
    

    注意:自定义事件需要调用触发。

  4. 移除事件对象

app.removeEventListener('customClick', handleClick)

后记

DOM 文档对象模型,提供了丰富的 API,利用这些 API 可以完成 JavaScript 与 HTML、CSS 的勾连。正式 DOM 的存在才使得 JavaScript 能够操作页面内容,完成页面复杂的交互。

posted @ 2023-03-24 17:13  深巷酒  阅读(8)  评论(0编辑  收藏  举报