04_JavaScript DOM编程_基础篇

发布时间 2023-10-31 15:28:27作者: 城市炊烟

一、JavaScript DOM 概述

1.1、DOM概念

​ 文档对象模型(Document Object Model,简称DOM),是W3C组织推荐的处理超文本标记语言的标准编程接口。它是一种与平台和语言无关的应用程序接口(API),它可以动态地访问程序和脚本,更新其内容、结构和www文档的风格(目前,HTML和XML文档是通过说明部分定义的)。文档可以进一步被处理,处理的结果可以加入到当前的页面。DOM是一种基于树的API文档,它要求在处理过程中整个文档都表示在存储器中。

​ DOM(Document Object Model)即文档对象模型,针对HTML和XML文档的API(应用程序接口)。

​ DOM描绘了一个层次化的节点树,可以添加、移除和修改页面的内容。

​ Document用于表现HTML页面当前窗体的内容,document对象包含一个节点对象,此对象包含每个单独页面的所有HTML元素,这就是W3C的DOM对象。

​ 通过 JavaScript,您可以重构整个HTML文档。您可以添加、移除、改变或重排页面上的项目。要改变页面的某个东西,JavaScript就需要对HTML文档中所有元素进行访问的入口。这个入口,连同对HTML 元素进行添加、移动、改变或移除的方法和属性,都是通过文档对象模型来获得的(DOM)。

​ 在 1998 年,W3C 发布了第一级的 DOM 规范。这个规范允许访问和操作 HTML 页面中的每一个单独的元素。所有的浏览器都执行了这个标准,因此,DOM 的兼容性问题也几乎难觅踪影了。DOM 可被 JavaScript 用来读取、改变 HTML、XHTML 以及 XML 文档。

1.2、DOM的作用

通过可编程的对象模型,JavaScript 获得了足够的能力来创建“动态的 HTML”。

JavaScript 能够改变页面中的所有 HTML 元素

JavaScript 能够改变页面中的所有 HTML 属性

JavaScript 能够改变页面中的所有 CSS 样式

JavaScript 能够对页面中的所有事件做出反应

静态页面和动态页面:

​ 静态页面,即静态网页,是实际存在的,无需经过服务器的编译,直接加载到客户浏览器上显示出来。静态网页,随着html代码的生成,页面的内容和显示效果就基本上不会发生变化了——除非你修改页面代码。常见的静态页面举例:.html扩展名的、.htm扩展名的。

​ 所谓的动态网页,是指跟静态网页相对的一种网页编程技术。动态网页,页面代码虽然没有变,但是显示的内容却是可以随着时间、环境或者数据库操作的结果而发生改变的。

​ 值得强调的是,不要将动态网页和页面内容是否有动感混为一谈。这里说的动态网页,与网页上的各种动画、滚动字幕等视觉上的动态效果没有直接关系,动态网页也可以是纯文字内容的,也可以是包含各种动画的内容,这些只是网页具体内容的表现形式,无论网页是否具有动态效果,只要是采用了动态网站技术生成的网页都可以称为动态网页。

​ 我们现在学习JS写的动画效果,不叫动态页面,只能称之为静态页面的动态效果;

​ 总之,动态网页是基本的html语法规范与Java、VB、VC等高级程序设计语言、数据库编程等多种技术的融合,以期实现对网站内容和风格的高效、动态和交互式的管理。因此,从这个意义上来讲,凡是结合了HTML以外的高级程序设计语言和数据库技术进行的网页编程技术生成的网页都是动态网页。

1.3、DOM总结

1573034437922

每个载入浏览器的 HTML 文档都会成为 Document 对象

​ Document 对象使我们可以从脚本中对 HTML 页面中的所有元素进行访问。
​ Document 对象是 Window 对象的一部分。

当浏览器打开一个HTML文档时,浏览器解析HTML文档的标签,并创建表示这些标签的对象,这些对象就是HTML文档对象(标签元素对象)

​ 文档对象即Document 对象,指的是一回事。

二、JavaScript节点与节点树

​ 当浏览器加载html页面的时候,会生成一个与之对应的Document对象。Dcoument对象又称之为文档对象;

​ 节点树: 加载HTML 页面时,Web 浏览器生成一个与之对应的树型结构,用来表示页面内部结构。DOM 将这种树型结构理解为由节点组成。

​ 下面这个图片表示一个文档树(节点树):

1573034494925

​ 从上图的树型结构,我们理解几个概念,html 标签没有父辈,没有兄弟,所以html 标签为根标签。head 标签是html子标签,meta 和title标签之间是兄弟关系。

​ 如果把每个标签当作一个节点的话,那么这些节点组合成了一棵节点树。经常把标签称作为元素,是同一个意思。

​ 常用节点分为三类:元素节点、文本节点、属性节点。

1573034534818

根据 DOM,HTML 文档中的每个成分都是一个节点。

DOM 是这样规定的:

整个文档是一个文档节点。

每个 HTML 标签是一个元素节点。

包含在 HTML 元素中的文本是文本节点。

每一个 HTML 属性是一个属性节点。

注释属于注释节点。

三、JavaScript获取HTML标签元素

​ W3C提供了比较方便简单的定位节点的方法,以便我们快速的对节点进行操作,分别为:getElementById()、getElementsByClassName()、getElementsByName()、getElementsByTagName()、getElementsByTagNameNS(),其中常用的是下面五个方法。

3.1、常用方法介绍

getElementById()​ 返回对拥有指定 id 的第一个对象的引用,返回值Element。

getElementsByName()​ 返回带有指定名称的对象集合。返回值NodeList/HTMLCollection。

