JavaScript - DOM事件基本应用

154 阅读9分钟

这是我参与8月更文挑战的第15天,活动详情查看:8月更文挑战

事件

• 事件:在什么时候做什么事

• 执行机制:触发--响应机制

绑定事件(注册事件)三要素

  1. 事件源:给谁绑定事件

  2. 事件类型:绑定什么类型的事件 click 单击

  3. 事件函数:事件发生后执行什么内容,写在函数内部

事件监听

• JavaScript 解析器会给有绑定事件的元素添加一个监听,解析器会一直监测这个元素,只要触发对应的绑定事件,会立刻执行事件函数。

常用事件监听方法

• 方法1:绑定 HTML 元素属性。

<input type="button" id="btn" value="点击有惊喜" onclick="alert('点我做什么?')">

• 方法2:绑定 DOM 对象属性。

<input type="button" id="btn" value="点击有惊喜">
    <script>
        // 获取元素
        var btn = document.getElementById("btn");
        // 添加绑定事件
        btn.onclick = function () {
            // 定义的是事件被触发后要做的事情
            alert("点我干嘛?");
        };
    </script>

常用的鼠标事件类型

onclick 鼠标左键单击触发

ondbclick 鼠标左键双击触发

onmousedown 鼠标按键按下触发

onmouseup 鼠标按键放开时触发

onmousemove 鼠标在元素上移动触发

onmouseover 鼠标移动到元素上触发 (从外界移动到元素上)

onmouseout 鼠标移出元素边界触发 (从元素上移动到外界)

onfocus 获得焦点

onblur 失去焦点

DOM 元素属性操作

非表单元素的属性

• 例如:href、title、id、src 等。

• 调用方式:元素对象打点调用属性名,例如 obj.href。

注意:

部分的属性名跟关键字和保留字冲突,会更换写法。

class → className

for → htmlFor

rowspan → rowSpan

• 属性赋值:给元素属性赋值,等号右侧的赋值都是字符串格式。

    <img src="images/a.jpg" alt="美女" class="pic" id="pic">
    <script>
        // 等号赋值,值必须是字符串类型的
        pic.src = "images/b.jpg";
    </script>

获取标签内部内容的属性

• 获取标签内部内容的属性有两个:innerHTML 和 innerText

  • innerHTML属性,在获取标签内部内容时,如果包含标签,获取的内容会包含标签,获取的内容包括空白换行等。

  • innerText属性,在获取标签内部内容时,如果包含标签,获取的内容会过滤标签,获取的内容会去掉换行和缩进等空白。

<body>
  <div id="box">
    这是一个 div 标签
    <span>这是一个 span 标签</span>
  </div>
  <script>
    // 获取元素
    var box = document.getElementById("box");
   
    // 调用标签内部内容
    console.log(box.innerHTML);
    console.log(box.innerText);
  </script>

image.png

更改标签内容

还可以通过两个属性给双标签内部去更改内容:

• innerHTML 设置属性值,有标签的字符串,会按照 HTML 语法中的标签加载

<body>
  <div id="box">
    这是一个 div 标签
    <span>这是一个 span 标签</span>
  </div>
  <script>
    // 获取元素
    var box = document.getElementById("box");
    
    // 设置标签内部的内容
    box.innerHTML = "<h2>hello JS</h2>";
  </script>

image.png

• innerText 设置属性值,有标签的字符串,会按照普通的字符加载

<body>
  <div id="box">
    这是一个 div 标签
    <span>这是一个 span 标签</span>
  </div>
  <script>
    // 获取元素
    var box = document.getElementById("box");
  
    // 设置标签内部的内容
    box.innerText = "<h2>hello JS</h2>";
  </script>

image.png

对比使用场景:

• innerText:在设置纯字符串时使用。

• innerHTML:在设置有内部子标签结构时使用。

表单元素属性

• value 用于大部分表单元素的内容获取(option除外)

• type 可以获取input标签的类型(输入框或复选框等)

• disabled 禁用属性

• checked 复选框选中属性

• selected 下拉菜单选中属性

<body>
  <input type="button" value="按钮" id="btn"><br>
  <input type="text" id="txt">
  <select id="list">
    <option value="beijing">北京</option>
    <option value="shanghai">上海</option>
  </select>
  <script>
    // 获取元素
    var btn = document.getElementById("btn");
    var txt = document.getElementById("txt");
    var list = document.getElementById("list");
    var opts = list.getElementsByTagName("option");
    
    // value 属性
    console.log(btn.value);
    console.log(txt.value);
    console.log(opts[0].value);
    console.log(opts[0].innerHTML);
  </script>

