当前位置: 首页 > article >正文

【H2O2|全栈】JS入门知识(八)DOM(2)

目录

JS

前言

准备工作

排他操作

概念

案例

开关

概念

案例

自定义属性

设置属性

获取属性

移除属性

H5标准自定义属性格式规范 

案例

节点

层级

父节点

子节点

兄弟节点

创建节点

添加节点

案例

结束语


JS

前言

本系列博客主要分享JavaScript的基础语法知识,本期为第八期,包含一些简单的js语法——排他操作、开关思想、自定义属性和节点的相关内容。

与HTML和CSS相比,JS加入了很多逻辑性的元素在里面,所以需要一定的逻辑思维能力,要求能够整合一些知识。如果遇到不理解之处,可以参阅同系列之前的章节。

准备工作

软件:【参考版本】Visual Studio Code

插件(扩展包):Open in browser, Live Preview, Live Server, Tencent Cloud AI Code Assistant, htmltagwrap

浏览器版本:Chrome

系统版本: Win10/11/其他非Windows版本

*我的电脑是Win10的版本,仅供参考*

排他操作

概念

所谓排他操作,就是指在一个元素实现指定的样式时,让同级的其余元素清除该样式。

排他操作的算法步骤如下: 

  1. 所有元素全部清除样式(干掉其他)
  2. 给当前元素设置样式 (留下自己)

注意顺序不能颠倒,首先干掉其他,再设置自己。

案例

Q:让下面的按钮,只有一个按钮处于特殊的状态——

A:实现一下上面的效果(比较简陋)——

    <button>按钮1</button>
    <button>按钮2</button>
    <button>按钮3</button>
    <button>按钮4</button>
    <button>按钮5</button>
    <script>
        // 1. 获取所有按钮元素
        var btns = document.getElementsByTagName('button');
        for (var i = 0; i < btns.length; i++) {
            btns[i].onclick = function() {
                // (1) 我们先把所有的按钮背景颜色去掉
                for (var i = 0; i < btns.length; i++) {
                    btns[i].style.backgroundColor = '';
                }
                // (2) 然后才让当前的元素背景颜色为red
                this.style.backgroundColor = 'red';

            }
        }
    </script>

开关

概念

开关通常是一个boolean类型的变量, 通过操作开关,可以保存一组操作的最终状态

通常用于涉及逻辑运算(与或非)的情况。

案例

Q:在一组checkbox中,实现全选和全不选效果。

具体效果如下——

A:首先,我们需要清除我们需要做到下面的几个效果——

  • 点击全选使之被选中,则A、B、C均被选中
  • 取消全选,A、B、C均取消选中
  • 如果A、B、C都被选中了,则全选框也被选中
  • 只要A、B、C中有一个被取消选择,则全选框也被取消选中

参考HTML代码如下——

     <table>
        <tr>
            <th><input type="checkbox" name="" id="all"><label for="all">全选</label></th>
            <th>商品名</th>
            <th>价格</th>
        </tr>
        <tr>
            <td><input type="checkbox" name="" id="type-a" class="tp"><label for="type-a">A类</label></td>
            <td>1</td>
            <td>666</td>
        </tr>
        <tr>
            <td><input type="checkbox" name="" id="type-b" class="tp"><label for="type-b">B类</label></td>
            <td>2</td>
            <td>555</td>
        </tr>
        <tr>
            <td><input type="checkbox" name="" id="type-c" class="tp"><label for="type-c">C类</label></td>
            <td>3</td>
            <td>444</td>
        </tr>
    </table>

CSS样式如下——

        * {
            margin: 0;
            padding: 0;
            list-style: none;
            text-decoration: none;
            box-sizing: border-box;
        }

        table,
        tr,
        th,
        td {
            border: 2px solid;
            border-collapse: collapse;
        }

        table {
            margin: 30px auto;
        }

        th,
        td {
            height: 40px;
            padding: 0 10px;
        }

        input {
            width: 20px;
        }