getElementsByTagName()​ 返回带有指定标签名的对象集合。返回值NodeList/HTMLCollection。

getElementsByClassName()​ 返回所有指定类名的对象集合。返回值NodeList/HTMLCollection。

querySelector() 返回文档中匹配指定 CSS 选择器的一个元素。

querySelectorAll() 返回文档中匹配指定 CSS 选择器的所有元素。

3.1.1、根据id获取元素

getElementById()方法

​ 元素id属性表示一个元素节点的唯一性,不能同时给两个或以上的元素节点创建同一个命名的id。

​ getElementById()方法,接受一个参数:获取元素的id。如果找到相应的元素则返回该元素,如果不存在,则返回null。

// 比如获取id 为box 的元素节点:
var box = document.getElementById('box');

//获取到的数据类型 HTMLDivElement,对象都是有类型的

​ 当我们通过getElementById()获取到特定元素节点时,访问它的一系列属性。

​ 注意:由于id名具有唯一性,部分浏览器支持直接使用id名访问元素,但不是标准方式,不推荐使用。

3.1.2、根据name获取元素

getElementsByName()方法

​ 元素name属性表示一个元素节点的名字,在同一个网页上,该属性并不要求唯一性,相同的name可以出现在多个标签上。

​ getElementsByName()方法可以获取相同名称(name)的元素,返回一个元素节点列表。

​ 可以通过下标来访问列表里面的元素。

示例:

document.getElementsByName('goods') //获取属性name的值为goods的所有元素

document.getElementsByName('goods')[0]//获取属性name的值为goods的第一个元素

document.getElementsByName('goods')[1]//获取属性name的值为goods的第二个元素

// 根据所有的爱好复选框
var inputs = document.getElementsByName('hobby');
for (var i = 0; i < inputs.length; i++) {
  var input = inputs[i];
  console.log(input);
}

3.1.3、根据标签名获取元素

getElementsByTagName()方法

​ getElementsByTagName()方法将返回一个元素节点对象,这个对象保存着所有相同元素名的节点列表。

示例:

document.getElementsByTagName('*'); //获取所有元素

document.getElementsByTagName('li'); //获取所有li 元素

document.getElementsByTagName('li')[0]; //获取第一个li元素

document.getElementsByTagName('li').length; //获取所有li元素的数目

// 获取所有div元素
var divs = document.getElementsByTagName('div');
// 遍历
for (var i = 0; i < divs.length; i++) {
  var div = divs[i];
  console.log(div);
} 

​ 注:NodeList不仅可以通过下标访问指定标签元素,还可以通过length属性来查询列表节点数量.

重点: 该方法不仅可以通过document对象调用,还可以通过元素节点Element来访问

3.1.4、根据类名获取元素

getElementsByClassName()方法

​ getElementsByClassName()方法返回文档中所有指定类名的元素集合,作为 NodeList 对象。参数为String,需要获取的元素类名,多个类名使用空格分隔(尽量不用);

示范代码1:

//获取包含 "example" 和 "color" 类名的所有元素:
var x = document.getElementsByClassName("example color");

示范代码2:

//查看文档中有多少个样式class="example"的元素(使用 NodeList 对的 length 属性):
var x = document.getElementsByClassName("example").length;

3.1.5、根据选择器获取元素

​ querySelector()方法返回文档中匹配指定 CSS 选择器的一个元素。

注意: querySelector() 方法仅仅返回匹配指定选择器的第一个元素。如果你需要返回所有的元素,请使用 querySelectorAll() 方法替代。

// 获取页面中id为text的元素
var text = document.querySelector('#text');
console.log(text);

// 获取页面中所有class为box的元素
var boxes = document.querySelectorAll('.box');
for (var i = 0; i < boxes.length; i++) {
  var box = boxes[i];
  console.log(box);
}

深入解析document.write():

​ write() 向文档写 HTML 表达式 或 JavaScript 代码。

​ 温馨提示:文档加载之后使用docunment.write()会覆盖原文档.

​ 除了直接输出文字外,它还支持带有HTML标签的输出内容。
​ 比如直接输出一个标题
​ 比如在输出内容中加入br换行标签
​ 比如直接输出列表项......

注意事项:绝对不要在文档加载完成之后使用 document.write()。这会覆盖该文档。

3.1.6、封装函数获取元素

function my$(id) {
  return document.getElementById(id);
}

3.2、Element和NodeList

​ 在 HTML DOM 中,Element 对象表示 HTML 元素。

​ Element 对象可以拥有类型为元素节点、文本节点、注释节点的子节点。

​ NodeList 对象表示节点列表,比如 HTML 元素的子节点集合。

​ 我们可通过节点列表中的节点索引号来访问列表中的节点(索引号由0开始)。

​ 节点列表可保持其自身的更新。如果节点列表或 XML 文档中的某个元素被删除或添加,列表也会被自动更新。

四、JavaScript操作标签内容

1573034825065

​ element.innerHTML​ 设置或返回元素的内容。

​ 注:可以设置内容的时候加上标签,设置的标签会被浏览器解析

​ element.innerText​ 设置或返回元素的文本内容

​ element.tagName​ 返回元素的标签名。

var box = document.getElementById('box');

box.innerHTML = '我是文本<p>我会生成为标签</p>';

console.log(box.innerHTML);

box.innerText = '我是文本<p>我不会生成为标签</p>';

console.log(box.innerText);

innerText和innerHTML的区别:

​ innerText只能获取到文本内容,标签不会被获取; innerHTML获取到所有的内容,包含标签;

​ innerText设置的内容会被浏览器认为是纯文本;innerHTML设置的内容会被浏览器认为是HTML文本,如果存在标签会被解析;