image.png

    // 更改 input 标签的 value
    btn.value = "点击";
    txt.value = "请输入内容"

image.png

注意: 在 DOM 中元素对象的属性值只有一个时,会被转成布尔值显示。

例如:txt.disabled = true;

    // 表单元素特有的一些属性,属性名和属性值是一致的
    console.log(txt.disabled);
    btn.disabled = true;

image.png

image.png

自定义属性操作

• getAttribute(name) 获取标签行内属性

• setAttribute(name,value) 设置标签行内属性

• removeAttribute(name) 移除标签行内属性

与element.属性的区别 上述三个方法用于获取任意的行内属性,包括自定义的属性。

自定义的属性是不能打点调用的:

    var box = document.getElementById("box");
    // 只能用于元素自有属性
    console.log(box.id);
    console.log(age.id);
    console.log(sex.id);

image.png

    // 元素自定义的新属性不能用点语法直接调用
    // 可以调用元素对象的获取自定义属性的方法
    console.log(box.getAttribute("age"));
    console.log(box.getAttribute("sex"));
    // 也可以调用自有的属性
    console.log(box.getAttribute("id"));

    // 设置属性,添加新的自定义属性或者自有属性
    box.setAttribute("age","20");
    box.setAttribute("city","Beijing");
    // 传的参数不需要进行属性名的修改
    box.setAttribute("class","demo");

    // 移除属性
    box.removeAttribute("age");
    box.removeAttribute("class");

style 样式属性操作

• 使用 style 属性方式设置的样式显示在标签行内。

• element.style 属性的值,是所有行内样式组成的一个样式对象。元素对象的 style 属性的值是一个行内样式组成对象,对象内部封装了所有的行内的样式属性及属性值.

image.png

• 样式对象可以继续点语法调用或更改 css 的行内样式属性,例如 width、height 等属性。

注意:

  1. 类似 background-color 这种复合属性的单一属性写法,是由多个单词组成的,要改为驼峰命名方式书写 backgroundColor。

  2. 通过样式属性设置宽高、位置的属性类型是字符串,需要加上 px 等单位

<body>
    <input type="button" value="按钮" id="btn">
    <div id="box" style="width: 100px" class="bg">文字</div>
    <script>
        .....
        
        // 元素的样式属性对象可以继续打点调用,获取或设置相关的行内样式属性
        console.log(box.style);
        console.log(box.style.width);
        
        // 注意:如果使用的 css 属性名是复合属性的单一属性,需要更改为驼峰命名法
        console.log(box.style.backgroundColor);
        box.style.width = "200px"; //会层叠掉前面的样式
    </script>
</body>

className 类名属性操作

• 修改元素的 className 属性相当于直接修改标签的类名。

• 如果需要修改多条 css 样式,可以提前将修改后的样式设置到一个类选择器中,后续通过

修改类名的方式,批量修改 css 样式。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .cls {
          width: 100px;
          height: 100px;
          background-color: pink;
        }
    </style>
    <script src="common.js"></script>
</head>
<body>
    <input type="button" value="按钮" id="btn">
    <div id="box">文字</div>
    <script>
        // 问题:实际工作中我们应该选择哪种方法?
        // 获取元素
        var btn = my$("btn");
        var box = my$("box");
        // 给按钮添加事件
        btn.onclick = function () {
          // 1.通过更改类名
          // box.className = "cls";
          // 2.通过元素对象的 style 属性进行设置
          box.style.width = "100px";
          box.style.height = "200px";
          box.style.backgroundColor = "yellow";
        };
    </script>
</body>
</html>

DOM 节点操作

createElement 创建新节点

children 获取节点内部的子元素节点, 只取父子关系的

document.body.appendChild 给 body 添加一个新的节点子元素

document.body.removeChild 移除一个节点子元素

<body>
    <div id="box">div 1</div>
    <div id="demo">
        <p>paragraph 1</p>
        <p>paragraph 2</p>
        <p>paragraph 3</p>
        <div class="inner">
            <p>inner p</p>
        </div>
    </div>
    <script>
        // 生成一个新的元素对象
        var newNode = document.createElement("div");
        newNode.innerHTML = "新的 div";
        // 给 body 添加一个新的节点子元素 (添加到最后位置)
        document.body.appendChild(newNode);
        // 移除一些元素
        var box = document.getElementById("box");
        document.body.removeChild(box);

        // 获取节点内部的子元素节点, 只取父子关系的
        var demo = document.getElementById("demo");
        var child1 = demo.children;
        console.log(child1);
    </script>
</body>

child1: image.png

节点属性

