JavaScript - DOM

DOM

1 DOM是什么

DOM(Document Object Model,文档对象模型),是用来呈现以及与任意 HTML 或 XML文档交互的API(Application Program Interface,应用程序接口)。DOM 是载入到浏览器中的文档模型,以节点树的形式来表现文档,每个节点代表文档的构成部分(例如:页面元素、字符串或注释等等)。

DOM 是万维网上使用最为广泛的 API 之一,它允许运行在浏览器中的代码访问文件中的节点并与之交互。节点可以被创建,移动或修改。事件监听器可以被添加到节点上并在给定事件发生时触发。

DOM 并不是天生就被规范好了的,它是浏览器开始实现 JavaScript 时才出现的。这个传统的 DOM 有时会被称为 DOM 0。现在, WHATWG 维护 DOM 现存标准。

2 DOM 树

DOM 树又叫文档树模型,把文档映射成树形结构,通过节点对象对其处理,处理的结果可以加入到当前的页面。

文档:一个 HTML 页面就是一个文档,使用 document 表示

<html>
  <head>
    <title>DOM Tutorial</title> 
  </head> 
  <body> 
    <h1>DOM Lesson one</h1> 
    <p>Hello world!</p> 
  </body> 
</html>

上面所有的节点彼此间都存在关系

除文档节点之外的每个节点都有父节点。大部分元素节点都有子节点。比方说, 节点有一个子节点:当节点分享同一个父节点时,它们就是同辈(同级节点)。节点也可以拥有后代,后代指某个节点的所有子节点,或者这些子节点的子节点,以此类推。节点也可以拥有先辈。先辈是某个节点的父节点,或者父节点的父节点,以此类推。

3 节点

节点是构成网页最基本的组成部分,网页中的所有内容在文档树中都是节点(标签、属性、文本、注释等),使用node表示

  • 标签节点:也叫元素节点,指网页中的所有标签,使用element表示
  • 属性节点:指元素节点的属性
  • 文本节点:指元素节点的内容

4 获取元素

想要对 DOM 进行操作,首先需要获取页面中的元素,在 JavaScript 中,提供了以下几种获取页面元素的方法。

4.1 根据id获取

语法格式:

var 元素对象 = document.getElementById("id属性名称");

作用:通过页面中某个元素的 id 属性来获取这个元素对象。

返回 值:这个方法执行后会有一个返回值,如果获取到则返回当前对象,否则返回 null。

示例:

<div id="box">这是一个块</div>
<script>
    var box = document.getElementById('box')
    console.log(box);
    console.log(typeof box);
</script>

4.2 根据标签名获取

语法格式:

var 返回对象集 = document.getElementsByTagName('标签名称');

作用:根据指定的标称名称返回这些对象。

示例:

<ul>
    <li>北京</li>
    <li>天津</li>
    <li>上海</li>
    <li>重庆</li>
</ul>
<script>
    var lis = document.getElementsByTagName('li')
    console.log(lis);
</script>

除了可以使用 document 来引用这个方法外,还可以使用它父组对象来引用这个方法。

<ul id="first">
    <li>北京</li>
    <li>天津</li>
    <li>上海</li>
    <li>重庆</li>
</ul>
<ul id="last">
    <li>西安</li>
    <li>广州</li>
    <li>四川</li>
    <li>青海</li>
</ul>
<script>
    var lis = document.getElementsByTagName('li')
    console.log(lis);

    // 获取父级元素
    var ul = document.getElementById('first')
    console.log(ul);

    // 通过父级元素来获取子元素
    lis = ul.getElementsByTagName('li');
    console.log(lis);
</script>

4.3 根据 name 属性来获取

语法格式:

var 元素对象集 = document.getElementsByName('name属性名')

作用:根据 name 属性来获取元素对象的集合

示例:

<p>
<input type="checkbox" name="hobby" value="音乐">音乐
<input type="checkbox" name="hobby" value="游戏">游戏
</p>
<script>
    var hobbies = document.getElementsByName('hobby')
    console.log(hobbies);
</script>

4.4 根据类名来获取

语法格式:

var 元素对象集 = document.getElementsByClassName('类名称');

作用:根据元素的样式名称来获取元素集(也就是元素的 class 属性来获取)。

示例:

<div class="box">div1</div>
<div class="box">div2</div>
<script>
    var divs = document.getElementsByClassName('box')
    console.log(divs);
</script>

4.5 根据选择器获取

根据指定选择器获取元素对象有两种方式:一是获取单个,一是获取多个。

获取单个对象:

var 元素对象 = document.querySelector('选择器名称');

注意:这个方法执行后只会返回一个对象。

获取多个对象:

var 元素对象 = document.querySelectorAll('选择器名称');

示例:

<div id="box1">有id的div</div>
<div class="box2">有类样式的div</div>
<div class="box2">有类样式的div</div>
<p>
    <input type="text" name="account" value="xian">
    <input type="radio" name="gender" value="男">男
    <input type="radio" name="gender" value="女">女
</p>
<script>
    // 根据标签获取
    var div = document.querySelector('div')
    console.log(div);
    var divs = document.querySelectorAll('div')
    console.log(divs);

    // 根据id获取
    div = document.querySelector('#box1')
    console.log(div);

    // 根据类样式
    divs = document.querySelectorAll('.box2')
    console.log(divs);

    // 根据属性选择器来获取
    var gender = document.querySelectorAll('input[type="radio"]')
    console.log(gender);
</script>

4.6 获取body元素

语法格式:

document.body;

作用:获取body中的所有元素

示例:

<div class="box"></div>
<input type="text" name="" id="">
<h1>h1</h1>
<script>
    //var bd = document.querySelectorAll('body')
    //console.log(bd);

    var body = document.body;
    console.log(body);
</script>

4.7 获取html元素

语法格式:

document.documentElement;