innerText浏览器兼容问题

<div id="box">
    我是一个box
    <b>hello</b>
</div>
<script>
    // innerText  最早出现在IE浏览器中
    // 浏览器兼容问题
    
    // 老版本的firefox浏览器不支持innerText
    // 2016的时候 已经把innerText 在DOM中规定为正式的标准属性
   
    // textContent

    var box = document.getElementById('box');
    // 都会把标签过滤
    // 前后的空白和一些换行去掉
    console.log(box.innerText);
    // 原封不动的把内容输出
    console.log(box.textContent);
</script>
<div id="box">
    hello
</div>
<script>
    // 如何知道。浏览器是否支持元素的某个属性
    var box = document.getElementById('box');

    // 当属性不存在的时候返回的是  undefined
    console.log(typeof box.a);
    // 当属性存在的时候返回的是 该属性的类型
    console.log(typeof box.id);


    var box = document.getElementById('box');
    console.log(getInnerText(box));

    // 处理innerText的兼容性问题
    function getInnerText(element) {
        // 判断当前浏览器 是否支持元素的innerText属性,支持innerText 使用element.innerText获取内容
        // 如果不支持innerText属性,使用element.textContent获取内容

        if (typeof element.innerText === 'string') {
            return element.innerText;
        } else {
            return element.textContent;
        }
    }

    // box.innerHTML = 'hello world';

    // 设置内容的时候
    // innerText(textContent)       当设置不含标签的内容的时候应该使用innerText,效率高
    // innerHTML 
</script>

五、JavaScript操作标签属性

5.1、HTML基本属性操作

​ element.属性名​ 设置或返回元素的指定属性。

​ element.className​ 设置或返回元素的 class 属性。

var link = document.getElementById('link');
console.log(link.href);
console.log(link.title);

var pic = document.getElementById('pic');
console.log(pic.src);

​ 注:class属性的操作必须使用className

​ 注:Element对象使用 . 可以调用对象的各个属性

​ 注:当html中的标签的属性,只有一个值的时候,DOM中对应的元素的属性值是布尔类型

5.2、HTML方法操作属性

element.getAttribute()​ 返回元素节点的指定属性值。

​ getAttribute()方法将获取元素中某个属性的值。它和直接使用.属性获取属性值的方法有一定区别。

document.getElementById('box').getAttribute('id');//获取元素的id 值
document.getElementById('box').id; //获取元素的id 值
document.getElementById('box').getAttribute('mydiv');//获取元素的自定义属性值
document.getElementById('box').mydiv //获取元素的自定义属性值,非IE 不支持
document.getElementById('box').getAttribute('class');//获取元素的class 值

element.setAttribute()​ 把指定属性设置或更改为指定值
​ setAttribute()方法将设置元素中某个属性和值。它需要接受两个参数:属性名和值。如果属性本身已存在,那么就会被覆盖。

document.getElementById('box').setAttribute('align','center');//设置属性和值
document.getElementById('box').setAttribute('bbb','ccc');//设置自定义的属性和值

element.removeAttribute() 从元素中移除指定属性。
​ removeAttribute()可以移除HTML 属性。

document.getElementById('box').removeAttribute('style');//移除属性

注:使用属性只能来获取和设置的是元素对象原始属性;使用方法可以用来设置元素对象自定义的属性;

5.3、案例

5.3.1、点击按钮切换图片

<input type="button" value="点我" id="btn">
<br>
<img src="images/a.jpg" id="mv" alt="" width="400" height="300">

<script>
    // 1 获取元素
    var btn = document.getElementById('btn');
    var mv = document.getElementById('mv');
    // 2 给按钮注册事件
    var flag = 1;  // 当flag的值是1 的时候 对应a.jpg   当flag的值是2的时候 对应 b.jpg
    btn.onclick = function () {
        // 3 切换图片
        if (flag === 1) {
            mv.src = 'images/b.jpg';
            flag = 2;
        } else if (flag === 2) {
            mv.src = 'images/a.jpg';
            flag = 1;
        }
    }
</script>

5.3.2、点击按钮显示隐藏div

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    #box {
      background-color: red;
      width: 200px;
      height: 200px;
    }

    .show {
      display: block;
    }

    .hidden {
      display: none;
    }
  </style>
</head>
<body>
   <input type="button" id="btn" value="隐藏">
   <br>
   <div id="box" >
     
   </div>
  <script>
    //1 获取元素
    var btn = document.getElementById('btn');
    //2 给按钮注册事件
    // isShow记录了box的状态,true 显示 ,false 隐藏
    var isShow = true;
    btn.onclick = function () {
      //3 控制div的显示隐藏
      var box = document.getElementById('box');
      if (isShow) {
        // 为什么DOM对象的对应的标签的class属性的名字叫做className ,应为class 在js中是关键字
        // 关键字不可以作为变量或者属性的名字
        box.className = 'hidden';

        // 如何设置按钮对应的元素的属性
        // btn.value = '显示';
        this.value = '显示';

        isShow = false;
      } else {
        box.className = 'show';
        this.value = '隐藏';
        isShow = true;
      }
      
    }
  </script>
</body>
</html>

使用hidden属性实现

<input type="button" id="btn" value="隐藏">
<br>
<div id="box">

</div>
<script>
    //1 获取元素
    var btn = document.getElementById('btn');

    var isShow = true;
    btn.onclick = function () {
        //3 控制div的显示隐藏
        var box = document.getElementById('box');
        if (isShow) {
            box.setAttribute("hidden", true);

            isShow = false;
        } else {
            box.removeAttribute("hidden");
            isShow = true;
        }

    }