nodeType 节点的类型,属性值为数字,表示不同的节点类型,共 12 种,只读

1 元素节点

2 属性节点

3 文本节点

nodeName 节点的名称(标签名称),只读

nodeValue 节点值,返回或设置当前节点的值. 元素节点的 nodeValue 始终是 null

父子节点常用属性

• childNodes

只读属性,获取一个节点所有子节点的实时的集合,集合是动态变化的。

• children 常用

只读属性,返回一个节点所有的子元素节点(只有父子关系)集合,是一个动态更新的 HTML 元素集合。

• firstChild

只读属性,返回该节点的第一个子节点,如果该节点没有子节点则返回 null。

• lastChild

只读属性,返回该节点的最后一个子节点,如果该节点没有子节点则返回 null。

• parentNode

返回一个当前节点的父节点,如果没有这样的节点,比如说像这个节点是树结构的顶端或者没有插入一棵树中,这个属性返回 null。

• parentElement

返回当前节点的父元素节点,如果该元素没有父节点,或者父节点不是一个 DOM元素,则返回 null。

<body>
    <div id="box">
      <p>段落</p>
      <span>span 小盒子</span>
    </div>
    <script>
        var box = document.getElementById("box");
        // 获取子节点
        console.log(box.childNodes);   //或获取所有类型的子节点
        console.log(box.children);   //或获取所有元素类型的子节点(范围更小)
        console.log(box.firstChild);   //或获取所有类型的子节点的第一个
        console.log(box.firstElementChild);   //或获取所有元素类型的子节点的第一个
        console.log(box.lastChild);   //或获取所有类型的子节点的最后一个
        console.log(box.lastElementChild);   //或获取所有元素类型的子节点的最后一个
        // 获取父级
        console.log(box.parentNode);
        console.log(box.parentElement);
    </script>
</body>

image.png

兄弟节点常用属性

• nextSibling

只读属性,返回与该节点同级的下一个节点,如果没有返回null。

• previousSibling

只读属性,返回与该节点同级的上一个节点,如果没有返回null。

• nextElementSibling

只读属性,返回与该节点同级的下一个元素节点,如果没有返回null。

• previousElementSibling

只读属性,返回与该节点同级的上一个元素节点,如果没有返回null。

注意:nextElementSibling 和 previousElementSibling 有兼容性问题,IE9以后才支持。

<body>
    <div id="box">
        <p>这是段落1</p>
        <p>这是段落2</p>
        <p id="p3">这是段落3</p>
        <p>这是段落4</p>
        <p>这是段落5</p>
    </div>
    
    <script>
        // 获取元素
        var p3 = document.getElementById("p3");
        // 上一个兄弟节点
        console.log(p3.previousSibling);
        // 下一个兄弟节点
        console.log(p3.nextSibling);
        // 上一个兄弟元素的节点
        console.log(p3.previousElementSibling);
        // 下一个兄弟元素的节点
        console.log(p3.nextElementSibling);
        
    </script>
</body>

image.png

创建新节点的方法

• document.createElement("div")

创建元素节点

• document.createAttribute("id")

创建属性节点

• document.createTextNode("hello")

创建文本节点

一般将创建的新节点存在变量中,方便使用。创建的节点是存储在内存中的, 并没有加载到 dom 树上.

节点常用操作方法

节点常用操作方法 1

• parentNode.appendChild(child)

将一个节点添加到指定父节点的子节点列表末尾。

<body>
  <div id="box">
    <p>段落内容 1</p>
    <p id="p2">段落内容 2</p>
    <p>段落内容 3</p>
    <p>段落内容 4</p>
  </div>
  <script src="common.js"></script>
  <script>
    // 创建新的节点
    var div = document.createElement("div");
    var cls = document.createAttribute("class");
    var txt = document.createTextNode("hello");
    // 创建的新的节点,是存储在内存中的,但是并没添加到 DOM 树上
    // 获取元素
    var box = my$("box");
    var p2 = my$("p2");
    box.appendChild(div);
    // 文本节点也可以添加到元素内部
    div.appendChild(txt);
    // 注意:自己创建的元素节点本身也是一个对象,也可以去添加一些新的属性和方法,这些操作将来在元素加载到 DOM 树中时,依旧保留

    // DOM 中原有的节点也可以传给 appendChild 的参数
    // 相当于删除了原来的, 又重新添加了一个元素, 会出现在页面的最后
    box.appendChild(p2);
  </script>
</body>

image.png

• parentNode.replaceChild(newChild, oldChild)

用指定的节点替换当前节点的一个子节点,并返回被替换掉的节点。

    // 替换节点
    box.replaceChild(div,p2);