作用:获取整个html元素

示例:

<div class="box"></div>
<input type="text" name="" id="">
<h1>h1</h1>
<script>
    var html = document.documentElement;
    console.log(html);
</script>

5 操作元素

JavaScript 的 DOM 操作可以改变网页内容、结构和样式,因此可以利用 DOM 操作元素来改变元素里面的内容、属性等。

5.1 改变内容

在 DOM 中改变元素内容通常使用元素的 innerText 和 innerHTML 属性来实现。

属性 使用 说明
innerText element.innerText 获取或设置去除html标签、空格和换行后的元素内容
innerHTML element.innerHTML 获取或设置包括html标签、空格和换行后的元素内容

演示示例:

<div class="box">
    今年是<strong>2022</strong>年第一天
</div>
<script>
    // 1. 获取元素
    var box = document.querySelector('.box')
    console.log(box);
    // 2. 获取元素的内容
    // 2.1 innerHTML
    var content = box.innerHTML
    console.log(content);
    // 2.2 innerText
    content = box.innerText
    console.log(content);

    // 3. 设置元素内容
    // 3.1 innerHTML
    box.innerHTML = '<font color="red">这是红色的字</font>';
    // 3.2 innerText
    box.innerText = '<font color="red">这是红色的字</font>';
</script>

总结:

  1. 使用对象中的innerHTML或innerText属性可以获取对象的内容。
  2. 通过给对象的innerHTML或innerText属性赋值可以改变对象的内容。
  3. innerHTML属性会把内容中的 html 元素解析后再执行,而 innerText 不会解析内容中的 html 元素。
  4. 如果 innerText 用于获取对象的内容,当内容中包含有 html 元素时, 获取中它会把这个元素给删除。
  5. 如果 innerText 用于设置对象的内容,当内容中包含有 html 元素时,它会把这些元素原封不动的设置给对象。

5.2 元素属性

在 DOM 中,改变元素属性是通过元素对象.属性名的方式来实现的,它同样具有读写功能。元素可操作的常用属性有:src、href、id、alt、title以及自定义属性等。

语法 说明
元素对象.属性名 获取元素指定属性的值
元素对象.属性名 = 值 设置元素指定属性的值

5.3 表单元素

在 Web 开发中,表单是一种常用的元素,利用 DOM 可以操作如下表单元素的属性。

属性 说明
type 表单元素类型
value 表单元素值
checked 表单元素选中状态
selected 表单元素选择状态
disabled 表单元素禁用开关

这些属性都具有读写操作,通过元素对象.属性名来获取元素属性值,通过元素对象.属性名=值来设置元素属性值。而 checked、selected和disabled 元素属性的值是布尔类型。

演示案例:

<input type="text" name="name" value="123456"><br>
<input type="checkbox" name=""><br>
<select name="">
    <option value="">--请选择--</option>
    <option value="北京">北京</option>
    <option value="上海">上海</option>
    <option value="重庆">重庆</option>
</select>
<script>
    var input = document.querySelector('input')
    // 获取type
    console.log(input.type);
    // 设置type
    //input.type = 'password';

    // 获取value
    console.log(input.value);
    // 设置value
    input.value = 'jock'

    // 获取disabled
    console.log(input.disabled); // false
    // 设置 disabled
    input.disabled = true;

    // 获取元素
    var ck = document.querySelector('[type="checkbox"]')
    console.log(ck);
    // 获取选中状态
    console.log(ck.checked); // false
    // 设置选中状态
    ck.checked = true;

    // 获取下拉列表
    var sl = document.querySelector('select')
    console.log(sl);
    //console.log(sl[2]);
    var op = sl[2];
    console.log(op.selected);
    // 设置被选中
    op.selected = true;
</script>

5.4 样式元素

在 DOM 中可以通过 JavaScript 来修改元素的大小、颜色和位置等样式。常用方式有:

属性 使用 说明
style element.style 操作行内样式
className element.className 操作类名样式

5.4.1 style

示例:

<head>
    <meta charset="UTF-8">
    <title>使用style方式操作样式</title>
    <style>
        .box {
            width: 150px;
            height: 100px;
            background-color: blueviolet;
        }
    </style>
</head>
<body>
<div class="box"></div>
<script>
    // 通过 JS 来改变 div 的宽和背景颜色
    var box = document.querySelector('.box')
    //console.log(box);
    // 通过 style 方式
    box.style.width = '300px';
    box.style.backgroundColor = 'red';
</script>
</body>

注意:如果样式名称中包含有连接符(-),那么连接符要去掉,同时后面的字母要大写。

5.4.2 className属性

示例:

<head>
    <meta charset="UTF-8">
    <title>使用style方式操作样式</title>
    <style>
        .box {
            width: 150px;
            height: 100px;
            background-color: blueviolet;
        }
        .bg {
            width: 200px;
            height: 200px;
            background-color: cornflowerblue;
        }
        .ft {
            color: white;
        }
    </style>
</head>
<body>
<div class="box">这是文字</div>
<script>
    // 通过 JS 来改变 div 的宽和背景颜色
    var box = document.querySelector('.box')
    console.log(box.className);
    box.className = 'bg ft'
</script>
</body>

说明:

  1. 通过 对象.className 的方式可以获取当前对象的样式名称
  2. 通过 对象.className='值' 的方式可以给当前对象设置样式

5.5 操作属性

对属性的操作也可以设置属性值,也可获取属性值。

5.5.1 setAtrribute()

这个方法可以给元素设置属性。它的语法如下:

对象名称.setAttribute('属性名', '属性值');

示例:

<head>
    <meta charset="UTF-8">
    <title>属性方法操作</title>
    <style>
        div {
            width: 100px;
            height: 30px;
            border: 1px solid #000000;
        }
        .bg {
            background-color: cornflowerblue;
            color: white;
        }
    </style>