</script>

5.3.3、美女相册

取消超链接默认跳转行为

<a id="link" href="http://www.baidu.com">百度</a>
<script>
    var link = document.getElementById('link');
    link.onclick = function () {
        alert('点击我了');

        // 取消a标签的默认行为(跳转到href)
        return false;
    }
</script>

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <style type="text/css">
        body {
            font-family: "Helvetica","Arial",serif;
            color: #333;
            background-color: #ccc;
            margin: 1em 10%;
        }

        #imagegallery a {
            margin: 0px 20px 20px 0px;
            padding: 0px;
            display: inline;
        }

        #imagegallery a img {
            border: 0;
        }
    </style>
</head>
<body>
<h2>
    美女画廊
</h2>

<div id="imagegallery">
    <a href="images/1.jpg" title="美女A">
        <img src="images/1-small.jpg" width="100px" alt="美女1" />
    </a>
    <a href="images/2.jpg" title="美女B">
        <img src="images/2-small.jpg" width="100px" alt="美女2" />
    </a>
    <a href="images/3.jpg" title="美女C">
        <img src="images/3-small.jpg" width="100px" alt="美女3" />
    </a>
    <a href="images/4.jpg" title="美女D">
        <img src="images/4-small.jpg" width="100px" alt="美女4" />
    </a>
</div>

<div style="clear:both"></div>

<img id="image" src="images/placeholder.png" alt="" width="450px" />

<p id="des">选择一个图片</p>
    <script>
        //1 获取到所有的a标签
        var imagegallery = document.getElementById('imagegallery');
        var links = imagegallery.getElementsByTagName('a');
        //2 给所有的a标签注册事件
        for (var i = 0; i < links.length; i++) {
            // 所有的a标签对应的元素
            var link = links[i];
            // 注册事件
            link.onclick = function () {
                //4 切换图片
                // 获取占位的大`的img标签
                var image = document.getElementById('image');
                // 把img标签的src属性 设置为当前点击的a标签的href
                image.src = this.href;
                
                //5 设置p标签的内容
                var des = document.getElementById('des');
                // 当前点击的a标签的title属性
                // this.title
                // 如何设置p标签中显示的内容
                // console.dir(des);
                des.innerText = this.title;

                //3 取消a的默认行为
                return false;
            }
        }

        // 当循环结束之后 link是谁??
        // link 是最后一个a标签
        // console.log(link);
    </script>
</body>
</html>

5.3.4、表单属性操作案例

点击按钮禁用文本框

<input type="button" value="禁用文本框" id="btn">
<br>
<input type="text" id="txt" value='123'>
<script>
    // 获取元素
    var btn = document.getElementById('btn');
    // 注册事件
    btn.onclick = function () {
        console.log(txt.disabled);
        txt.disabled = true;
    }
</script>

给文本框赋值,获取文本框的值

<input type="button" value="设置文本框的值" id="btn1">
<br>
<input type="text" id="txt" value='123'>
<script>
    var btn1 = document.getElementById('btn1');
    btn1.onclick = function () {
        var txt = document.getElementById('txt');
        // 设置文本框中的内容
        txt.value = 'hello world';
    }
</script>

检测用户名是否是3-6位,密码是否是6-8位,如果不满足要求高亮显示文本框

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    .bg {
      background-color: yellow;
    }
  </style>
</head>

<body>
  <input type="text" id="txtUserName"> <br>
  <input type="password" id="txtUserPassword"> <br>
  <input type="button" value=" 登 录 " id="btnLogin">
  <script>
    // 检测用户名是否是3-6位,密码是否是6-8位,如果不满足要求高亮显示文本框
    var btnLogin = document.getElementById('btnLogin');

    btnLogin.onclick = function () {
      // 检测用户名是否是3-6位,密码是否是6-8位
      var txtUserName = document.getElementById('txtUserName');
      var txtUserPassword = document.getElementById('txtUserPassword');

      //检测用户名是否是3-6位
      if (txtUserName.value.length < 3 || txtUserName.value.length > 6) {
        txtUserName.className = 'bg';
        return;
      } else {
        txtUserName.className = '';
      }

      // 密码是否是6-8位
      if (txtUserPassword.value.length < 6 || txtUserPassword.value.length > 8) {
        txtUserPassword.className = 'bg';
        return;
      } else {
        txtUserPassword.className = '';
      }

      // 
      console.log('执行登录');
    }
  </script>
</body>

</html>

设置下拉框中的选中项

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>

<body>
  <input type="button" value="设置" id='btnSet'>
  <select id="selCities">
    <option value="1">北京</option>
    <option value="2">上海</option>
    <option value="3">杭州</option>
    <option value="4">郑州</option>
    <option value="5">武汉</option>
  </select>
  <script>
    // 1 给按钮注册事件
    var btnSet = document.getElementById('btnSet');
    btnSet.onclick = function () {
      // 2 获取下拉框中的所有option
      var selCities = document.getElementById('selCities');
      var options = selCities.getElementsByTagName('option');
      // 3 随机生成索引
      // Math.random() -> [0, 1)
      // Math.random() * 5 -> [0, 5)
      var randomIndex = parseInt(Math.random() * options.length);
      // 4 根据索引获取option,并让option选中
      var option = options[randomIndex];
      option.selected = true;
    }
  </script>
</body>

</html>

模拟placeholder属性

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        /* 默认提示的文字颜色 */
        .default {
            color: #666;
        }

        /* 正常输入的文字颜色 */
        .normal {
            color: black;
        }
    </style>
</head>