上述部分不是我们本章的重点,略过,关键来看JS实现。

首先,获取我们的全选框和三个选择框——

    var cbs = document.querySelectorAll('.tp')
    var cbAll = document.querySelector('#all')

添加全选框单击事件,让所有的其他复选框在全选框选中时选中,取消时取消,即所有checkbox的状态都和全选框状态保持一致——

    cbAll.onclick = function () {
        for (var i = 0; i < cbs.length; i++) {
            // 所有按钮的状态可以由全选按钮的状态统一决定
            cbs[i].checked = this.checked
        }
    }

如此一来,我们已经实现了由全选框向其他复选框的单向控制。

接下来实现由其他复选框向全选框的反向控制。

遍历其他的选择框,绑定单击事件,然后设置一个开关(flag),让所有其他选择框元素来控制这个开关,最后再由开关的状态来决定全选框的选中状态——

    for (var i = 0; i < cbs.length; i++) {
        cbs[i].onclick = function() {
            // 开关
            var flag = true
            // 检查所有按钮
            for (var j = 0; j < cbs.length; j++) {
                if (!cbs[j].checked) {
                    // 只要有一个没被选到,就关闭开关
                    flag = false
                    break
                }
            }
            cbAll.checked = flag
        }
    }

如此一来,我们的复选框的功能就全部实现了。

自定义属性

设置属性

有时,原本的属性并不能完全满足我们的要求,在部分情况下使用已有的属性可能会出现意想不到的错误。

比如,当我们利用for循环为一组元素绑定事件之后,实质上该绑定行为已经将for中的 var i 遍历走完到最后,即 i 的值已经来到循环条件退出的情况了。

此时,如果我们还需要用 elements[i] 的方式获取指定元素,将获取不到当前元素,因为此时的 i = elements.length

想要解决这个问题,我们就可以绑定一个自定义的属性——index,用于在绑定事件之前记录当前元素的索引值。

我们知道,以往想要给一个元素的属性赋值,可以这么做——

e.属性 = '值'

但是,这个操作的前提是该属性是元素e本身具有的内置属性

而自定义属性在设置时,不但要赋值,还需要设置该属性——

e.setAttribute( '属性' , '值' )

获取属性

同样的,在获取属性时,如果该属性为内置属性,则可以这样操作——

e.属性

如果该属性为由 setAttribute() 设置的自定义属性,则可以用与之对应的方式获取该属性——

e.getAttribute( '属性' )

当然,该方式也可以获取内置属性。

移除属性

如果我们不再需要使用我们的自定义属性,则也有对应的移除方式——

e.removeAttribute( '属性' )

H5标准自定义属性格式规范 

自定义属性目的是为了保存并使用数据,有些数据可以保存到页面中而不用保存到数据库中。

但是有些自定义属性很容易引起歧义,不容易判断是元素的内置属性还是自定义属性。

因此,H5规定自定义属性以 data- 开头作为属性名。

此外,H5新增了使用下面两种方式获取自定义属性(以data-index为例)——

e.dataset.index

e.dataset[ 'index' ]

这两种方式只有较新版本才支持,考虑兼容性时还是使用 getAttribute() 较为稳妥。 

案例

Q:完成下面需求(不要求样式一致,重点在JS代码实现)——

A:分析案例——

这里主要讲解JS原理,所以我给出一个简化版的效果——

参考HTML代码——

    <ul class="tabs">
        <li class="current">区域一</li>
        <li class="">区域二</li>
        <li class="">区域三</li>
    </ul>
    <div class="box">
        <div class="content">内容一</div>
        <div class="content">内容二</div>
        <div class="content">内容三</div>
    </div>

