元素的属性和特性

我们知道,一个元素除了有开始标签、结束标签、内容之外,还有很多的属性(attribute)

浏览器在解析HTML元素时,会将对应的attribute也创建出来放到对应的元素对象上。

- 比如id、class就是全局的attribute,会有对应的id、class属性;
- 比如href属性是针对a元素的,type、value属性是针对input元素的;

attribute的分类

属性attribute的分类:

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

attribute的操作

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

  • elem.hasAttribute(name) — 检查特性是否存在。
  • elem.getAttribute(name) — 获取这个特性值。
  • elem.setAttribute(name, value) — 设置这个特性值。
  • elem.removeAttribute(name) — 移除这个特性。
  • attributes:attr对象的集合,具有name、value属性;

attribute具备以下特征:

  • 它们的名字是大小写不敏感的(id 与 ID 相同)。
  • 它们的值总是字符串类型的。
  

  <!-- 属性:attribute(特性) -->
  <!--
    attribute的分类:
      1.如果是 HTML 标准制定的attribute,称之为标准Attribute
      2.而自定义的Attribute,称之为非标准Attribute
  -->

  <div id="abc" class="box" title="box" age="18" height="1.88">
    我是box
  </div>
  <a href="https://www.baidu.com">百度一下</a>
  <input type="checkbox" checked="checked">

  <script>
    var boxEl = document.querySelector('.box')
    // 1.所有的attribute的操作
    console.log(boxEl.hasAttribute('age'), boxEl.hasAttribute('abc'), boxEl.hasAttribute('id')) // true false true
    console.log(boxEl.getAttribute('AGE'), boxEl.getAttribute('abc'), boxEl.getAttribute('id')) // 18 null abc

    boxEl.setAttribute('id', 'cba')
    boxEl.removeAttribute('id')

    var boxAttributes = boxEl.attributes
    for (var attr of boxAttributes) {
      console.log(attr.name, attr.value)
    }

    // 2.通过 getAttribute 一定是字符串类型
    var inputEl = document.querySelector('input')
    console.log(inputEl.getAttribute('checked')) // 这里我们希望拿到的是boolean类型,但是实际上最好是boolean
  </script>

元素的属性(property)

对于标准的attribute,会在DOM对象上创建与其对应的property属性:

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

  • 改变property,通过attribute获取的值,会随着改变;
  • 通过attribute操作修改,property的值会随着改变;
    • 但是input的value修改只能通过attribute的方法;

除非特别情况,大多数情况下,设置、获取attribute,推荐使用property的方式:

  • 这是因为它默认情况下是有类型的;

  <!-- 元素中的属性称之为attribute -->
  <!-- 标准的attribute在对应的对象模型中都有对应的property -->
  <div id="abc" class="box" title="标题" age="18" height="1.88">
    我是box
  </div>

  <input type="checkbox" checked>

  账号:<input class="account" type="text">

  <button class="btn">设置input的值</button>

  <script>
    /* 对象中的属性称之为 property
      var obj = {
        // property
        name:'what'
      }
     */

    //  1.通过property获取attribute的值
    //  获取 box 元素
    var boxEl = document.querySelector('.box')
    console.log(boxEl.id, boxEl.className, boxEl.title, boxEl.age, boxEl.height)

    // input 元素
    var inputEl = document.querySelector('input')
    /* if (inputEl.getAttribute('checked')) {
      console.log('checked 处于选中状态')
    } */

    if (inputEl.checked) {
      console.log('checked 处于选中状态')
    }

    console.log(typeof inputEl.checked)
    
    // 2.attribute 和 property 是相互影响的
    boxEl.id = 'aaa'
    console.log(boxEl.getAttribute('id'))

    boxEl.setAttribute('title', '呵呵呵呵')
    console.log(boxEl.title)

    // 3.比较特殊的情况,input设置值
    var accountInputEl = document.querySelector('.account')

    var btnEl = document.querySelector('.btn')
    btnEl.onclick = function () {
      accountInputEl.value = 'what' // 优先级更高
      // accountInputEl.setAttribute('value', 'kobe')
    }

