JavaScript — DOM API

x33g5p2x  于2022-02-07 转载在 JavaScript  
字(8.8k)|赞(0)|评价(0)|浏览(460)

前言:
JS 分为三大部分,之前已经学习了ES(基础语法部分),本篇来讨论第二部分:DOM API — 操作页面结构

DOM 概念

DOM (Document Object Model),页面文档对象模型
W3C 标准给我们提供了一系列的函数,让我们可以操作:

  • 网页内容
  • 网页结构
  • 网页样式

DOM 树
一个页面的结构是一个树形结构,称为 DOM 树
相当于 html 树形结构,为一个多子树结构(一个节点下可以有多个);每个节点都可以抽象为一个页面文档的对象

DOM API
即:JS 提供的,操作界面元素(节点)的API

事件

基本概念

JS 及 DOM API,都和事件有关
JS 要构建动态页面,就需要感知到用户的行为;如:用户对于页面的一些操作(点击,选择,修改等) 操作都会在浏览器中产生一个个事件,被 JS 获取到,从而进行更复杂的交互操作

时间三要素

  • 事件源: 由哪个元素触发的;如:用户点了一个按钮,这个按钮就是事件源
  • 事件类型: 用户操作的类型,如:点击,选中,修改…
  • 事件处理程序: 事件发生之后,需要做什么?即: 进一步如何处理,往往是一个回调函数(回调函数,事件发生后,由浏览器自动执行里边的代码)

举例:

<input type="button" value="点我呀" onclick="alert('点就点');">

JS 代码主要流程

写 JS 代码,主要流程:
1.事件源: 学习如何选择页面元素 (DOM API 中,把这些页面元素也称为 DOM 元素)
2.调用 DOM 元素的 API 来做事情
做什么事情?
2.1 设置属性(比如style属性就是样式,on开头的属性就是事件属性)
2.2 设置标签内容

获取页面元素

这部分工作类似于 CSS 选择器的功能

querySelector & querySelectorAll

querySelector

之前的获取元素的方式都比较麻烦,而使用 querySelector 能够完全复用前面学过的 CSS 选择
器知识,可以更快更精准的获取到元素对象

querySelector 调用的节点元素下,去查找满足选择器条件的元素,最后 element 就是选择到的元素
var element = document.querySelector(selectors);
.
document: 页面顶级节点
selectors: 字符串内容,格式和 CSS 一样(CSS 选择器可以获取到哪些元素,querySelector 就可以获取到哪些元素)

总结:

  • selectors 包含一个或多个要匹配的选择器的 DOM字符串 DOMString,该字符串必须是有效的
    CSS选择器字符串;如果不是,则引发 SYNTAX_ERR 异常
  • 表示文档中与指定的一组CSS选择器匹配的第一个元素的 html元素 Element 对象.
  • 如果您需要与指定选择器匹配的所有元素的列表,则应该使用 querySelectorAll()
  • 可以在任何元素上调用,不仅仅是 document;调用这个方法的元素将作为本次查找的根元素
    如:

querySelectorAll

用法和 querySelector 类似

举例:
若选择器返回多个元素,需要使用querySelectorAll,返回的是一个数组,其中包含多个元素

<body>
    <p>p1</p>
    <p>p2</p>
    <p>p3</p>
</body>

<script>
    // 如果选择器返回多个元素,需要使用querySelectorAll,返回的是一个数组,其中包含多个元素
    var arr = document.querySelectorAll("p");
    // 遍历数组
    for(let i=0; i<arr.length; i++){
        console.log(arr[i]);
    }
</script>

操作元素

获取 / 修改元素内容

innerHTML 用的场景比 innerText 更多

innerText

Element.innerText 属性表示一个节点及其后代的"渲染"文本内容
读操作 —— var renderedText = HTMLElement.innerText;
写操作 —— HTMLElement.innerText = string;

举例:

<body>
    <div></div>
</body>

<script>
    // 给div 添加内容
    var div = document.querySelector("div");
    div.innerText = "innerText设置内容";
</script>

缺点: 若是修改内容时,带一些 html 的标签作为字符串,此时不会把标签渲染成 html 的元素 (不识别 html 标签)

<script>
    var div = document.querySelector("div");
    div.innerText = "<span>innerText设置内容</span>";
</script>

由上述例子可以看到,通过 innerText 无法获取到 div 内部的 html 结构,只能得到文本内容; 修改页面的时候也会把 span 标签当成文本进行设置,不会渲染为 html 结构

innerHTML

Element.innerHTML 属性设置或获取HTML语法表示的元素的后代
读操作 —— var content = element.innerHTML;
写操作 —— element.innerHTML = htmlString;