参考CSS代码——

        * {
            margin: 0;
            padding: 0;
            list-style: none;
            text-decoration: none;
            box-sizing: border-box;
        }

        .tabs {
            width: 540px;
            height: 90px;
            margin: 30px auto;
            display: flex;
            justify-content: space-between;
        }

        .tabs li {
            width: 160px;
            height: 100%;
            display: flex;
            justify-content: center;
            align-items: center;
            border: 2px solid #ccc;
            background: #fff;
            font-weight: bold;
            font-size: 32px;
            cursor: pointer;
        }

        .tabs li.current {
            background: #f00;
        }

        .box {
            width: 540px;
            height: 200px;
            margin: 0 auto;
            background: #5ad6f5;
        }

        .box .content {
            display: none;
            font-size: 40px;
        }

        .box .content:first-child {
            display: block;
        }

来到JS部分,首先是获取必要的元素(事件源)——

    var tabs = document.querySelector('.tabs')
    var lis = tabs.querySelectorAll('li')
    var box = document.querySelector('.box')
    var contents = box.querySelectorAll('.content')

然后是遍历获取所有事件源(图中的区域),并且在绑定事件之前提前设置好data-index属性——

for (var i = 0; i < lis.length; i++) {
        lis[i].setAttribute("data-index", i)
        // ...
    }

接下来,绑定鼠标单击事件,利用排他操作,为当前选中的元素设置current类名(激活时的样式表)——

    for (var i = 0; i < lis.length; i++) {
        // ...
        for (var j = 0; j < lis.length; j++) {
            lis[j].className = ""
        }
        this.className = "current"
        // ...
    }

最后,还是利用排他操作,仅让当前index对应的内容显示。

完整的for循环如下——

    for (var i = 0; i < lis.length; i++) {
        lis[i].setAttribute("data-index", i)
        lis[i].onclick = function () {
            for (var j = 0; j < lis.length; j++) {
                lis[j].className = ""
            }
            this.className = "current"
            for (var j = 0; j < contents.length; j++) {
                contents[j].style.display = 'none'
            }
            contents[this.dataset.index].style.display = 'block'
        }
    }

节点

在上一期中,我们讲到了DOM树的概念,即网页中的所有内容都是节点(标签、属性、文本、注释等)。

在DOM 中,节点使用 node 来表示,所有节点均可通过 JavaScript 进行访问,所有 HTML 元素(节点)均可被修改,也可以创建或删除。

一般地,节点至少拥有nodeType(节点类型)、nodeName(节点名称)和nodeValue(节点值)这三个基本属性。

常见的节点类型有下面三种——

  • 元素节点 nodeType = 1
  • 属性节点 nodeType = 2
  • 文本节点 nodeType = 3

在我们的实际开发中,更多的是使用元素节点。 

层级

由DOM树从上到下, 不同节点之间存在亲代兄弟关系,即存在层级关系。

父节点

我们把当前节点称为node,想要获取父节点(最近),可以这么做——

node.parentNode

子节点

想要获取子节点,则有下面两种方式——

node.childNodes // 标准方式

node. children // 非标准方式

前者返回的是包含指定节点的子节点的集合,这个集合实时更新,包含所有nodeType的节点,如果只想获取其中的元素节点,则不提倡使用该方式

后者是一个只读属性,返回所有的子元素节点。由于它只返回元素节点,所以很方便。

如果想要返回特定的子元素节点,可以使用类数组的索引方式操作——

parentNode.children[i] 

兄弟节点

获取兄弟节点或仅获取兄弟元素节点(后者存在兼容性问题)的方式有下面四种——

1.nextSibling 下一个兄弟节点 包含元素节点或者 文本节点 等等
        div.nextSibling
        div.previousSibling
2. nextElementSibling 得到下一个兄弟元素节点
        div.nextElementSibling
        div.previousElementSibling

创建节点

此前,我们知道可以通过JS获取HTML元素,同样的,我们也可以将HTML元素节点添加到HTML文档中。但是在此之前,我们需要先讲新的节点创建出来——

document.creatElement( 'tagName' )

其中,tagName是我们的标签名称,与getElementsByTagName()相对应。

该方式也叫做动态创建元素节点

添加节点