HTML5的data-*自定义属性

HTML5的data-*自定义属性,它们也是可以在dataset属性中获取到的:

  <div id="a" class="box" title="标题" data-age="18" data-height="1.88">我是box</div>

  <script>
    var boxEl = document.querySelector('.box')
    console.log(boxEl.dataset)
    console.log(boxEl.dataset.age)
    console.log(boxEl.dataset.height)
  </script>

JavaScript动态修改样式

有时候我们会通过JavaScript来动态修改样式,这个时候我们有两个选择:

  • 选择一:在CSS中编写好对应的样式,动态的添加class;
  • 选择二:动态的修改style属性;

开发中如何选择呢?

  • 在大多数情况下,如果可以动态修改class完成某个功能,更推荐使用动态class;
  • 如果对于某些情况,无法通过动态修改class(比如精准修改某个css属性的值),那么就可以修改style属性;
  <div class="box">我是box</div>
  <script>
    // 1.获取 boxEl
    var boxEl = document.querySelector('.box')

    // 2.监听点击
    var counter = 1
    boxEl.onclick = function () {
    
      /*  直接修改style
        boxEl.style.color = 'red'
        boxEl.style.fontSize = '24px'
        boxEl.style.backgroundColor = 'skyblue'
      */

      //  2.动态添加样式
      boxEl.className = 'active'
      
      // 3.动态修改boxEl的宽度
      boxEl.style.width = 100 * counter + 'px'

      counter++

    }

  </script>

元素的className和classList

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

  • 这是因为JavaScript早期是不允许使用class这种关键字来作为对象的属性,所以DOM规范使用了className;
  • 虽然现在JavaScript已经没有这样的限制,但是并不推荐,并且依然在使用className这个名称;

我们可以对className进行赋值,它会替换整个类中的字符串。

如果我们需要添加或者移除单个的class,那么可以使用classList属性。

  • elem.classList 是一个特殊的对象:
  • elem.classList.add (class) :添加一个类
  • elem.classList.remove(class):添加/移除类。
  • elem.classList.toggle(class) :如果类不存在就添加类,存在就移除它。
  • elem.classList.contains(class):检查给定类,返回 true/false。

classList是可迭代对象,可以通过for of进行遍历

  <div class="box">我是box</div>
  <button class="btn">切换</button>

  <script>
    var boxEl = document.querySelector('.box')
    
    // 1.方法一:className
    // boxEl.className = 'abc'

    // 2.方法二:classList
    boxEl.classList.add('abc')
    boxEl.classList.add('active')
    boxEl.classList.remove('abc')

    // 需求 box 在active之间切换
    var btnEl = document.querySelector('.btn')
    btnEl.onclick = function () {
      /*
        if (boxEl.classList.contains('active')) {
          boxEl.classList.remove('active')
        } else {
          boxEl.classList.add('active')
        }
      */
      boxEl.classList.toggle('active')
    }
  </script>

元素的style属性

如果需要单独修改某一个CSS属性,那么可以通过style来操作:

  • 对于多词(multi-word)属性,使用驼峰式 camelCase

如果我们将值设置为空字符串,那么会使用CSS的默认样式:

多个样式的写法,我们需要使用cssText属性:

  • 不推荐这种用法,因为它会替换整个字符串;

  <div class="box" style="background-color: aqua;color: white;">
    我是box
  </div>

  <script>
    var boxEl = document.querySelector('.box')

    // 1.在 property 中使用的是小驼峰模式
    console.log(boxEl.style)
    
    // 2.将一个属性的值设置为空,使用的是浏览器的默认样式
    /*
      boxEl.style.display = ''
      boxEl.style.fontSize = ''
    */
    // 3.设置多个样式
    /* boxEl.style.fontSize = '30px'
    boxEl.style.fontSize = '30px' */
    boxEl.style.cssText = 'font-size:30px;color:red;'