image.png

• parentNode.insertBefore(newNode, referenceNode)

在参考节点之前插入一个拥有指定父节点的子节点,referenceNode 必须设置,如果 referenceElement 为 null 则 newNode将被插入到子节点的末尾。(也就是说在referenceNode之前添加一个节点, 如果referenceNode为null, 则添加的节点会出现在页面最后)

    // 在某个指定子节点之前添加一个新的子节点
    box.insertBefore(div,p2);

image.png

    // 在某个指定子节点之前添加一个新的子节点
    box.insertBefore(div,null);

image.png

• parentNode.removeChild(child)

移除当前节点的一个子节点。这个子节点必须存在于当前节点中。

    // 移除节点
    box.removeChild(p2);

image.png

节点常用操作方法 2

• Node.cloneNode():克隆一个节点,并且可以选择是否克隆这个节点下的所有内容。参数为 Boolean 布尔值:

  1. 如果为 true, 表示采用深度克隆, 则该节点的所有后代节点也都会被克隆;
  2. 如果为 false, 表示采用浅度克隆, 只克隆该节点本身,节点下的内容不会被克隆。 注意: 一定要设置true或false, 因为前期默认true, 后期又默认false, 为了避免麻烦, 最后写好true还是false

注意:克隆时,标签上的属性和属性值也会被复制,写在标签行内的绑定事件可以被复制,但是通过 JavaScript 动态绑定的事件不会被复制

浅度克隆:

<body>
  <div id="box">
    <p>段落内容 1</p>
    <p id="p2">段落内容 2</p>
    <p>段落内容 3</p>
    <p>段落内容 4</p>
  </div>
  <script src="common.js"></script>
  <script>
    // 获取元素
    ......

    // 克隆元素 box
    // 浅度克隆
    var newBox = box.cloneNode(false);
    document.body.appendChild(newBox);
  </script>
</body>

image.png

深度克隆: 可以给新克隆的元素一个id, 避免与原来的元素一致.

    // 深度克隆
    var newBox = box.cloneNode(true);
    newBox.id = "newBox";
    document.body.appendChild(newBox);

image.png

节点常用操作方法 3

• Node.hasChildNodes():没有参数,返回一个 Boolean 布尔值,来表示该元素是否包含有子节点, 不区分节点类型, 即便是只有一个回车也会返回true。

• Node.contains(child):返回一个 Boolean 布尔值,来表示传入的节点是否为该节点的后代节点。(并不只是局限于父子关系)

<body>
  <div id="box">
    <p>段落内容 1</p>
    <div>
      <p id="p2">段落内容 2</p>
    </div>
    <p>段落内容 3</p>
    <p>段落内容 4</p>
  </div>
  <div id="demo">

  </div>
  <script src="common.js"></script>
  <script>
    // 获取元素
    ......
    
    // 判断有没有子节点,不区分节点类型
    console.log(box.hasChildNodes());
    console.log(demo.hasChildNodes());
    
    // 判断节点内部是否有某个后代节点
    console.log(box.contains(p2));

  </script>
</body>

image.png

可以看到 <div id="demo"> 中只有一个回车, 并没有任何元素, 但结果仍然返回了true

判断当前节点是否有子节点的方法总结

有三种方法可以判断当前节点是否有子节点。

• node.firstChild !== null

• node.childNodes.length > 0

• node.hasChildNodes()

<body>
  <div id="box">
    <p>段落内容 1</p>
    <div>
      <p id="p2">段落内容 2</p>
    </div>
    <p>段落内容 3</p>
    <p>段落内容 4</p>
  </div>
  <div id="demo">

  </div>
  <script src="common.js"></script>
  <script>
    // 获取元素
    var box = my$("box");
    var p2 = my$("p2");
    var demo = my$("demo");
    
    // 判断内部有子节点的方法有三种
    // 1.判断有没有子节点,不区分节点类型
    // console.log(box.hasChildNodes());
    // console.log(demo.hasChildNodes());
    // 2.判断节点内部第一个子节点是否不为空
    console.log(box.firstChild !== null);
    console.log(demo.firstChild !== null);
    // 3.判断子节点的数组对象的长度是否为0
    console.log(box.childNodes.length > 0);
    console.log(demo.childNodes.length > 0);

    // 补充子元素节点是否存在
    console.log(box.children.length > 0);
    console.log(demo.children.length > 0);
    // 判断节点内部是否有某个后代节点
    // console.log(box.contains(p2));

  </script>
</body>

image.png

注意一下 childNodes.lengthchildren.length 的区别.