添加节点的方式有两种,分别是在父节点的最后一个子节点的末尾添加和在父节点的指定子节点的前面添加,二者的作用类似于 :after 和 :before ——

parentNode.appendChild(child)

parentNode.insertBefore(child, node)

案例

Q:完成一个简单的公屏聊天功能——

A:分析案例——

参考的HTML代码如下——

    <textarea name="" id="" placeholder="输入内容"></textarea>
    <button>发送</button>
    <div class="screen">
        <ul></ul>
    </div>

参考的CSS代码如下——

        * {
            margin: 0;
            padding: 0;
            list-style: none;
            text-decoration: none;
            box-sizing: border-box;
        }

        textarea {
            width: 300px;
            height: 100px;
            padding: 10px;
            resize: none;
        }

        button {
            cursor: pointer;
        }

        .screen {
            width: 300px;
            height: 300px;
            overflow: auto;
            border: 2px solid #ccc;
        }

        ul {
            width: 100%;
        }

        li {
            margin: 10px 10%;
            background: #f3afaf;
        }

来到JS部分,首先还是获取事件源——

    var ta = document.querySelector('textarea')
    var btn = document.querySelector('button')
    var screen = document.querySelector('.screen')
    var ul = screen.querySelector('ul')

随后,绑定按钮的鼠标单击事件,同时检测是否有内容输入,如果输入值为空则弹出警告框。

最后在ul创建子节点并添加。使用appendChild()和insertBefore() 均可——

    btn.onclick = function () {
        if (ta.value == '') {
            alert("请输入内容")
        } else {
            var li = document.createElement('li')
            li.innerHTML = ta.value
            ul.insertBefore(li, ul.children[0])
        }
    }

结束语

本期的内容到这里就结束了,在后续的本系列博客中,我会继续更新js的基础语法知识,并适当地配合上一些案例。

在全栈领域,博主也只不过是一个普通的萌新而已。本系列的博客主要是记录一下自己学习的一些经历,然后把自己领悟到的一些东西总结一下,分享给大家。

文章全篇的操作过程都是笔者亲自操作完成的,一些定义性的文字加入了笔者自己的很多理解在里面,所以仅供参考。如果有说的不对的地方,还请谅解。

==期待与你在下一期博客中再次相遇==

——还在漏气的【H2O2】

 


http://www.kler.cn/news/364091.html

相关文章:

  • 【OpenAI】第六节(语音生成与语音识别技术)从 ChatGPT 到 Whisper 的全方位指南
  • ConcurrentHashMap 线程安全的具体实现方式/底层具体实现
  • 多ip访问多网站
  • Java 多线程(七)—— 定时器
  • 数据结构编程实践20讲(Python版)—19字典树
  • 公交IC卡收单管理系统 assets 信息泄露
  • rabbitmq 使用注意事项
  • JVM 的定义、内部工作原理以及不同 JVM 实现的区别, Oracle JVM 、 OpenJ9、GraalVM对比。
  • 51 单片机[11]:蜂鸣器播放提示音和音乐
  • DNS 原理
  • 证明非平方整数阶射影平面关联矩阵的主对角线有t+1个1
  • Python 爬虫下载图片
  • 将 Docker 安装到指定目录
  • Spring Boot 中常见的注解,分类列出
  • 机房巡检机器人有哪些功能和作用
  • 【数据分析】Power BI的使用教程
  • asp.net core会话session设置滑动过期时间
  • Web3.0技术入门
  • YOLO11改进 | 主干网络 | 简单而优雅且有效的VanillaNet 【华为诺亚方舟】
  • 【rk3568】sg90舵机pwm控制
  • uniapp 获取签名证书 SHA1 自有证书签名打包
  • 什么是Kubernetes?K8s基础与工作原理
  • QMetaObject invokeMethod
  • 长城坦克正式公布全新越野架构Hi4-Z,开启越野新时代
  • STM32之EC800K 4G模块驱动
  • 网络文件系统搭建