元素style的读取 - getComputedStyle

如果我们需要读取样式:

  • 对于内联样式,是可以通过style.*的方式读取到的;
  • 对于style、css文件中的样式,是读取不到的;

这个时候,我们可以通过getComputedStyle的全局函数来实现:

<!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>Document</title>
  <style>
    .box {
      font-size: 20px;
    }
  </style>
</head>

<body>
  <div class="box" style="background-color:red;">我是box</div>
  <script>
    var boxEl = document.querySelector('.box')
    console.log(boxEl.style.backgroundColor)
    console.log(boxEl.style.fontSize)

    console.log(getComputedStyle(boxEl).fontSize)
  </script>
</body>
</html>

创建元素

使用 document.write 方法写入一个元素:

  • 这种方式写起来非常便捷,但是对于复杂的内容、元素关系拼接并不方便;
  • 它是在早期没有DOM的时候使用的方案,目前依然被保留了下来;

那么目前我们想要插入一个元素,通常会按照如下步骤:

  • 步骤一:创建一个元素;
  • 步骤二:插入元素到DOM的某一个位置

创建元素: document.createElement(tag)


插入元素

插入元素的方式如下:

  • 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 替换为给定的节点或字符串

<span>11111</span>
  <div class="box">
    <span class="box-frist">hhhh</span>
    <p>呵呵呵呵</p>
  </div>

  <script>
    var boxEl = document.querySelector('.box')

    // 1.通过innerHTML(不推荐)
    /*
        boxEl.innerHTML = `<h2 class='title'>我是标题</h2>`
        setTimeout(function () {
          var titleEl = document.querySelector('.title')
          titleEl.classList.add('active')
        }, 2000)
     */

    //  2.真实创建一个 DOM 对象
    var h2El = document.createElement('h2')
    h2El.className = 'title'
    h2El.classList.add('active')
    h2El.textContent = '我是标题'

    // 将元素插入 boxEl
    // boxEl.append(h2El)
    // boxEl.prepend(h2El)
    // boxEl.after(h2El)
    // boxEl.replaceWith(h2El)

    // 插入到 span和p元素之间
    // var spanEl = document.querySelector('span')
    // var spanEl = boxEl.children[0]
    var spanEl = boxEl.querySelector('span')

    spanEl.after(h2El)
  </script>

移除和克隆元素

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

如果我们想要复制一个现有的元素,可以通过cloneNode方法:

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

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

  <button class="remove-btn">移除box</button>
  <button class="clone-btn">复制box</button>

  <div class="box">
    我是box
    <h2>我是标题</h2>
    <p>我是文本 呵呵哈哈哈</p>
  </div>

  <script>
    // 1.获取元素
    var boxEl = document.querySelector('.box')
    var removeBtnEl = document.querySelector('.remove-btn')
    var cloneBtnEl = document.querySelector('.clone-btn')

    // 2.监听removeBtnEl点击事件
    removeBtnEl.onclick = function () {
      boxEl.remove()
    }
    
    // 3.复制 box
    var counter = 0
    cloneBtnEl.onclick = function () {
      var newNode = boxEl.cloneNode(true)
      newNode.children[0].textContent = '我也是标题' + counter
      // boxEl.after(newNode)
      document.body.append(newNode)
      counter++
    }
  </script>

旧的元素操作方法

在很多地方我们也会看到一些旧的操作方法:

  • parentElem.appendChild(node):
    • 在parentElem的父元素最后位置添加一个子元素
  • parentElem.insertBefore(node, nextSibling):
    • 在parentElem的nextSibling前面插入一个子元素;
  • parentElem.replaceChild(node, oldChild):
    • 在parentElem中,新元素替换之前的oldChild元素;
  • parentElem.removeChild(node):
    • 在parentElem中,移除某一个元素;