<body>
    <input type="text" id="ip1" value="请输入用户名" class="default">
    <hr>
    <input type="text" placeholder="请输入用户名">
    <script>
        var ip1 = document.getElementById("ip1");
        // focus 获取焦点事件
        ip1.onfocus = function () {
            // console.log("获取焦点事件");
            // 如果value值是请输入用户名,并且class值是default,才清空输入框
            if (this.value === "请输入用户名" && this.className === "default") {
                // 清空输入框
                this.value = "";
                // 并且文字改为正常颜色
                this.className = "normal";
            }
        }

        // blur 失去焦点事件
        ip1.onblur = function () {
            // console.log("失去焦点事件");
            if (this.value === "") {
                this.value = "请输入用户名";
                this.className = "default"
            }
        }
    </script>
</body>

</html>

全选反选

<!DOCTYPE html>
<html>

<head lang="en">
  <meta charset="UTF-8">
  <title></title>
  <style>
    * {
      padding: 0;
      margin: 0;
    }

    .wrap {
      width: 300px;
      margin: 100px auto 0;
    }

    table {
      border-collapse: collapse;
      border-spacing: 0;
      border: 1px solid #c0c0c0;
      width: 300px;
    }

    th,
    td {
      border: 1px solid #d0d0d0;
      color: #404060;
      padding: 10px;
    }

    th {
      background-color: #09c;
      font: bold 16px "微软雅黑";
      color: #fff;
    }

    td {
      font: 14px "微软雅黑";
      text-align: center;
    }

    tbody tr {
      background-color: #f0f0f0;
    }

    tbody tr:hover {
      cursor: pointer;
      background-color: #fafafa;
    }
  </style>

</head>

<body>
  <div class="wrap">
    <table>
      <thead>
        <tr>
          <th>
            <input type="checkbox" id="j_cbAll" />
          </th>
          <th>商品</th>
          <th>价钱</th>
        </tr>
      </thead>
      <tbody id="j_tb">
        <tr>
          <td>
            <input type="checkbox" />
          </td>
          <td>iPhone8</td>
          <td>8000</td>
        </tr>
        <tr>
          <td>
            <input type="checkbox" />
          </td>
          <td>iPad Pro</td>
          <td>5000</td>
        </tr>
        <tr>
          <td>
            <input type="checkbox" />
          </td>
          <td>iPad Air</td>
          <td>2000</td>
        </tr>
        <tr>
          <td>
            <input type="checkbox" />
          </td>
          <td>Apple Watch</td>
          <td>2000</td>
        </tr>

      </tbody>
    </table>
    <input type="button" value="  反 选  " id="btn">
  </div>
  <script>
    var j_tb = document.getElementById('j_tb');
    var inputs = j_tb.getElementsByTagName('input');
    // 1 全选
    // 1.1 获取父checkbox,注册点击事件
    var j_cbAll = document.getElementById('j_cbAll');
    j_cbAll.onclick = function () {
      // 1.2 找到所有子的checkbox,让这些checkbox的状态跟父checkbox保持一致

      for (var i = 0; i < inputs.length; i++) {
        var input = inputs[i];
        if (input.type === 'checkbox') {
          // 让子的checkbox的选中状态,和父checkbox的选中状态一致
          input.checked = this.checked;
        }
      }
    }

    // 2 当点击子的checkbox,如果所有的子的checkbox都被选中了,让父的checkbox也选中
    // 如果有一个子的checkbox没有被选中,父的checkbox也不被选中

    // 此处的循环,是遍历所有子的checkbox,注册点击事件
    for (var i = 0; i < inputs.length; i++) {
      var input = inputs[i];
      // 判断是否是checkbox
      if (input.type !== 'checkbox') {
        // 结束当前循环,继续下一次循环
        continue;
      }
      // 给子的checkbox注册点击事件
      input.onclick = function () {
        checkAllCheckBox();
      }
    }


    // 判断父的checkbox的状态 封装成函数
    function checkAllCheckBox() {
      // 假设所有的子的checkbox都被选中了
      var isAllChecked = true;
      // 判断是否所有的子的checkbox都被选中了
      for (var i = 0; i < inputs.length; i++) {
        var input = inputs[i];
        if (input.type !== 'checkbox') {
          continue;
        }
        // 判断当前的所有checkbox是否都被选中
        if (!input.checked) {
          isAllChecked = false;
        }
      }
      // 设置父的checkbox的状态
      j_cbAll.checked = isAllChecked;
    }

    // 3 反选
    // 3.1 给反选按钮注册点击事件
    var btn = document.getElementById('btn');
    btn.onclick = function () {
      // 3.2 找到所有的子的checkbox,让其反选
      for (var i = 0; i < inputs.length; i++) {
        var input = inputs[i];
        // 判断是否是checkbox
        if (input.type !== 'checkbox') {
          continue;
        }
        // 让子的checkbox反选
        input.checked = !input.checked;
        // 设置父的checkbox的状态
        checkAllCheckBox();
      }
    }
  </script>
</body>
</html>

六、JavaScript Document对象属性一览

​ document可以通过一下属性可以查询指定属性值或给指定属性赋值.

语法:
​ document.属性名 获取属性值

​ document.属性名 = 值; 给指定属性设置值

常用:
document.title //设置或读取文档标题等价于HTML的<title>标签

​ document.bgColor //设置或获取页面背景色

​ document.fgColor //设置或获取前景色(文本颜色)

​ document.linkColor //设置或获取未点击过的链接颜色

​ document.alinkColor //设置或获取激活链接(焦点在此链接上)的颜色

​ document.vlinkColor //设置或获取已点击过的链接颜色