</head>
<body>
<div title="这是标题">这是内容</div>
<script>
    var div = document.querySelector('div')
    // 设置属性
    div.setAttribute('class', 'bg')
</script>

5.5.2 getAttribute()

这个方法是用于获取对象中的指定属性名称的值。它的语法如下:

属性值 = 对象.getAttribute('属性名')

示例:

<div title="这是标题">这是内容</div>
<script>
    var div = document.querySelector('div')
    // 获取title属性
    //console.log(div.title);
    var attrValue = div.getAttribute('title')
    console.log(attrValue);
</script>

5.5.3 removeAttibute()

这个方法是用于删除对象中指定的属性。它的语法为:

对象.removeAttribute('属性名');

示例:

<div title="这是标题">这是内容</div>
<script>
    var div = document.querySelector('div')

    // 删除title属性
    div.removeAttribute('title')
</script>

5.5.4 自定义属性

自定义属性目的是为了保存并使用临时数据,这些数据不需要从数据库中获取。自定义属性是通过 setAttribute('属性', '值') 来设置,通过 getAttribute('属性') 来获取。

由于有些自定义属性很容易引起歧义,不容易判断是元素的内置属性还是自定义属性,为了解决这种问题,H5 中规定了自定义属性都以 data- 开头来作为属性名。

H5 中规定的属性定义方式可以使用 getAttribute('属性') 方式来获取,也可以使用 H5 中新增的属性获取方式来获取。

获取方式 返回
element.dataset.属性 属性值
element.dataset['属性'] 属性值

使用示例:

<div data-index="1" data-list-name="jock"></div>
<script>
    var div = document.querySelector('div');
    console.log(div.getAttribute('data-index'));
    div.setAttribute('data-title', '标题')
</script>

6 DOM事件

6.1 什么是事件

所谓事件是指文档或浏览器窗口中发生的一些特定的交互瞬间。JavaScript 与 HTML 之间的交互就是通过事件来实现的。对于 Web 应用来说,鼠标点击,鼠标移动,按下键盘某个键都是属于事件。而这些相应操作就会触发相应的响应机制来进行事件处理。

6.2 事件的要素

对于事件来说,它包含三个要素:事件源、事件类型和事件处理程序。

  • 事件源:触发事件的元素
  • 事件类型:触发了哪种类型的事件,如click、mouseover、focus、keyup等。
  • 事件处理程序:事件触发后要执行的程序代码,通常是一个函数。

示例:

<button id="btn">点我会触发事件</button>
<script>
    // 1. 获取元素
    var btn = document.querySelector('#btn')
    // 1.事件源:这个按钮就是事件源
    // 2. 事件类型:这个按钮要点击后才会触发,那么它的事件类型就是点击事件
    // 3. 事件处理程序
    btn.onclick = function () {
        console.log('你点了我');
    };
</script>

6.3 常见鼠标事件类型

DOM 事件非常多,而这些事件中使用最多最频繁的就是鼠标事件,常用的鼠标事件如下表所示。

鼠标事件 触发条件
onclick 点击鼠标左键时触发
ondblclick 鼠标双击时触发
onmouseover 鼠标经过时触发
onmouseout 鼠标离开时触发
onmousemove 鼠标移动时触发
onmoseup 鼠标按键弹起时触发
onmousedown 鼠标按键按下时触发
onfocus 获取鼠标焦点时触发
onblur 失去鼠标焦点时触发
onchange 文本框内容改变时触发
onselect 文本框内容被选中时触发
onload 网页加载时触发

7. 节点操作

7.1 节点概述

网页中的所有内容都是节点(如:标签、属性、文本、注释等),在DOM 中,节点使用 node 来表示。HTML DOM 树中的所有节点均可通过 JavaScript 进行访问,所有 HTML 元素(节点)均可被修改、创建或删除。

7.2 节点属性

一般来说节点至少拥有 nodeType(节点类型)、nodeName(节点名称)和 nodeValue(节点值)这三个基本属性。如下表:

节点分类 nodeName(节点名称) nodeType(节点类型) nodeValue(节点值)
文档节点 #document 9 null
元素节点 标签名 1 null
属性节点 属性名 2 属性值
文本节点 #text 3 文本内容
注释节点 #comment 8 注释内容

7.3 父级节点

在 DOM 中,父级节点使用 parentNode 属性来获取,语法为:

node.parentNode

parentNode 属性可返回指定节点最后的一个父节点,如果指定节点没有父节点,则返回 null。

<div class="container">
    <div class="nav">
        <a href="">首页</a>
    </div>
</div>
<script>
    var a = document.querySelector('a')
    console.log(a.nodeName + '\t' + a.nodeType);

    // 获取父级节点
    var pn = a.parentNode; // div.nav
    console.log(pn);

    var gpn = pn.parentNode // div.container
    console.log(gpn);

    var body = gpn.parentNode // body
    console.log(body);

    var html = body.parentNode // html
    console.log(html);

    var doc = html.parentNode // document
    console.log(doc);

    var v = doc.parentNode // null
    console.log(v);
</script>

说明:从上面的运行结果可以发现,当通过对象的 parentNode 属性来获取父级节点时,如果返回的是 null,则表示没有父级元素,否则返回的就是父级元素。

7.4 子级节点

7.4.1 所有子节点

通过 childNodes 属性可以获取指定节点的子节点集合,在这些子节点中包括元素节点和文本节点。

<ul>
    <li>北京</li>
    <li>上海</li>
    <li>天津</li>
    <li>重庆</li>
</ul>
<script>
    var ul = document.querySelector('ul')
    //console.log(ul.childNodes);
    var childs = ul.childNodes;
    for (var i = 0; i < childs.length; i++) {
        if (childs[i].nodeType === 3) continue;
        console.log(childs[i]);
    }
</script>