元素的大小、滚动

  • clientWidth:contentWith+padding(不包含滚动条)

  • clientHeight:contentHeight+padding

  • clientTop:border-top的宽度

  • clientLeft:border-left的宽度

  • offsetWidth:元素完整的宽度

  • offsetHeight:元素完整的高度

  • offsetLeft:距离父元素的x

  • offsetHeight:距离父元素的y

  • scrollHeight:整个可滚动的区域高度

  • scrollTop:滚动部分的高度

<!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>Document</title>
  <style>
    body {
      padding: 100px;
    }
    .box {
      width: 100px;
      height: 100px;
      padding: 20px;
      border: 10px solid red;
      /* box-sizing: border-box0 */
      background-color: orange;
      overflow: auto;
    }
  </style>
</head>

<body>
  <div class="box">
    We’ve started making a tradition of rounding up the latest front-end research at the end of each year. We did it in
    2020 and again in 2021. Reports are released throughout the year by a bunch of different companies and organizations
    researching everything from web design trends to developer skills to popular coding languages and […]
  </div>
  <script>
    var boxEl = document.querySelector('.box')

    // 1.获取样式(局限性强)
    var boxStyle = getComputedStyle(boxEl)
    console.log(boxStyle.width, boxStyle.height)

    // 2.获取更多信息
    console.log(boxEl.clientWidth)
    console.log(boxEl.clientHeight)

    console.log(boxEl.clientTop)
    console.log(boxEl.clientLeft)

    console.log(boxEl.offsetWidth)
    console.log(boxEl.offsetHeight)

    console.log(boxEl.offsetLeft)
    console.log(boxEl.offsetHeight)

    // windows 对象
    window.onclick = function () {
      console.log(boxEl.scrollTop)
    }
  </script>
</body>
</html>

window的大小、滚动

window的width和height

  • innerWidth、innerHeight:获取window窗口的宽度和高度(包含滚动条)
  • outerWidth、outerHeight:获取window窗口的整个宽度和高度(包括调试工具、工具栏)
  • documentElement.clientHeight、documentElement.clientWidth:获取html的宽度和高度(不包含滚动条)

window的滚动位置:

  • scrollX:X轴滚动的位置(别名pageXOffset)
  • scrollY:Y轴滚动的位置(别名pageYOffset)

也有提供对应的滚动方法:

  • 方法 scrollBy(x,y) :将页面滚动至 相对于当前位置的 (x, y) 位置;
  • 方法 scrollTo(pageX,pageY) 将页面滚动至 绝对坐标;
<!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>Document</title>
  <style>
    .box {
      /* width: 3000px; */
      height: 200px;
      background-color: orange;
    }

    .scroll-btn {
      position: fixed;
      right: 20px;
      bottom: 20px;
      /* display: none; */
    }
  </style>
</head>

<body>
  <div class="box"></div>
  <button class="scroll-btn">回到顶部</button>
  <br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
  
  <script>
    // window 大小
    console.log(window.outerWidth)
    console.log(window.outerHeight)

    console.log(window.innerWidth)
    console.log(window.innerHeight)
    
    console.log(document.documentElement.offsetWidth)
    console.log(document.documentElement.offsetHeight)

    // 获取 window 的滚动区域
    window.onclick = function () {
      console.log(window.scrollX)
      console.log(window.scrollY)
    }

    var scrollBtn = document.querySelector('.scroll-btn')
    scrollBtn.hidden = true
    window.onscroll = function () {
      var scrollY = window.scrollY
      if (scrollY > 600) {
        // scrollBtn.style.display = 'block'
        scrollBtn.hidden = false
      } else {
        // scrollBtn.style.display = 'none'
        scrollBtn.hidden = true
      }
    }

    // 点击按钮后滚动到某个位置
    scrollBtn.onclick = function () {
      // window.scrollBy(0, 100)
      window.scrollTo(0, 0)
    }
  </script>
</body>
</html>