document.cookie //设置和读出cookie

cookie 是存储于访问者的计算机中的变量。每当同一台计算机通过浏览器请求某个页面时,就会发送这个 cookie。你可以使用 JavaScript 来创建和取回 cookie 的值。

设置cookie:
	每个cookie都是一个键/值对,可以把下面这样一个字符串赋值给document.cookie:
		document.cookie="userId=1001";
	document.cookie看上去就像一个属性,可以赋不同的值。但它和一般的属性不一样,改变它的赋值并不意味着丢失原来的值,例如连续执行下面两条语句:
		document.cookie="userId=1001";
		document.cookie="userName=zhangsan";
	这时浏览器将维护两个cookie,分别是userId和userName,因此给document.cookie赋值更像执行类似这样的语句:
		document.addCookie("userId=1001");
		document.addCookie("userName=zhangsan");

修改cookie:
	事实上,浏览器就是按照这样的方式来设置cookie的,如果要改变一个cookie的值,只需重新赋值,例如:
		document.cookie="userId=1002";

获取cookie的值:
	下面介绍如何获取cookie的值。cookie的值可以由document.cookie直接获得:
		var strCookie=document.cookie; 
	这将获得以分号隔开的多个名/值对所组成的字符串,这些名/值对包括了该域名下的所有cookie。例如:
		document.cookie="userId=1001";
        document.cookie="userName=zhangsan";
        var strCookie=document.cookie;
        console.log(strCookie); //userId=1001; userName=zhangsan
	由此可见,只能够一次获取所有的cookie值,而不能指定cookie名称来获得指定的值,这正是处理cookie值最麻烦的一部分。
	用户必须自己分析这个字符串,来获取指定的cookie值,例如,要获取userId的值,可以这样实现:
		document.cookie = "userId=1001";
    	document.cookie = "userName=zhangsan";

    	document.cookie = "userId=1002";

    	var strCookie = document.cookie;
    	console.log(strCookie); //userId=1001; userName=zhangsan

        function toCookieObj(strCookie) {
          var cookieObj = {};
          var arrCookie = strCookie.split(";");
          for (var i = 0; i < arrCookie.length; i++) {
            var arr = arrCookie[i].split("=");
            cookieObj[arr[0].trim()] = arr[1];
          }
          return cookieObj;
        }

        var cookieObj = toCookieObj(strCookie);
        console.log(cookieObj.userName);

​ document.URL ​ //返回当前文档的 URL

document.images //获取页面上的<img>标签

​ images集合(页面中的图象):可以使用指定的元素访问其各个属性

​ 注:HTMLCollection 是一个集合,表示 HTML 元素的集合,它提供了可以遍历列表的方法和属性。
​ HTML DOM 中的 HTMLCollection 是“活”的;如果基本的文档改变时,那些改变通过所有 HTMLCollection 对象会立即显示出来。
​ HTMLCollection 对象是只读的,不能给它添加新元素,即使采用 JavaScript 数组语法也是如此。
​ HTMLCollection 对象和 NodeList 对象很相似

document.images.length      //对应页面上<img>标签的个数
document.images[0]          //第1个<img>标签
document.images.item(2)          //第3个<img>标签

​ forms集合(页面中的表单):可以使用指定的元素访问其各个属性

document.forms                 //对应页面上的<form>标签
document.forms.length          //对应页面上<form>标签的个数
document.forms[0]              //第1个<form>标签
document.forms[i].length       //第i-1个<form>中的控件数
document.forms[i].elements[j]  //第i-1个<form>中第j-1个控件

​ 注:除了forms/images,还有anchors/links/applets

​ anchors​ 返回对文档中所有 Anchor 对象的引用。(锚点)

​ links​ 返回对文档中所有 Area 和 Link 对象引用(超链接)。

​ applets​ 返回对文档中所有 Applet 对象的引用。

​ 注:Applet是采用Java编程语言编写的小应用程序,该程序可以包含在 HTML(标准通用标记语言的一个应用)页中,与在页中包含图像的方式大致相同。

​ 含有Applet的网页的HTML文件代码中部带有<applet></applet>这样一对标记,当支持Java的网络浏览器遇到这对标记时,就将下载相应的小应用程序代码并在本地计算机上执行该Applet。

body 获取到body元素节点

head 获取到head元素节点

七、JavaScript获取层次节点

7.1、节点概念

​ 节点最常见的类型有元素节点、属性节点、注释节点和文本节点.

​ 节点的层次结构可以划分为:父节点与子节点、兄弟节点、祖先后代节点这三种。当我们获取其中一个元素节点的时候,就可以使用层次节点属性来获取它相关层次的节点

示例:

<div id="div1">
    <p>我是div中的p段落</p>
    <!--我是页面中的注释-->
    <div>我是div1中的子div</div>
    我是div的纯文本内容
    <h2>我是div中的h2标题标签</h2>
</div>

​ 上面的案例中: id为div1的元素是父节点,并且本身也是元素节点; div1里面的节点都是它的子节点;那么到底有多少个子节点呢?

​ 我们知道标签元素都是元素节点,纯文本内容就是文本节点,属性就是属性节点,注释就是注释节点,那么子节点看似是有5个,但是不是.....我们进行深入研究。

​ childNodes​ 获取当前元素节点的所有子节点

​ 因为浏览器会把节点之间的空白符当作文本结点处理。

<div>
    javascript  
    <p>javascript</p> <!-- ←空白节点 -->
    <div>jQuery</div> <!-- ←空白节点 -->
    <h5>PHP</h5> <!-- ←空白节点 -->
</div>