注意:使用 childNodes 属性来获取子节点时,它会把换行也当作是一个子节点。所以这种方式来获取时需要进行判断。

7.4.2 子元素节点

如果想获取子元素节点而不包含文本节点,那么可以使用 children 属性来实现。children 属性是一个只读属性,是非标准的,但没有兼容器问题。

<ul>
    <li>北京</li>
    <li>上海</li>
    <li>天津</li>
    <li>重庆</li>
</ul>
<script>
    var ul = document.querySelector('ul')
    var childs = ul.children;
    console.log(childs);
</script>

2.4.3 第一个节点

如果想获取指定节点的第一个子节点,可以使用 firstChild 属性,该属性会返回第一个子节点,如果没有子节点则返回 null,但它也可能返回一个文本节点。

<ul>
    <li>北京</li>
    <li>上海</li>
    <li>天津</li>
    <li>重庆</li>
</ul>
<script>
    var ul = document.querySelector('ul')
    var fc = ul.firstChild
    console.log(fc);
</script>

注意:这个属性也把换行当作是一个子节点。

7.4.4 最后一个节点

获取指定节点的最后一个子节点,可以使用 lastChild 属性,该属性会返回第一个子节点,如果没有子节点则返回 null,但它也可能返回一个文本节点。

<ul>
    <li>北京</li>
    <li>上海</li>
    <li>天津</li>
    <li>重庆</li>
</ul>
<script>
    var ul = document.querySelector('ul')
    var lc = ul.lastChild
    console.log(lc);

    lc = ul.children[ul.children.length - 1]
    console.log(lc);
</script>

注意:lastChild 也会把换行当作是子节点。

7.4.5 案例

需求:实现下拉菜单

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>案例</title>
    <style>
        * {margin: 0; padding: 0;}
        li {list-style-type: none;}
        a {text-decoration: none; font-size: 14px;}
        .menu {margin: 100px;}
        .menu>li {
            position: relative;
            width: 100px;
            height: 40px;
            text-align: left;
            float: left;
        }
        .menu>li>a {
            display: block;
            width: 100%;
            height: 100%;
            line-height: 40px;
            color: #333;
            padding-left: 5px;
            background: #f5f5f5;
        }
        .menu>li>a:hover {
            background-color: #ffffff;
        }
        .menu ul {
            display: none;
            position: absolute;
            top: 40px;
            left: 0;
            width: 100%;
        }
        .menu>li>ul>li a {
            display: block;
            width: 100%;
            height: 100%;
            line-height: 40px;
            color: #333;
            padding-left: 5px;
            background: #ffffff;
            border: 1px solid #f5f5f5;
            border-top: none;
        }
        .menu ul li a:hover {
            background-color: #f5f5f5;
        }
    </style>
</head>
<body>
<ul class="menu">
    <li>
        <a href="">我的淘宝</a>
        <ul>
            <li><a href="">已买到的宝贝</a></li>
            <li><a href="">我的足迹</a></li>
        </ul>
    </li>
    <li>
        <a href="">收藏夹</a>
        <ul>
            <li><a href="">收藏的宝贝</a></li>
            <li><a href="">收藏的店铺</a></li>
        </ul>
    </li>
    <li>
        <a href="">联系客服</a>
        <ul>
            <li><a href="">消费者客服</a></li>
            <li><a href="">卖家客服</a></li>
        </ul>
    </li>
    <li>
        <a href="">网站导航</a>
        <ul>
            <li><a href="">主题市场</a></li>
            <li><a href="">特色市场</a></li>
        </ul>
    </li>
</ul>
<script>
    var menu = document.querySelector('.menu')
    var lis = menu.children
    for (var i = 0; i < lis.length; i++) {
        lis[i].onmouseover = function () {
            this.children[1].style.display = 'block';
        };
        lis[i].onmouseout = function () {
            this.children[1].style.display = 'none';
        };
    }
</script>
</body>
</html>

7.5 获取兄弟节点

使用 nextSibling 属性可以获取指定节点的下一个兄弟节点,找不到就返回 null。而通过 previousSibling 属性可以获取指定节点的前一个兄弟节点,找不到也返回 null。

属性 说明
nextSibling 获取指定节点下一个兄弟节点,找不到返回 null
previousSibling 获取指定节点前一个兄弟节点,找不到返回 null
nextElementSibling 获取指定节点下一个兄弟元素节点,找不到返回 null
previousElementSibling 获取指定节点前一个兄弟元素节点,找不到返回 null

示例:

<ul>
    <li>北京</li>
    <li>上海</li>
    <li>天津</li>
    <li>重庆</li>
</ul>
<script>
    var firstLi = document.querySelector('li')
    console.log(firstLi);

    // 使用 nextSibling 来获取下一个兄弟节点
    var next = firstLi.nextSibling
    console.log(next);
    // 使用 nextElementSibling 来获取下一个兄弟节点
    next = firstLi.nextElementSibling
    console.log(next);

    // 使用 previousSibling 获取前一个兄弟节点
    var prev = next.previousSibling
    console.log(prev);
    // 使用 previousElementSibling 来获取下一个兄弟节点
    prev = next.previousElementSibling
    console.log(prev);
</script>

说明:在以后开发中,我们基本上都是使用 nextElementSibling 和 previousElementSibling 属性来获取兄弟节点。

7.6 创建节点

创建节点通过document.createElement('tagName') 语法来创建。targName 是指要创建的 HTML 元素。

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

这样就创建了一个 li 节点,但该节点只是在内在中创建出来了,还需要通过下面的方法添加到页面中。

7.7 添加节点

添加节点的方法有两种如下表所示。

方法 说明
parentNode.appendChild(child) 将指定节点添加到指定父节点的子节点末尾
parentNode.insertBefore(child, element) 将指定节点添加到指定父节点的指定子节点前面

使用示例:

<ul>
    <li>北京</li>
