JavaScript DOM BOM 笔记

247 阅读18分钟

DOM

DOM树

获取元素

  • 根据ID获取    getElementById()

    document.getElementById() //参数是大小写敏感的字符串

    2021-5-15
  • 根据标签名获取   getElementsByTagName()

    • 知否知否,应是绿肥红瘦
    • 知否知否,应是绿肥红瘦
    • 知否知否,应是绿肥红瘦
    • 知否知否,应是绿肥红瘦
    • 知否知否,应是绿肥红瘦
    1. 生僻字
    2. 生僻字
    3. 生僻字
    4. 生僻字
    5. 生僻字
  • 通过HTML5新增的方法获取

    document.getElementsByClassName('类名'); //根据类名获得某些元素的集合

    document.querySelector('选择器'); //返回指定选择器中的第一个元素对象 例:var firstBox = document.querySelector('.box'); // .表示类 var nav = document.querySelector('#nav');//#表示id var li = document.querySelector('li'); //标签

    document.querySelectorAll('选择器'); //根据指定选择器返回所有元素对象集合 例:var allBox = document.querySelectorAll('.box');

  • 获取body  html标签

    //获取body var bodyEle = document.body;

    //获取html var htmlELe = document.documentElement;

事件

1.事件由三部分组成: 事件源 事件类型 事件处理程序(事件三要素)

事件源: 事件被触发的对象  谁  按钮

事件类型: 如何触发 什么事件  比如 鼠标点击 还是鼠标经过  还是键盘按下

事件处理程序: 通过一个函数赋值的方式 完成

<body>
    <button id="btn">唐伯虎</button>
    <script>
        var btn = document.getElementById('btn');
        //btn事件源  onclick 时间类型  function 事件处理程序
        btn.onclick = function () {
            alert('点秋香');
        }
    </script>

2.执行事件的步骤

1)获取事件源

2)注册事件(绑定事件)

3)添加事件处理程序(采取函数赋值形式)

3. 常用的鼠标事件

onclick                                            鼠标点击左键触发
onmouseover                                        鼠标经过触发
onmouseout                                         鼠标离开触发
onfocus                                            获得鼠标焦点触发
onblur                                             失去鼠标焦点触发
onmousemove                                        鼠标移动触发
onmouseup                                          鼠标弹起触发
onmousedown                                        鼠标按下触发

操作元素

1.改变元素内容

element.innerText 从起始位置到终止位置的内容,但它不识别html标签,同时空格和换行也会被去掉

element.innerHTML 从起始位置到终止位置的全部内容,包括html标签,同时保留空格和换行

这两个标签是可读写的,可以获取元素里面的内容

<body>
    <p>
        我是文字
        <span>123</span>
    </p>
    <script>
        var p = document.querySelector('p');
        console.log(p.innerText);
        console.log(p.innerHTML);
    </script>
 </body>
········································································
输出结果:
 我是文字 123
------------------------------

        我是文字
        <span>123</span>
    

2. 常用元素的属性操作

src、href、id、title、alt等

3.表单元素的属性操作

表单属性:

type、value、checked、selected、disabled

<body>
    <button>按钮</button>
    <input type="text" value="输入内容">
    <script>
        // 1.获取元素
        var btn = document.querySelector('button');
        var input = document.querySelector('input');
        // 2.注册事件 处理程序
        btn.onclick = function () {
            //表单里面的值 文字内容通过value而不是innerText innerHTML来处理
            input.value = "被点击了";
            // btn.disabled = true;
            //this指向的是事件函数的调用者btn
            this.disabled = true;
        }
    </script>
</body>

4. 样式属性操作

1.element.style      行内样式操作
2.element.className  类名样式操作
//JS修改style样式操作  产生的是行内样式 CSS权重比较高
//JS 里面的样式采用驼峰命名法 比如fontSize backgroundColor
//className会覆盖原先的类名
<head>
    <style>
        div: {
            width:200px;
            height:200px;
            background-color: pink;
        }
    </style>
</head>
<body>
    <div></div>
    <script>
        var div = document.querySelector('div');
        div.onclick = function() {
            div.style.backgroundColor = 'purple';
            this.style.width = '250px';
        }
    </scrip>