​ 4个可见结点 + 3个看不出来的空白节点 = 7个节点
​ 注:文字节点之前之后的空格都算是内容的一部分。

​ 这些节点又有三个非常有用的属性,分别为:nodeName、nodeType 和nodeValue。

信息节点属性:

​ 节点类型​ nodeName​ nodeType​ nodeValue

​ 元素​ ​ 元素名称​ ​ 1​ ​ ​ null

​ 属性​ ​ 属性名称​ ​ 2​ ​ ​ 属性值

​ 文本​ ​ #text​ ​ ​ 3​ ​ ​ 文本内容

​ 注释​ ​ #comment​ ​ 8 ​ ​ ​ 注释内容

7.2、获取层次节点

​ firstChild​ 获取当前元素节点的第一个子节点

firstElementChild​ 获取当前元素节点的第一个元素子节点

​ IE9以下版本和Safari不支持;​

function getFirstElementChild(element) {
    var node, nodes = element.childNodes, i = 0;
    while (node = nodes[i++]) {
        if (node.nodeType === 1) {
            return node;
        }
    }
    return null;
}

​ lastChild​ 获取当前元素节点的最后一个子节点

lastElementChild​ 获取当前元素节点的最后一个元素子节点

​ ownerDocument​ 获取该节点的文档节点

parentNode​ 获取当前节点的父节点

​ nextSibling​ 获取当前节点的后一个同级节点

nextElementSibling​ 获取当前节点的后一个同级元素节点

// 处理浏览器兼容性
// 获取下一个兄弟元素
 function getNextElementSibling(element) {
    var el = element;
    while (el = el.nextSibling) {
      if (el.nodeType === 1) {
          return el;
      }
    }
    return null;
  }

​ previousSibling​ 获取当前节点的前一个同级节点

previousElementSibling​ 获取当前节点的前一个同级元素节点

​ attributes​ 获取当前元素节点的所有属性节点集合

childElementCount​ 获取当前节点的元素节点的个数

children​ 获取当前节点的所有元素子节点

​ childNodes 获取当前节点的所有子节点

注意:

​ childNodes和children的区别,childNodes获取的是子节点,children获取的是子元素

​ nextSibling和previousSibling获取的是节点,获取元素对应的属性是nextElementSibling和previousElementSibling获取的是元素

​ nextElementSibling和previousElementSibling有兼容性问题,IE9以后才支持

八、JavaScript操作节点

8.1、操作节点API

1、createElement()​ 创建一个元素节点

document.createElement(‘标签名’)
document.createElement('p'); //创建一个元素节点,这里写字符串标签名

2、appendChild()​ 将新节点追加到子节点列表的末尾

元素节点.appendChild(‘节点’)
var box = document.getElementById('box'); //获取某一个元素节点
var p = document.createElement('p'); //创建一个新元素节点<p>
box.appendChild(p); //把新元素节点<p>添加子节点末尾

3、createTextNode()​ 创建一个文本节点

document.createTextNode(‘文本值’)
var text = document.createTextNode('段落'); //创建一个文本节点
p.appendChild(text); //将文本节点添加到子节点末尾

4、insertBefore(新节点,已有节点) 可在已有的子节点前插入一个新的子节点.
​ 父节点.insertBefor(新节点,已有节点)
​ old节点的父节点.insertBefore(new,old);
​ 需要注意的是insertBefore()方法可以给当前元素的前面创建一个节点,但却没有提供给当前元素的后面创建一个节点。

function insertAfter(newElement, targetElement) {
    //得到父节点
    var parent = targetElement.parentNode;
    //如果最后一个子节点是当前元素,那么直接添加即可
    if (parent.lastChild === targetElement) {
        parent.appendChild(newElement);
    } else {
        //否则,在当前节点的下一个节点之前添加
        parent.insertBefore(newElement, targetElement.nextElementSibling);
    }
}

5、repalceChild() 可将某个子节点替换为另一个。
​ 父节点.insertBefor(新节点,旧节点)

parentDiv.replaceChild(p,box); //把<div>换成了<p>

6、cloneNode() 复制节点
​ 元素节点.cloneNode(Boolean)
​ 可选。该方法将复制并返回调用它的节点的副本。如果传递给它的参数是 true,它还将递归复制当前节点的所有子孙节点。否则,它只复制当前节点。

7、removeChild() 删除父节点下指定的子节点
​ 父节点.removeChild(子节点)

注:无论是插入到节点之前还是替换节点,节点本身自己不能操作,需要通过父节点来完成。

向页面插入带标签内容,效率测试:

<!--通过innerHTML插入内容-->
<script>
    function fn() {
        var d1 = new Date(); 

        for (var i = 0; i < 1000; i++) {
            document.body.innerHTML += '<div style="width:100px; height:2px; border:1px solid blue;"></div>';
        }
        var d2 = new Date();
        console.log(d2 - d1);
    }
    fn();
</script>

<!--通过innerHTML插入内容-->
<script>
    function fn() {
        var d1 = new Date();
        var array = [];
        for ( var i = 0; i < 1000; i++ ) {
            array.push('<div style="width:100px; height:2px; border:1px solid blue;"></div>');
        }
        document.body.innerHTML = array.join('');
        var d2 = new Date();
        console.log( d2 - d1 );
    }
    fn();
</script>

<!--通过createElement插入内容-->
<script>
    function fn() {
        var d1 = new Date();
        
        for ( var i = 0; i < 1000; i++ ) {
            var div = document.createElement('div');
            div.style.width = '100px';
            div.style.height = '2px';
            div.style.border = '1px solid red';
            document.body.appendChild( div );
        }
        var d2 = new Date();
        console.log( d2 - d1 );
    }
    fn();