</ul>
<script>
    var ul = document.querySelector('ul');

    // 创建节点通过 document.createElement('tagName'); 执行后会返回这个元素
    var li = document.createElement('li');
    var text = document.createTextNode('重庆') // 创建文本节点
    li.appendChild(text)

    // 添加到 ul 节点中
    ul.appendChild(li);

    li = document.createElement('li')
    li.innerHTML = '上海';
    ul.insertBefore(li, ul.children[1]);
</script>

添加元素还有以下两个方法:

  1. append(element):把指定的元素添加到指定的父元素的最后,没有返回值
  2. prepend(element):把指定的元素添加到指定的父元素的最前面,没有返回值
    li = document.createElement('li');
    li.innerText = '天津'
    ul.append(li)

    li = document.createElement('li');
    li.innerText = '西安'
    ul.prepend(li)

7.8 复制节点

复制节点也叫克隆节点,需要使用 cloneNode() 方法来实现。该方法执行后会返回调用该方法节点的一个副本。该方法可以传一个布尔类型参数,默认为 false 表示浅拷贝,如果参数为 true 表示深拷贝。浅拷贝只会复制节点元素,不会复制节点内容。而深拷贝会复制节点以及所有的子节点。

语法格式:

克隆的副本 = srcNode.cloneNode([false|true]);

演示示例:

<ul>
    <li>北京</li>
    <li>上海</li>
    <li>天津</li>
    <li>
        <ul>
            <li>重庆</li>
            <li>渝北</li>
        </ul>
    </li>
</ul>
<script>
    // 需求:克隆 ul
    // 1. 获取被克隆元素
    var ul = document.querySelector('ul');
    // 2. 调用 cloneNode() 方法进行克隆
    var newNode = ul.cloneNode(false);
    //console.log(newNode);

    // 把新的节点添加到 body 中
    document.body.prepend(newNode);

    // 3. 深拷贝
    newNode = ul.cloneNode(true);
    document.body.prepend(newNode);
</script>

7.9 案例实战

需求:动态生成表格

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>节点案例</title>
    <style>
        table {
            width: 500px;
            margin: 100px auto;
            border-collapse: collapse;
            text-align: center;
        }
        th, td {
            height: 30px;
            border: 1px solid #eee;
        }
        thead tr {
            height: 40px;
            background-color: #f8f8f8;
        }
    </style>
</head>
<body>
<table>
    <thead>
    <tr>
        <th>姓名</th>
        <th>年龄</th>
        <th>武力</th>
        <th>操作</th>
    </tr>
    </thead>
    <tbody>
    </tbody>
</table>
<script>
    var data = [
        {name: '刘备', age: 20, score: 10},
        {name: '关羽', age: 19, score: 98},
        {name: '张飞', age: 18, score: 90},
        {name: '赵云', age: 17, score: 90}
    ];
    // 获取 tbody
    var tbody = document.querySelector('tbody');
    // 循环创建 tr,因为一个 tr 表示一行数据
    for (var i = 0; i < data.length; i++) {
        var tr = document.createElement('tr');
        tbody.appendChild(tr);
        // 循环获取所有列的内容,遍历对象使用 for(变量名称 in 对象) 来循环。
        //console.log(data[i]);
        for (var k in data[i]) {
            //console.log(person);
            //console.log(data[i][k]);
            // 创建单元格
            var td = document.createElement('td');
            // 给单元格添加内容
            td.innerText = data[i][k];
            // 把单元格添加到行标签中
            tr.appendChild(td);
        }
        // 创建操作列
        var td = document.createElement('td');
        td.innerHTML = '<a href="">删除</a>';
        tr.append(td);
    }
</script>
</body>
</html>

7.10 删除节点

删除节点需要使用 removeChild(child) 方法来实现。该方法是从指定节点中删除一个子节点,并返回被删除的节点。

语法格式:

父级节点.removeChild(子节点);

示例:完成上节案例中的删除功能。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>删除节点</title>
    <style>
        table {
            width: 500px;
            margin: 100px auto;
            border-collapse: collapse;
            text-align: center;
        }
        th, td {
            height: 30px;
            border: 1px solid #eee;
        }
        thead tr {
            height: 40px;
            background-color: #f8f8f8;
        }
    </style>
</head>
<body>
<table>
    <thead>
    <tr>
        <th>姓名</th>
        <th>年龄</th>
        <th>武力</th>
        <th>操作</th>
    </tr>
    </thead>
    <tbody>
    </tbody>
</table>
<script>
    var data = [
        {name: '刘备', age: 20, score: 10},
        {name: '关羽', age: 19, score: 98},
        {name: '张飞', age: 18, score: 90},
        {name: '赵云', age: 17, score: 90}
    ];
    var tbody = document.querySelector('tbody');
    var tr = null, td = null;
    for (var i = 0; i < data.length; i++) {
        tr = document.createElement('tr');
        tbody.appendChild(tr);
        for (var k in data[i]) {
            td = document.createElement('td');
            td.innerText = data[i][k];
            tr.appendChild(td);
        }
        td = document.createElement('td');
        td.innerHTML = '<a href="javascript:;">删除</a>';
        tr.append(td);
    }

    // 获取所有的 a 标签
    var as = document.querySelectorAll('a');
    //console.log(as);
    for (var i = 0; i < as.length; i++) {
        as[i].onclick = function () {
            //console.log(this)
            //console.log(this.parentNode)
            //console.log(this.parentNode.parentNode)
            tbody.removeChild(this.parentNode.parentNode);
        };
    }
</script>
</body>
</html>

7.11 修改节点

修改节点需要使用 replaceChild() 方法,该方法有两个参数,第一个参数是新节点,第二个参数是被替换的子节点。这个方法执行后,会返回被替换的子节点。

语法格式:

父级节点.replaceChild(新节点, 被替换的子节点);
replaceChild<T extends Node>(node: Node, child: T): T;

使用示例:

<ul>
    <li>北京</li>
    <li>上海</li>
    <li>天津</li>
    <li>重庆</li>
</ul>
<script>
    var ul = document.querySelector('ul');
    var li = document.createElement('li');
    li.innerHTML = '西安';

    ul.replaceChild(li, ul.children[0]);
</script>

注意:替换节点还可以使用 replaceChildren() 方法,它的参数是一个可变参数,可以传多个值,执行后的结果为:原节点中的内容全部被替换为指定的参数列表中的内容。

8. 位置操作

8.1 偏移量

8.1.1 offset

offset 就是偏移量,使用 offset 系列相关属性可以动态得到该元素的位置(偏移)、大小等。

  1. 获取元素距离带有定位父元素的位置
  2. 获取元素自身大小(宽度和高度)
属性 作用
element.offsetParent 返回作为该元素带有定位的父级元素,如没有父级元素则返回 body
element.offsetTop 返回元素相对带有定位父元素上方的偏移
element.offsetLeft 返回元素相对带有定位父元素左边的偏移
element.offsetWidth 返回自身包括padding、边框、内容区的宽度,返回数值无单位
element.offsetHeight 返回自身包括padding、边框、内容区的高度,返回数值无单位

说明:

  1. offsetWidth的计算公式为:border-left + padding-left + 内容宽度 + padding-right + border-right;
  2. offsetHeight的计算公式:border-top + padding-top + 内容高度 + padding-bottom + border-bottom;
  3. offsetTop:它是根据自身的 margin-top 的值 + 带有定位父级的内边距来确定的;
  4. offsetLeft:它是根据自身的 margin-left 的值 + 带有定位父级的内边距来确定的;

offsetParent

1)元素自身有 fixed 定位,offsetParent 是 null

<div id="box" style="position: fixed;"></div>
<script>
    var box = document.querySelector('#box');
    console.log(box.offsetParent);
</script>

2)元素自身没有定位或者是相对或绝对,offsetParent 是 body

<div id="box">aaa</div>
<script>
    var box = document.querySelector('#box');
    console.log(box.offsetParent);
</script>

3)元素自身没有定位,但父级元素有定位,offsetParent 是最近的定位元素。

<div id="father" style="position: relative;">
    <div>
        <div id="box"></div>
    </div>
</div>
<script>
    var box = document.querySelector('#box');
    console.log(box.offsetParent);
</script>

4)body 元素的 offsetParent 是 null

console.log(document.body.offsetParent);

其它属性的使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>offset其它属性操作</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
        .container {
            position: relative;
            width: 300px;
            height: 300px;
            background-color: blueviolet;
            margin: 150px;
        }
        .box {
            width: 150px;
            height: 150px;
            background-color: purple;
            margin-left: 50px;
            padding: 10px;
            border: 15px solid red;
        }
    </style>
</head>
<body>
<div class="container">
    <div class="box"></div>
</div>
<script>
    var container = document.querySelector('.container')
    var box = document.querySelector('.box')

    console.log(container.offsetTop); // 150 => margin: 150px
    console.log(container.offsetLeft); // 150 => margin: 150px
    console.log(container.offsetWidth); // 300 = 0 + 0 + 300 + 0 + 0
    console.log(container.offsetHeight); // 300  = 0 + 0 + 300 + 0 + 0

    console.log(box.offsetTop); // 0
    console.log(box.offsetLeft); // 50  =>  margin-left: 50px;
    console.log(box.offsetWidth); // 200 => 15 * 2 + 10 * 2 + 150
    console.log(box.offsetHeight); // 200 => 15 * 2 + 10 * 2 + 150
</script>
</body>
</html>

8.1.2 计算当前元素在页面的偏移量

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>计算当前元素在页面的偏移量</title>
    <style>

        * {
            margin: 0;
            padding: 0;
        }
        #grandfather{
            border: 5px solid red;
            padding: 30px;
            position: absolute;
        }
        #father{
            padding: 20px;
            border: 1px solid #000;
            position: relative;
        }
        #son{
            width: 100px;
            height: 100px;
            margin: 10px;
            background-color: red;
        }
    </style>
</head>
<body>
<div id="grandfather">
    <div id="father">
        <div id="son"></div>
    </div>
</div>
<script>
    var son = document.querySelector('#son')
    console.log(son.offsetLeft); // 30 => father.padding-right + son.margin-left
    console.log(son.offsetTop); // 30 => father.padding-top + son.margin-top
    console.log(son.offsetParent);

    console.log(getElementLeft(son));

    // 往上追溯到 body 并计算 offsetLeft 的值
    function getElementLeft(obj) {
        // 1. 获取当前元素的左偏移量
        var actualLeft = obj.offsetLeft;
        // 2. 求出定位父级
        var parent = obj.offsetParent;
        // 3. 循环判断付出定位父级
        while (parent != null) {
            // 计算出当前的左方偏移量
            actualLeft += parent.offsetLeft + parent.clientLeft;
            // 更新定位的父级
            parent = parent.offsetParent;
        }
        return actualLeft + 'px';
    }
</script>
</body>
</html>

8.1.3 offset 和 style 的区别

比较项 offset style
样式 可以得到任意样式表中的样式值 只能得到行内样式表中的样式值
单位 获得的数值是没有单位 获得的数值带有单位
宽度 padding+border+width 不包含padding和border 的值
读写 只能获取不能赋值 可以获取也可以赋值
用途 获取元素大小位置 给元素更改值

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>offset 和 style 的区别</title>
    <style>
        .box {
            width: 100px;
            height: 100px;
            background-color: red;
            padding: 10px;
        }
    </style>