<script>
    var div = document.querySelector("div");
    div.innerHTML = "<span>innerHTML设置内容</span>";
</script>

通过 innerHTML 获取到的字符串, 不光能获取到页面的 html 结构,同时也能修改结构;并且获取到的内容保留空格和换行

获取 / 修改元素属性

可以通过 Element 对象的属性来直接修改,就能影响到页面显示效果

举例:
console.dir() 表示,将一个对象中的属性、方法打印出来

<body>
    <img src="xiawen2.jpg" alt="加载失败" title="哈温呐">
</body>
<script>
    var img = document.querySelector("img");
    console.dir(img);
</script>

修改 img 的 src 属性:

<body>
    <img src="xiawen2.jpg" alt="加载失败" title="哈温呐">
</body>
<script>
    var img = document.querySelector("img");
    console.dir(img);
    img.src = "xiawen.jpg";
</script>

打开网页的时候,最开始是 xiawen2.jpg,还没有等我们反应过来时,就已经变成了 xiawen.jpg,因为执行的速度非常快

点击图片,切换为另一张图片:

<script>
    var img = document.querySelector("img");
    img.onclick = function () {
        if (img.src.lastIndexOf('xiawen2.jpg') !== -1) {
            img.src = './xiawen.jpg';
       } else {
            img.src = './xiawen2.jpg';
       }
   }
</script>

获取 / 修改样式属性

CSS 中指定给元素的属性,都可以通过 JS 来修改

行内样式操作
element.style.[属性名] = [属性值];
element.style.cssText = [属性名+属性值];

“行内样式”,通过 style 直接在标签上指定的样式,优先级很高;适用于改的样式少的情况

举例:

<div style="font-size: 20px; font-weight: 700;">一朵花花</div>

点击修改颜色:

// 点击后修改颜色
    var div = document.querySelector('div');
    div.onclick = function () {
        // 获取 div 标签文本颜色
        var curColor = div.style.color;
        if(curColor == '' || curColor == 'black'){
            div.style.color = "red";
        }
        else{
            div.style.color = "black";
        }
    }

类名样式操作
element.className = [CSS 类名];

修改元素的 CSS 类名,适用于要修改的样式很多的情况

夜间模式切换

<style>
    /* 白天模式: 白底黑字 */
    .day {
        background-color: white;
        color: black;
    }
    /* 黑夜模式: 黑底白字 */
    .night {
        background-color: black;
        color: white;
    }
</style>
<body>
    <span class="day">花花呀</span>
</body>

<script>
    var span = document.querySelector("span");
    span.onclick = function(){
        //获取当前点击的 class
        var cls = span.className;
        if(cls == 'day') {
            span.className = "night";
        }
        else{
            span.className = "day";
        }
    }
</script>

由于 class 是 JS 的保留字,所以名字叫做 className

获取 / 修改表单元素属性

表单 (主要是指 input 标签) 的以下属性都可以通过 DOM 来修改

  • value: input 的值
  • disabled:禁用
  • checked:复选框会使用
  • selected:下拉框会使用
  • type:input 的类型(文本,密码,按钮,文件等)

例1 — 切换按钮的文本

<input type="button" id="bf" value="播放">

 // 选择按钮元素,绑定点击事件,切换显示的文版内容
 var btn = document.querySelector("#bf");
 btn.onclick = function(){
     var content = btn.value;
     if(content == '播放') {
         btn.value = '暂停';
     }else{
         btn.value = '播放';
     }
 }

例2 — 点击按钮,文本值+1

<input type="text" id="sum" value="0">
<input type="button" id="sum_btn" value="点我+1">

var sum = document.querySelector("#sum");
var sumBtn = document.querySelector("#sum_btn");
sumBtn.onclick = function(){
    var count = +sum.value; //返回字符串
    console.log(count); // 转换成数值
    sum.value = count + 1;
}

例3 — 全选 / 取消全选按钮

在 HTML 中,属性值设为 “checked” 为选中;而在 JS 中,需要设置为 true / false

<input type="checkbox" id="all">全部选中
<input type="checkbox" class="item">艾希
<input type="checkbox" class="item">凯特琳
<input type="checkbox" class="item">VN

// 全部选择 / 取消
var all = document.querySelector("#all");
all.onclick = function(){
    //子复选框
    var items = document.querySelectorAll(".item");
    if(all.checked){
        for(item of items){
            item.checked = true;
        }
    }  
    else{
        for(item of items){
            item.checked = false;
        }
    }
}

优化用户体验:

var all = document.querySelector("#all");
var items = document.querySelectorAll(".item");
all.onclick = function(){
    console.log(all.checked);
    for(item of items){
        item.checked = all.checked;
    }
}

for(item of items){
    item.onclick = function(){
        // all 复选框是否被选中
        let allChecked = true;
        // 所有子复选框是否被选中
        for(it of items){
            if(!it.checked){
                allChecked = false;
            }
        }
        all.checked = allChecked;
    }
}

操作节点

新增节点

分成两个步骤

  1. 创建元素节点
  2. 把元素节点插入到 DOM 树中
1.创建元素节点

可以直接在最后的元素后创建

<input type="text" id="content">
<input type="button" id="add" value="内容">
<div id="container">
    <h3>内容</h3>
</div>

var add = document.querySelector("#add");
var content  = document.querySelector("#content");
var container  = document.querySelector("#container");
add.onclick = function(){
    // 点击,获取文本框内容
    var text = content.value;
    // innerHTML ,先获取 container 中的所有元素,在最后添加元素
    var html = container.innerHTML;
    html += "<p>";
    html += text;
    html += "</p>";
    container.innerHTML = html;    
}

但以上修改 container 中的所有内容,效率比较差,已有的标签已经渲染了,重新设置又会再次渲染
可以使用 createElement 方法来创建一个元素
var element = document.createElement(tagName[, options]);

var add = document.querySelector("#add");
var content  = document.querySelector("#content");
var container  = document.querySelector("#container");
add.onclick = function(){
    var text = content.value;
    // 方式2
    // 创建一个dom元素(<p>),然后添加到 container 中,作为最后一个子节点
    var p = document.createElement("p"); // 创建一个元素
    p.innerHTML = text; 
    container.appendChild(p); //添加到dom树形结构中,作为最后一个子节点
}
2.插入节点到 DOM 树中
  • 使用 appendChild 将节点插入到指定节点的最后一个孩子之后
    element.appendChild(aChild)
    .
    DOM 包含的子节点不动,在最后添加 element 节点,效率比较高

仍以上述为例:

container.appendChild(p); —— 添加到dom树形结构中,作为最后一个子节点
  • 使用 insertBefore 将节点插入到指定节点之前
    var insertedNode = parentNode.insertBefore(newNode, referenceNode);
    .
    含义: 在 parentNode 节点中,有一个 insertedNode 的子节点,在这个子节点前,插入一个 newNode 节点

  • insertedNode 被插入节点(newNode)

  • parentNode 新插入节点的父节点

  • newNode 用于插入的节点

  • referenceNode newNode 将要插在这个节点之前

如果 referenceNode 为 null 则 newNode 将被插入到子节点的末尾
注意: referenceNode 引用节点不是可选参数

补充:
DOM 对象,其中包含属性:

  • dom.children — 返回 DOM 对象下一级左右的子节点数组
  • dom.parentNode — 返回该 DOM 对象上一级的父节点
<div id="insertBeforeDiv">
    <p>11111</p>
    <p>22222</p>
    <p>33333</p>
    <p>44444</p>
</div>

// insertBefore 学习
var insertBeforeDiv = document.querySelector("#insertBeforeDiv");
// 准备要插入的节点
var insertNode = document.createElement("p");
insertNode.innerHTML = "新插入节点";
insertBeforeDiv.insertBefore(insertNode,  insertBeforeDiv.children[0]);

如果节点是页面已存在的,就做移动操作

<p id="beInsert">原P标签</p>
<ul>
    <li>
        <p>p111</p>
    </li>
    <li>
        <p>p222</p>
    </li>
</ul>
    
var beInsert = document.querySelector("#beInsert");
var ul = document.querySelector("ul");
// 构造一个 li 标签
var li = document.createElement("li");
li.appendChild(beInsert); // <li><p>beInsert</p></li>
ul.insertBefore(li,ul.children[0]);

删除节点

使用 removeChild 删除子节点
oldChild = element.removeChild(child);
.
含义: element 作为父节点,删除里边的 child 子节点,返回值 oldChild,作为已经删除的节点,还可以继续使用.
.
① child 为待删除节点
② element 为 child 的父节点
③ 返回值为该被删除节点
④ 被删除节点只是从 dom 树被删除了,但是仍然在内存中,可以随时加入到 dom 树的其他位
置.
⑤ 若 child节点 不是 element 节点的子节点,则该方法会抛出异常

举例: 删除 ul 中的最后一个 li

var last = ul.children[ul.children.length - 1]; // 取最后一个 li 节点
ul.removeChild(last);

相关文章