</body>

------------------------------------------------------------
<head>
    <style>
        div: {
            width:200px;
            height:200px;
            background-color: pink;
        }
        .change {
            background-color: purple;
            color: #fff;
            font-size: 25px;
            margin-top: 100px;
        }

    </style>
</head>
<body>
    <div class="first">文本</div>
    <script>
        var div = document.querySelector('div');
        div.onclick = function() {
//    当样式比较多或者功能复杂   可以用className直接修改类
            this.className = 'change';
//如果想在原先的类的基础上加新的类
            this.className = 'first change';
        }
    </scrip>
</body>

5.自定义属性的操作

1.获得属性的值
element.属性    获得内置的属性
element.getAttribute('属性')   获得新的自定义的属性

例:
<div id="demo" index="1"></div>
//id是内置属性  index是自定义属性
var div = document.querySelector('div');
console.log(div.id);
2.设置属性值
element.属性 = '值'   设置内置属性值
element.setAttribute('属性','值')   主要针对自定义属性
例:
div.id = "test";

3.移除属性
element.removeAttribute('属性')
例:
div.removeAttribute('index')

//H5里面规定 所有自定义属性都以data-开头  
新增的获取自定义属性的方法element.dataset.index 或者 element.dataset['index']
dataset 是存放了所有以data开头的自定义属性的集合
比如上面的自定义属性index 改成data-index
兼容性不太行 建议用getAttribute