</head>
<body>
<div class="box" style="width: 200px;"></div>
<script>
    var div = document.querySelector('div')
    console.log(div.offsetWidth); // 220 = 10 * 2 + 200
    // div.offsetWidth = 300; // 赋值无效

    console.log(div.style.width); // 200px
    // div.style.width = '300px'; // 赋值有效
</script>
</body>
</html>

8.1.4 案例

8.1.4.1 获取盒子内鼠标坐标

要得到鼠标在盒子内的坐标,首先要获取鼠标在页面中的坐标,其次要获取到盒子在页面中的距离,然后使用前者的值减去后者的值就得到鼠标在盒子内的坐标了。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>获取盒子内鼠标坐标</title>
    <style>
        .box {
            width: 200px;
            height: 200px;
            border: 1px solid #000;
            margin: 100px auto;
        }
    </style>
</head>
<body>
<div class="box"></div>
<script>
    // 1. 获取盒子
    var box = document.querySelector('.box')
    box.onmousemove = function (e) {
        // e.pageX 它的值是鼠标所在位置距离页面左边的距离
        // offsetLeft 它的值是当前这个对象距离页面左边的距离
        // 当要计算鼠标在 div 中的位置时,需要使用 e.pageX - offsetLeft 的值
        // console.log(e.pageX , this.offsetLeft)
        var cursorPlaceX = e.pageX - this.offsetLeft;
        //console.log(cursorPlaceX);

        var cursorPlaceY = e.pageY - this.offsetTop;
        console.log('鼠标所在当前位置:' + cursorPlaceX + ', ' + cursorPlaceY);
    };
</script>
</body>
</html>

8.1.4.2 拖拽模态框

弹出框,我们也称为模态框。

  1. 点击弹出层,会弹出模态框, 并且显示灰色半透明的遮挡层。

  2. 点击关闭按钮,可以关闭模态框,并且同时关闭灰色半透明遮挡层。

  3. 鼠标放到模态框最上面一行,可以按住鼠标拖拽模态框在页面中移动。

  4. 鼠标松开,可以停止拖动模态框移动

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>拖拽模态框</title>
    <style>
        * {
            padding: 0;
            margin: 0;
        }
        .login-header {
            width: 100%;
            text-align: center;
            height: 30px;
            font-size: 24px;
            line-height: 30px;
        }
        .login {
            display: none;
            width: 512px;
            height: 280px;
            position: fixed;
            border: 1px solid #ebebeb;
            left: 50%;
            top: 50%;
            background: #ffffff;
            box-shadow: 0 0 20px #ddd;
            z-index: 9999;
            transform: translate(-50%, -50%);
        }
        .login-title {
            width: 100%;
            margin-top: 10px;
            text-align: center;
            line-height: 40px;
            height: 40px;
            font-size: 18px;
            position: relative;
            cursor: move;
        }
        .login-input-content {
            margin-top: 20px;
        }
        .login-button {
            width: 50%;
            margin: 30px auto 0 auto;
            line-height: 40px;
            font-size: 14px;
            border: 1px solid #ebebeb;
            text-align: center;
        }
        .login-bg {
            display: none;
            width: 100%;
            height: 100%;
            position: fixed;
            top: 0;
            left: 0;
            background: rgba(0, 0, 0, .3);
        }
        a {
            text-decoration: none;
            color: #000000;
        }
        .login-button a {
            display: block;
        }
        .login-input input.list-input {
            float: left;
            line-height: 35px;
            height: 35px;
            width: 350px;
            border: 1px solid #ebebeb;
            text-indent: 5px;
        }
        .login-input {
            overflow: hidden;
            margin: 0 0 20px 0;
        }
        .login-input label {
            float: left;
            width: 90px;
            padding-right: 10px;
            text-align: right;
            line-height: 35px;
            height: 35px;
            font-size: 14px;
        }
        .login-title span {
            position: absolute;
            font-size: 12px;
            right: -20px;
            top: -30px;
            background: #ffffff;
            border: 1px solid #ebebeb;
            width: 40px;
            height: 40px;
            border-radius: 20px;
        }
    </style>
</head>
<body>
<div class="login-header"><a id="link" href="javascript:;">点击弹出登录框</a></div>
<div id="login" class="login">
    <div id="title" class="login-title">登录会员
        <span><a id="closeBtn" href="javascript:void(0);" class="close-login">关闭</a></span>
    </div>
    <div class="login-input-content">
        <div class="login-input">
            <label>用户名:</label>
            <input type="text" placeholder="请输入用户名" name="info[username]" id="username" class="list-input">
        </div>
        <div class="login-input">
            <label>登录密码:</label>
            <input type="password" placeholder="请输入登录密码" name="info[password]" id="password" class="list-input">
        </div>
    </div>
    <div id="loginBtn" class="login-button"><a href="javascript:void(0);" id="login-button-submit">登录会员</a></div>
</div>
<!-- 遮盖层 -->
<div id="bg" class="login-bg"></div>
<script>
    // 1. 获取元素
    var link = document.querySelector('#link')
    var login = document.querySelector('#login')
    var title = document.querySelector('#title')
    var closeBtn = document.querySelector('#closeBtn')
    var mask = document.querySelector('#bg')

    // 2. 点击弹框
    link.onclick = function () {
        login.style.display = 'block';
        mask.style.display = 'block';
    };

    // 3. 点击关闭
    closeBtn.onclick = function () {
        login.style.display = 'none';
        mask.style.display = 'none';
    };

    // 4. 拖拽弹框
    title.onmousedown = function (e) {
        e = e || window.event;
        //var x = e.pageX - this.parentNode.offsetLeft;
        // 获取鼠标在 login 窗口中的 x 坐标和 y 坐标的位置
        var x = e.pageX - login.offsetLeft;
        var y = e.pageY - login.offsetTop;

        // 给文档对象添加移动事件
        document.onmousemove = function (e) {
            // 修改 login 对象的位置
            // e.pageX - x 是计算 login 框的左边距离页面的左边的距离
            // e.pageY - y 是计算 login 框的上边距离页面的上边的距离
            login.style.left = e.pageX - x + 'px';
            login.style.top = e.pageY - y + 'px';
        };

        // 释放鼠标
        document.onmouseup = function (e) {
            document.onmousemove = null;
        };
    };