练习

计数器

  <button class="add">+</button>
  <button class="sub">-</button>
  <h2 class="counter">0</h2>

  <script>
    // 1.获取按钮
    var addEl = document.querySelector('.add')
    var subEl = document.querySelector('.sub')
    var h2El = document.querySelector('.counter')
    
    // 2.监听 btnEl 的点击
    var counter = 0
    addEl.onclick = function () {
      h2El.textContent = counter++
    }

    subEl.onclick = function () {
      h2El.textContent = counter--
    }
  </script>

输入动态列表

  <h1>动态创建列表</h1>
  <ul class="list"></ul>
  
  <script>
    var listEl = document.querySelector('.list')

    var isFlag = true
    while (isFlag) {
      var message = prompt('请输入信息:')
      if (!message) {
        isFlag = false
      } else {
        var liEl = document.createElement('li')
        liEl.textContent = message
        listEl.append(liEl)
      }
    }
  </script>

动态显示时间

  <h1 class="time">2022-12-24 11:13:57</h1>

  <script>
    // 封装工具函数
    function padLeft (content, count, padStr) {
      count = count || 2
      padStr = padStr || '0'
      
      content = String(content)
      return content.padStart(count, padStr)
    }

    // 1.获取时间的元素
    var timeEl = document.querySelector('.time')

    setInterval(function () {
      // 2.获取具体的时间并且进行格式化
      var date = new Date()
      var year = date.getFullYear()
      var month = padLeft(date.getMonth() + 1)
      var day = padLeft(date.getDate())
      var hour = padLeft(date.getHours())
      var minute = padLeft(date.getMinutes())
      var second = padLeft(date.getSeconds())

      // 3.将时间放在 timeEl中
      timeEl.textContent = `${year}-${month}-${day} ${hour}:${minute}:${second}`
    }, 1000)

    /* 补充 String 方法
      var str = '124'
      console.log(str.padStart(4, '0'))
    */
  </script>

倒计时

<!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>Document</title>
  <style>
    .countdown {
      color: orange;
      font-size: 30px;
    }
    
    .countdown .time {
      background-color: orange;
      color: #fff;
      display: inline-block;
      padding: 5px;
      border-radius: 3px;
    }
  </style>
</head>

<body>
  <div class="countdown">
    <span class="time hour">03</span>
    <span class="split">:</span>
    <span class="time minute">26</span>
    <span class="split">:</span>
    <span class="time second">45</span>
  </div>

  <script>
    // 封装工具函数
    function padLeft (content, count, padStr) {
      count = count || 2
      padStr = padStr || '0'
      content = String(content)
      return content.padStart(count, padStr)
    }

    // 1.获取元素
    var hourEl = document.querySelector('.hour')
    var minuteEl = document.querySelector('.minute')
    var secondEl = document.querySelector('.second')

    var endDate = new Date()
    endDate.setHours(24)
    endDate.setMinutes(0)
    endDate.setSeconds(0)
    endDate.setMilliseconds(0)

    setInterval(function () {
      // 获取倒计时的小时-分钟-秒钟
      // 11:53 => 24:00
      var nowDate = new Date()
      var intervalTime = Math.floor((endDate.getTime() - nowDate.getTime()) / 1000)
      console.log(intervalTime)
      
      // 25926: x小时x分钟x秒钟
      var hour = Math.floor(intervalTime / 3600)
      var minute = Math.floor(intervalTime / 60) % 60
      var second = Math.floor(intervalTime % 60)

      // 2.设置内容
      hourEl.textContent = padLeft(hour)
      minuteEl.textContent = padLeft(minute)
      secondEl.textContent = padLeft(second)
    })

    // 123:x百x十x个
    /*
      var num = 125
      console.log(Math.floor(num / 100))
      console.log(Math.floor(num / 10) % 10)
      console.log(Math.floor(num % 10))
     */
  </script>
</body>
</html>

Q.E.D.