<div data-index="2" data-list-name="andy"></div>
var div = document.querySelector('div');
console.log(div.dataset.index); // 2  这里就不用加data-了
console.log(div.dataset['index'];//2
console.log(div.dataset.listName);//andy    注意如果自定义属性里面有多个-连接的单词 获取需要驼峰命名法

节点操作(获取元素的另一种方法)

1.节点概述

一般来说,元素、属性、文本全是节点。节点至少拥有nodeType(节点类型)、nodeName(节点名称)和nodeValue(节点值)三个基本属性

元素节点的nodeType为1

属性节点的nodeType为2

文本节点的nodeType为3(文本节点包括文字,空格,换行等)

2.节点层级

  • 父子节点

node.parentNode 得到的是离节点最近的父级节点,找不多返回null

node.childNodes 得到的是指定节点的子节点的集合,该集合为实时更新的集合。包含元素节点、文本节点等等

node.children 是一个只读属性,返回所有的子元素节点,只返回子元素节点  

node.firstChild 返回第一个子节点 不管是文本节点还是元素节点

node.lastChild 同上

node.firstElementChild 返回第一个子元素节点 有兼容性问题

node.lastElementChild 返回最后一个子元素节点 有兼容性问题

实际开发中 一般用node.children[0]来表示第一个子元素节点,一般用node.children[node.children.length-1]来表示最后一个子元素节点

  • 兄弟节点

node.nextSibling  返回当前元素的下一个兄弟节点 找不到则返回null。同样也包含了所有的节点

node.previousSibling 返回当前元素的上一个兄弟节点。同上  

node.nextELementSibling 返回当前元素的下一个兄弟元素节点 找不到则返回null。IE9以上才兼容

node.previousElementSibling 返回当前元素的上一个兄弟元素节点 同上

为了解决兼容性问题,自己封装一个函数:

function getNextElementSibling(element) {
    var el = element;
    while (el = el.nextSibling) {
        if (el.nodeType === 1) {
            return el;
        }
    }
    return null;
}

3. 创建节点 & 添加节点

document.createElement('tagName')

该方法创建由tagName指定的HTML元素。也称为动态创建元素节点

node.appendChild(child)   将一个节点添加到指定父节点的子节点列表的末尾。node父级  child是子集

node.insertBefore(child,指定元素) 将 child添加到父节点的指定子节点前面。

4.删除节点

node.removeChild(child)  从DOM中删除一个子节点 ,返回被删除的子节点

小tips:元素里面阻止链接跳转 可以href='javascript:;'

5.复制节点(克隆节点)

node.cloneNode() 返回调用该方法的节点的一个副本,也称为克隆节点

如果参数为空或者false,则是浅拷贝,只复制标签,不复制里面的内容;如果括号里面为true,则为深拷贝。复制标签和内容。

6.三种 动态创建元素的区别

document.write() 是直接将内容写入页面的内容流,但文档执行完毕,则他会导致页面全部重绘

innerHTML 创建多个元素效率更高(不要拼接字符串,而是采用数组形式拼接) 结构稍微复杂

createElement()创建多个元素效率稍微低一点,但结构更清晰

注册事件(绑定事件)

1.注册事件概述

给元素添加事件,称为注册事件或者绑定事件

注册事件由两种方式**:传统方式方法监听注册方式**

传统注册方式:

  • 利用on开头事件 onclick
  • btn.onclick = function(){}
  • 特点:注册事件的唯一性:同一个元素同一个事件只能设置一个处理函数,最新的处理函数会覆盖前面的函数

方法监听注册事件:

  • addEventListener()他是一个方法
  • IE9之前不支持,可以用attachEvent()
  • 特点:同一个元素同一个事件可以注册多个监听器

2.addEventListener事件监听方式

eventTarget.addEventListener(type, listener[, useCapture])

该方法将制定的监听器注册到eventTarget(目标对象)上,当该对象触发制定的事件时,就会执行事件处理函数。

该方法接受三个参数

type:事件类型字符串 比如click mouseover 注意这里不要带on

listener:事件处理函数 事件发生时,会调用该函数

useCapture:可选参数 布尔型 默认false

3.解绑事件

传统方式:

element.onclick = null;

方法监听注册方式:

element.removeEventListener(type, listener) //addEventListener()

element.detachEvent(type,listener) //attachEvent()

divs[1].addEventListener('click',fn); //要用这种移除方法 就要给function起个名字
function fn() {
    alert(22);
    divs[1].removeEventListener('click',fn);   //该函数弹出一次后就解绑自己 
}

divs[2].attachEvent('onclick',fn1); //要用这种移除方法 就要给function起个名字
function fn1() {
    alert(222);
    divs[2].detachEvent('click',fn1);   //该函数弹出一次后就解绑自己 
}

4.DOM事件流

事件流描述的是从页面接受事件的顺序

事件发生的时候会在元素节点之间按照特定的顺序传播,这个传播过程就是DOM事件流

比如给一个div注册了点击事件:

1.捕获阶段

2.当前目标阶段

3.冒泡阶段

onclick 和 attachEvent只能得到冒泡阶段

addEventListener 第三个参数如果是true,得到捕获阶段 ;如果是false或者默认 表示冒泡阶段。如果点击一个元素,元素和它的父元素都有监听事件,那么捕获阶段会先执行父元素的函数,再执行该元素的函数。冒泡则相反。

5.事件对象

var div = document.querySelector('div');
div.onclick = function(event){
    console.log(event);
}

div.addEventListener('click',function(e){console.log(e);})
//event就是一个事件对象,写在小括号里,当做形参来看
//事件对象只有有了事件才会存在,系统自己创建
//事件对象是我们事件的一系列相关数据的集合  跟事件相关的,比如鼠标点击就包含了鼠标的信息
//这个事件对象我们可以自己命名 比如event evt e等
//事件对象也有兼容性问题  ie678通过window.event     兼容性写法 event = event || window.event;

e.target 返回的是触发事件的对象(元素)  this返回的是绑定事件的对象(元素)

即 e.target 点击了哪个元素,就返回哪个元素 this是哪个元素绑定了这个点击事件 就返回谁

var a = document.querySelector('a');
a.addEventListener('click',function(e) {
    e.preventDefault();
})

6.事件委托(事件代理)

原理:不是每个子节点单独设置事件监听器,而是事件监听器设置在其父节点上,然后利用冒泡原理影响每个子节点

<ul>
    <li>知否知否,应是绿肥红瘦</li>
    <li>知否知否,应是绿肥红瘦</li>
    <li>知否知否,应是绿肥红瘦</li>
    <li>知否知否,应是绿肥红瘦</li>
    <li>知否知否,应是绿肥红瘦</li>
</ul>
//点击每个li都弹出对话框,以前需要给每个li都注册事件,辛苦且访问DOM次数越多,延长整个页面的交互就绪事件
//事件委托:给ul注册事件,然后利用事件对象的target来找到当前点击的li,因为点击li事件会冒泡到ul上,ul有注册事件,就会触发监听器
//只访问了一次DOM 提高交互速度

小tips:禁止选中 文字和禁止右键菜单

document.addEventListener('contextmenu', function(e) {
    e.preventDefault();
}) // 禁止右键菜单

document.addEventListener('selectstart', function(e) {
    e.preventDefault();
})

7.鼠标事件对象

8.键盘事件对象

三个事件执行顺序:keydown->keypress->keyup

keyup keydown 不区分事件大小写 a和A都得到65

keypress区分

e.keyCode 键盘按键的ASCII码值

BOM

1.BOM概述

BOM即浏览器对象模型,提供了独立于内容而与浏览器窗口进行交互的对象,核心对象是window

BOM比DOM更大,它包含DOM

window对象是浏览器的顶级对象,它有双重角色

1.它是JS访问浏览器窗口的一个接口

2.它是一个全局对象,定义在全局作用域中的变量、函数都会变成window对象的属性和方法

var num = 10;
console.log(num);
console.log(window.num);  //在调用的时候可以省略window

function fn(){
    console.log(11);
}
fn();
window.fn();

2.window对象的常见事件

  • 窗口加载事件  等页面所有内容都加载完了才执行这个函数

    window.onload = function(){} //传统刚注册事件方式只能写一次 如果有多个,以最后一个为准 或者 window.addEventListener('load',function(){}); //不存在这个问题

    document.addEventListener('DOMContentLoaded',function(){}) 该事件仅当DOM加载完成时候触发 不包括样式表 图片flash等等 IE9以上才支持

  • 调整窗口大小事件  只要窗口大小发生像素变化就会触发

    window.onresize = function(){} window.addEventListener = ("resize",function(){});

3.定时器

 setTimeout(调用函数 [, 延迟的毫秒数]) 用于设置一个定时器,该定时器在定时器到期后执行调用函数  window. 可以省略

setTimeout(function() {
    console.log('时间到了');
},2000);  
//调用函数可以直接写函数,也可以写函数名  或者 '函数名()'

function callback (){
    console.log('时间到了');
}
var time1 = setTimeout(callback,3000);
var time2 = setTimeout('callback',5000);

停止setTimeout()定时器window.clearTimeout (timeoutID);

window.可以省略

window.clearTimeout (timeoutID); 

var timer = setTimeout(function(){}, 5000);
btn.addEventListener('click', function() {
    clearTimeout(timer);
})

setInterval()计时器 每间隔一段时间 就去调用这个函数

setInterval(回调函数,[间隔的毫秒数]);

两个计时器基本相同,只是一个只执行一次 一个循环执行

停止setInterval()计时器

clearInterval();

4.this指向

//1.全局作用域或者普通函数中this指向全局对象window(定时器里面的this指向window)
console.log(this); //window

function fn() {
    console.log(this);
}
window.fn();  // window

window.setTimeout(function() {
    console.log(this);
},1000);    //window

//2.方法调用中 谁调用 this就 指向谁
var o = {
    sayHi: function() {
        console.log(this); // this 指向的是o这个对象
    }
}
o.sayHi();

var btn = document.querySelector('button');
btn.onclick = function(){
    console.log(this);  //this 指向btn
}

//3.构造函数中this指向构造函数的实例
function Fun() {
    console.log(this); //this指向的是fun 实例对象
}
var fun = new Fun();

5.JS 执行机制

JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。这是因为 Javascript 这门脚本语言诞生的使命所致——JavaScript 是为处理页面中用户的交互,以及操作 DOM 而诞生的。比如我们对某个 DOM 元素进行添加和删除操作,不能同时进行。 应该先进行添加,之后再删除。

为了解决这个问题,利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程。于是,JS 中出现了同步和异步。

**同步:**前一个任务结束后再执行后一个任务,程序的执行顺序与任务的排列顺序是一致的、同步的。比如做饭的同步做法:我们要烧水煮饭,等水开了(10分钟之后),再去切菜,炒菜。

**异步:**你在做一件事情时,因为这件事情会花费很长时间,在做这件事的同时,你还可以去处理其他事情。比如做饭的异步做法,我们在烧水的同时,利用这10分钟,去切菜,炒菜。

同步任务:都放在主线程上执行 ,形成一个执行栈

异步任务:通过回调函数实现的,有以下三类

           1.普通事件:click ,resize

           2.资源加载 如load、error等

           3.定时器,包括setInterval、setTimeout等

异步任务相关回调函数添加到任务队列中(也叫消息队列)

JS执行机制

1.先执行栈中的同步任务

2.异步任务(回调函数)放入任务队列中

3.一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行

事件循环参见文章:【JS】深入理解事件循环,这一篇就够了!(必看) - 知乎 (zhihu.com)

6.location对象

location.assign() 可以跳转页面 ,记录浏览历史,也可以后退

7.navigator对象

包含有关浏览器的信息,有很多属性 最常用的userAgent

8.history对象

与浏览器的历史记录进行交互 该对象包含用户(在浏览器窗口中)访问过的URL

PC端网页特效

1.offset:动态的得到钙元素的位置(偏移)、大小等

offset与style的区别

2.client 客户端 获取元素可视区的相关信息

3.立即执行函数

不需要调用,立马能够自己执行的函数

作用:创建了一个独立的作用域,避免了命名冲突问题

//写法
(function() {})();  或者 (function(){}()); //加个小括号表示调用
(function(形参) {})(实参);  或者 (function(形参){}(实参));

4.scroll 

滚动的,利用scroll属性可以动态得到该元素的大小,滚动距离等

注意:元素被卷去的头部是element.scrollTop,如果是页面被卷去的头部是window.pageYOffset

scrollHeight是返回内容的高度,clientHeight是返回盒子的高度

5.mouseenter 和 mouseover的区别

当鼠标移动到元素上时会触发mouseenter事件,与mouseover的区别是,mouseover经过自身盒子会触发,经过子盒子还会触发(冒泡)。mouseenter只在经过自身盒子触发,不会冒泡。

mouseenter搭配 鼠标离开mouseleave 同样不会冒泡

6.动画函数封装

动画实现原理:通过定时器setInterval()不断移动盒子位置

实现步骤

1.获得盒子当前位置

2.让盒子在当前位置上加1个移动距离

3.利用定时器不断重复操作

4.加上一个结束定时器的条件

5.注意此元素需要添加定位,才能使用element.style.left

简单动画函数封装:(obj 目标对象, target 目标位置)

function animate(obj,target) {
    clearInterval(obj.timer);//先清除以前的定时器,只保留当前的一个定时器执行
    obj.timer = setInterval(function() { //将计时器作为obj的一个方法,不声明新的变量,不占用新的内存,同时不同的obj有不同的timer                
        if (obj.offsetLeft == target) {
            clearInterval(obj.timer);
        }
        obj.style.left = obj.offsetLeft + 1 + 'px';
    },30);
}

var div = document.querySelector('div');
var span = document.querySelector('span');

animate(div,300);
animate(span,200); 

缓动动画效果原理

思路:

1.让盒子每次移动的距离慢慢变小,速度会慢慢落下来

2.核心算法:(目标值 - 现在的位置) / 10 作为每次移动的距离 步长

function animate(obj,target) {
    clearInterval(obj.timer);
    obj.timer = setInterval(function() {
        var step = (target - obj.offsetLeft) / 10; //步长公式   
        step = step > 0?Math.ceil(step) : Math.floor(step);  //step大于0向右走,向上取整,反之向下取整        
        if (obj.offsetLeft == target) {
            clearInterval(obj.timer);
        }
        obj.style.left = obj.offsetLeft + step + 'px';
    },30);

动画函数添加回调函数

函数可以作为一个参数,将这个函数作为参数传到另一个函数里面,当另一个函数执行完之后,再执行传进去的这个函数,这个过程就叫回调

function animate(obj,target, callback) {
    clearInterval(obj.timer);
    obj.timer = setInterval(function() {
        var step = (target - obj.offsetLeft) / 10; 
        step = step > 0?Math.ceil(step) : Math.floor(step);  
        if (obj.offsetLeft == target) {
            clearInterval(obj.timer);
            if (callback) {  //回调函数执行
                callback();
            }
        }
        obj.style.left = obj.offsetLeft + step + 'px';
    },30);

btn.addEventListener('click', function() {
    animate(span, 800, function() {            //回调函数  animate里面其他步骤执行完 执行这个function
        alert('你好吗');
    });
})

节流阀

移动端

触屏事件

触摸事件对象 TouchEvent

TouchEvent是一类描述手指在触屏的状态变化的事件。用于描述一个或多个触电,使得开发者可以检测触点的移动,增加和减少等等

 

div.addEventListener('touchstart',function(e) {
    console.log(e);
    console.log(e.touches);
    console.log(e.targetTouches);
    console.log(e.changedTouches);
});

classList 属性  返回元素的类名

<div class="one two"></div>
var div = document.querySelector('div');
console.log(div.classList); // ['one','two']
//添加类名 追加类名 不覆盖之前的类名
div.classList.add('three');
//删除类名
div.classList.remove('three');
//切换类名  有则去掉 无则添加
div.classList.toggle('bg');

本地存储

window.sessionStorage

1.生命周期为关闭浏览器窗口

2.在同一个窗口(页面)下数据可以共享

3.以键值对的形式存储使用

存储数据
sessionStorage.setItem(key,value)

获取数据
sessionStorage.getItem(key)

删除数据
sessionStorage.removeItem(key)

删除所有数据
sessionStorage.clear();

window.localStorage

1.生命周期永久有效,除非手动删除 否则关闭页面也会存在

2.可以多窗口(页面)共享(同一浏览器可以共享)

3.以键值对形式存储

方法同sessionStorage

Promise:

1.Promise使用

注意:1.执行器函数executor里面改变状态方法有三种:resolve()、reject()和抛出异常。抛出异常会让Promise变为失败状态。

注意:then方法,里面可以写一个参数,onResolved,可以省略onRejected。如果只想写一个onRejected,那么要写成(null,onRejected)

注意:4.Promise.resolve(value) 如果传入的值不是promise对象,则返回一个成功的promise对象。如果传入的是一个promise对象,则参数的结果决定了resolve的结果

let p1 = new Promise((resolve,reject) => {
    resolve('OK');
})
let p2 = Promise.resolve('Success');
let p3 = Promise.resolve('Oh Yeah');

const result = Promise.all([p1,p2,p3]); //成功的promise

2.Promise关键问题

  1. 一个Promise指定多个成功/失败回调函数,都会调用吗?

当promise改变为对应状态时,都会调用

  2.改变promise状态和指定回调函数谁先谁后?

都有可能。如果先指定的回调(只是说定义,而不是执行),那么当状态发生改变,回调函数就会调用得到数据。如果先改变状态,那么当指定回调时,回调函数就会调用,得到数据。

  3.例题 来判断异步同步的问题

let p = new Promise((resolve, reject) => {
    resolve('OK')
})

let result = p.then(value => {
    console.log(value);   //2.then方法为微任务,执行完第一个console.log之后执行then,于是return之后,result状态变为成功
    return value
},reason => {
    console.warn(reason);
})

console.log(result); //1.此时result只是进行了定义,但是后面的函数没有执行,没有确定result的状态
setTimeout(() => {
    console.log(result)
},1000);
        
依次输出:
Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined} // console.log(result)输出
OK
Promise {[[PromiseStatus]]: "fulfilled", [[PromiseValue]]: OK}

async与await

async函数

1.函数的返回值为promise对象

2.promise对象的结果由async函数执行的返回值决定

(返回规则同then方法)

async function main(){
    let p =new Promise((resolve,reject) => {
        resolve('OK');
    })
    let res = await p;
    console.log(res);//OK

    let res2 = await 10;
    console.log(res2); //10

    //promise失败了需要try catch捕获
}