</script>

8.2、案例

根据数据动态创建表格

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    #box table {
      border-collapse: collapse;
    }
  </style>
</head>

<body>
  <div id="box"></div>
  <script>
    // var s = {name: 'zs', subject: '语文', score: 90};
    // 模拟数据
    var datas = [{
        name: 'zs',
        subject: '语文',
        score: 90
      },
      {
        name: 'ls',
        subject: '数学',
        score: 80
      },
      {
        name: 'ww',
        subject: '英语',
        score: 99
      },
      {
        name: 'zl',
        subject: '英语',
        score: 100
      },
      {
        name: 'xs',
        subject: '英语',
        score: 60
      },
      {
        name: 'dc',
        subject: '英语',
        score: 70
      }
    ];

    // 表头数据
    var headDatas = ['姓名', '科目', '成绩', '操作'];

    // 1 创建table 元素
    var table = document.createElement('table');
    box.appendChild(table);

    // 2 创建表头
    var thead = document.createElement('thead');
    table.appendChild(thead);

    var tr = document.createElement('tr');
    thead.appendChild(tr);
    tr.style.height = '40px';
    tr.style.backgroundColor = 'lightgray';

    // 遍历头部数据,创建th
    for (var i = 0; i < headDatas.length; i++) {
      var th = document.createElement('th');
      tr.appendChild(th);
      // th.innerText
      setInnerText(th, headDatas[i]);
    }

    // 3 创建数据行
    var tbody = document.createElement('tbody');
    table.appendChild(tbody);
    tbody.style.textAlign = 'center';
    for (var i = 0; i < datas.length; i++) {
      // 一个学生的成绩 {name: 'zl', subject: '英语', score: 100},
      var data = datas[i];
      tr = document.createElement('tr');
      tbody.appendChild(tr);

      // 遍历对象
      for (var key in data) {
        var td = document.createElement('td');
        tr.appendChild(td);
        setInnerText(td, data[key]);
      }
      // 生成删除对应的列
      td = document.createElement('td');
      tr.appendChild(td);
      // 删除的超链接
      var link = document.createElement('a');
      td.appendChild(link);
      link.href = 'javascript:void(0)';
      setInnerText(link, '删除');

      link.onclick = linkDelete;
    }

    function linkDelete() {
      // removeChild()
      // 获取父元素
      // 
      // 获取要删除的行
      var tr = this.parentNode.parentNode;
      tbody.removeChild(tr);
      return false;
    }

    function setInnerText(element, content) {
      // 判断当前浏览器是否支持 innerText
      if (typeof element.innerText === 'string') {
        element.innerText = content;
      } else {
        element.textContent = content;
      }
    }
  </script>
</body>

</html>

选择水果

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        select {
            width: 200px;
            height: 200px;
            background-color: #33cccc;
            font-size: 20px;
        }
    </style>
</head>

<body>
    <select id="left" multiple="multiple">
        <option value="0">苹果</option>
        <option value="1">橘子</option>
        <option value="2">梨</option>
        <option value="3">西瓜</option>
        <option value="4">水蜜桃</option>
    </select>

    <input type="button" value=">>" id="btn1">
    <input type="button" value="<<" id="btn2">
    <input type="button" value=">" id="btn3">
    <input type="button" value="<" id="btn4">

    <select id="right" multiple="multiple">

    </select>

    <script>
        var left = document.getElementById("left");
        var right = document.getElementById("right");

        // 保留一份原始副本
        var conetent = left.innerHTML;

        // 当我们点击btn1的时候,把左边的都移到右边
        var btn1 = document.getElementById("btn1");
        var btn2 = document.getElementById("btn2");
        var btn3 = document.getElementById("btn3");
        var btn4 = document.getElementById("btn4");

        btn1.onclick = function () {
            // childElementCount 获取元素子节点的个数
            if (left.childElementCount != 0) {
                right.innerHTML = conetent;

                left.innerHTML = "";
            }
        }

        // 当我们点击btn2的时候,把右边的都移到左边
        btn2.onclick = function () {
            if (right.childElementCount != 0) {
                left.innerHTML = conetent;
                right.innerHTML = "";
                // 把 leftToRight数组清空;
                leftToRight = [];
            }
        }

        // 当我们点击btn3的时候,哪个option处于选中状态,则移到右边
        var leftToRight = [];

        btn3.onclick = function () {
            // 点击按钮3,遍历左边所有的option,如果option的selected属性为true,则移到右边
            // 获取左侧所有的option
            var leftOptions = left.children;

            for (var i = 0; i < leftOptions.length; i++) {
                // 获取得到每个左侧的option
                var op = leftOptions[i];
                if (op.selected) {
                    // 不能在这里面使用下标
                    leftToRight.push(op);

                    // 不要在这里面直接挪动,而是放到循环外面挪动
                    // 状态改为不选中
                    op.selected = false;
                }
            }

            // 为了移到右侧,还能保持顺序
            var leftToRightSort = sortArray(leftToRight); //西瓜  水蜜桃 苹果    

            // 把数组里面的元素节点放到右边
            for (var i in leftToRightSort) {
                if (leftToRightSort[i]) {
                    right.appendChild(leftToRightSort[i]);
                }
            }
        }

        function sortArray(arr) { // 西瓜  水蜜桃 苹果 
            var newArr = [];
            for (var i = 0; i < arr.length; i++) {
                var op = arr[i]; //获取到参数数组的每一个option
                newArr[op.value] = op; //[苹果,undefined,undefined,西瓜,水蜜桃]
            }
            return newArr;
        }
    </script>
</body>

</html>