</script>
</body>
</html>

8.2 可视区

8.2.1 client

client 指客户端,指的是当前元素内容到内边距占据的空间大小。

属性及作用如下表所示。

属性 作用
element.clientTop 返回元素上边框大小
element.clientLeft 返回元素左边框大小
element.clientWidth 返回自身包括padding、内容区宽度、不含边框,返回值不带单位
element.clientHeight 返回自身包括padding、内容区高度、不含边框,返回值不带单位
image-20210104164341248

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>client属性</title>
    <style>
        div {
            width: 200px;
            height: 200px;
            border: 10px solid red;
            padding: 10px;
        }
    </style>
</head>
<body>
<div></div>
<script>
    /**
     * client 表示客户端,它有四个属性:
     * 1. clientTop:返回元素的上边框
     * 2. clientLeft:返回元素的左边框
     * 3. clientWidth:返回元素的内边距 + 内容宽度的值
     * 4. clientHeight:返回元素的内边距 + 内容高度的值
     */
    var div = document.querySelector('div');
    console.log(div.clientLeft); // 10
    console.log(div.clientTop); // 10
    console.log(div.clientWidth); // 220 = 10 + 200 + 20
    console.log(div.clientHeight); // 220 = 10 + 200 + 20

    // 可以使用 clientWidth 和 clientHeight 来获取页面的大小
    window.onresize = function () {
        var doc = document.documentElement;
        console.log(doc.clientWidth, doc.clientHeight);
    };
</script>
</body>
</html>

8.3 滚动区

8.3.1 滚动高度

属性 作用
scrollHeight 表示元素的总高度,包括由于溢出而无法展示在网页的不可见部分
scrollWidth 表示元素的总宽度,包括由于溢出而无法展示在网页的不可见部分

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>滚动高度</title>
    <style>
        #test {
            width: 100px;
            height: 100px;
            background: teal;
            border: 5px solid red;
            padding: 10px 20px;
            margin: 10px 20px;
            overflow: scroll;
        }
    </style>
</head>
<div id="test">文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本</div>
<div>aaaaa</div>
<body>
<script>
    var test = document.querySelector('#test')
    console.log(test.scrollHeight);
    console.log(test.scrollWidth);
</script>
</body>
</html>

8.3.2 滚动长度和宽度

属性 作用
scrollTop 表示被隐藏在内容区域上方的像素数
scrollLeft 表示被隐藏在内容区域左侧的像素数

示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>滚动长度和宽度</title>
    <style>
        #test {
            width: 100px;
            height: 100px;
            background: teal;
            border: 5px solid red;
            padding: 10px 20px;
            margin: 10px 20px;
            overflow: scroll;
        }
    </style>
</head>
<div id="test">文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本文本</div>
<body>
<script>
    var div = document.querySelector('#test')
    // 添加滚动事件
    div.onscroll = function () {
        console.log(div.scrollTop, div.scrollLeft);
    };
</script>
</body>
</html>

8.3.3 页面滚动

需求:点击右下角的回到顶部按钮后,滚动条滚动最上方。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>页面滚动</title>
    <style>
        body {
            height: 2000px;
        }
        .box {
            width: 100px;
            height: 100px;
            border: 1px solid #000000;
            padding: 10px;
            margin: 10px;
            overflow: scroll;
        }
        #btn {
            position: fixed;
            bottom: 30px;
            right: 30px;
            display: none;
        }
    </style>
</head>
<body>
<div id="box">
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
</div>
<button id="btn">回到顶部</button>
<script>
    window.onscroll = function () {
        var btn = document.querySelector('#btn')
        // 滚动的高度
        var scrollVal = document.documentElement.scrollTop;
        // 可视化区域高度
        var showHeight = document.documentElement.clientHeight;
        // 判断滚动高度是否超过可视化区域高度(是否达到一屏距离)
        if (scrollVal <= showHeight) {
            btn.style.display = 'none';
        } else {
            btn.style.display = 'block';
        }
        btn.onclick = function () {
            document.documentElement.scrollTop = 0;
            btn.style.display = 'none';
        };
    };
</script>
</body>
</html>

8.3.4 滚动方法

scrollTo(x,y) 方法表示滚动当前 window 中显示的文档,让文档中坐标为x,y的点位于左上角

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>页面滚动</title>
    <style>
        body {
            height: 2000px;
        }
        .box {
            width: 100px;
            height: 100px;
            border: 1px solid #000000;
            padding: 10px;
            margin: 10px;
            overflow: scroll;
        }
        #btn {
            position: fixed;
            bottom: 30px;
            right: 30px;
            display: none;
        }
    </style>
</head>
<body>
<div id="box">
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
    <p>内容</p>
</div>
<button id="btn">回到顶部</button>
<script>
    window.onscroll = function () {
        var btn = document.querySelector('#btn')
        // 滚动的高度
        var scrollVal = document.documentElement.scrollTop;
        // 可视化区域高度
        var showHeight = document.documentElement.clientHeight;
        // 判断滚动高度是否超过可视化区域高度(是否达到一屏距离)
        if (scrollVal <= showHeight) {
            btn.style.display = 'none';
        } else {
            btn.style.display = 'block';
        }
        btn.onclick = function () {
            // document.documentElement.scrollTop = 0;
            scrollTo(0, 0)
            btn.style.display = 'none';
        };
    };
</script>
</body>
</html>
posted @ 2022-01-20 07:24  tothk  阅读(113)  评论(0编辑  收藏  举报