js2之DOM和BOM基础

398 阅读26分钟

1 DOM简介

文档对象模型(Document Object Model,简称 DOM),是 W3C 组织推荐的处理可扩展标记语言(HTML或者XML)的标准编程接口

W3C 已经定义了一系列的 DOM 接口,通过这些 DOM 接口可以改变网页的内容、结构和样式

1.1 DOM树

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

元素:页面中的所有标签都是元素,DOM 中使用 element 表示

节点:网页中的所有内容都是节点(属性、文本、注释等),DOM 中使用 node 表示

2 获取元素

DOM在我们实际开发中主要就是用来操作元素

获取页面中的元素可以使用以下几种方式:

根据 ID 获取

根据标签名获取

通过 HTML5 新增的方法获取

特殊元素获取

2.1 根据ID获取元素-getElementById()

使用 getElementById() 方法可以获取带有 ID 的元素对象

document.getElementById('idName'); // 参数是 id的名称

使用 console.dir() 可以打印获取的元素对象,更好的查看对象里面的属性和方法

2.2 根据标签名获取元素-getElementsByTagName()

使用 getElementsByTagName() 方法可以返回带有指定标签名的对象的集合

// 参数是 标签名,比如 div p li span 等
document.getElementsByTagName('标签名'); 

需要注意以下:

  • 因为得到的是一个对象的集合,所以我们想要操作里面的元素就需要遍历
  • 得到元素对象是动态的
  • 如果获取不到元素,则返回为空的伪数组(因为获取不到对象)

我们还可以根据标签名获取某个元素(父元素)内部所有指定标签名的子元素

element.getElementsByTagName('标签名');

父元素element必须是单个对象(必须指明是哪一个元素对象),获取的时候不包括父元素自己

 <ul>
    <li>你好,你好</li>
    <li>你好,你好</li>
    <li>你好,你好</li>
    <li>你好,你好</li>
  </ul>
  <ol id="ol">
    <li>生僻字</li>
    <li>生僻字</li>
    <li>生僻字</li>
    <li>生僻字</li>
  </ol>
  <script>
    var lis = document.getElementsByTagName('li');
    // 1.返回的是 获取的元素对象集合,是以伪数组的形式存储的
    console.log(lis, '返回所有的li元素对象集合');
    console.log(lis[0], '获取第一个元素');

    // 2. 如果想要依次打印里面的元素对象,可以采取遍历的方式
    for (var i = 0; i < lis.length; i++) {
      console.log(lis[i]);
    }

    // 3. 还可以根据标签名获取某个元素(父元素)内部所有指定标签名的子元素
    var ol = document.getElementById('ol');
    var lis2 = ol.getElementsByTagName('li');
    console.log(lis2, '返回ol里面的所有的li元素对象集合');
  </script>

2.3 H5方法获取

getElementsByClassName('类名')

根据类名返回元素对象集合

querySelector('选择器')

根据指定选择器返回第一个元素对象

如果有多个元素对象,也只返回第一个元素对象

document.querySelectorAll('选择器')

根据指定选择器返回元素对象的集合

querySelector 和 querySelectorAll里面的选择器需要加符号,比如:document.querySelector('#nav');

 <div class="box">盒子1</div>
  <div class="box">盒子2</div>
  <div id="nav">
    <ul>
      <li>首页</li>
      <li>产品</li>
    </ul>
  </div>
  <script>
    // 1.  getElementsByClassName 根据类名获得某些元素集合
    // 返回的是 获取的元素对象集合,是以伪数组的形式存储的
    var boxs = document.getElementsByClassName('box');
    console.log(boxs, '返回所有的box元素对象集合');


    // 2. querySelector 返回指定选择器的第一个元素对象  切记 里面的选择器需要加符号 .box  #nav
    var firstBox = document.querySelector('.box');
    console.log(firstBox); // 只返回第一个元素对象

    var nav = document.querySelector('#nav');
    console.log(nav);

    var li = document.querySelector('li');
    console.log(li);


    // 3. querySelectorAll()返回指定选择器的所有元素对象集合
    var allBox = document.querySelectorAll('.box');
    console.log(allBox);

    var lis = document.querySelectorAll('li');
    console.log(lis);
  </script>

2.4 特殊元素的获取

2.4.1 获取 body 元素

doucumnet.body  // 返回body元素对象

2.4.2 获取 html 元素

document.documentElement // 返回html元素对象

2.5 classList 属性

classList属性是HTML5新增的一个属性,返回元素的类名。但是ie10以上版本支持

2.5.1 添加类

element.classList.add('类名');

2.5.2 移除类

element.classList.remove('类名');

2.5.3 切换类

element.classList.toggle('类名');

3 事件基础

JavaScript 使我们有能力创建动态页面,而事件是可以被 JavaScript 侦测到的行为,就是触发--- 响应机制

网页中的每个元素都可以产生某些可以触发 JavaScript 的事件,例如,我们可以在用户点击某按钮时产生一个 事件,然后去执行某些操作

3.1 事件三要素

事件源 (哪个元素对象)

事件类型 (什么事件)

事件处理程序 (做什么)

3.2 执行事件的步骤

获取事件源

注册事件(绑定事件)

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

3.3 常见的鼠标事件

鼠标事件触发条件
onclick鼠标点击左键触发
onmouseover和onmouseout -- 有冒泡属性鼠标经过/离开触发
onmouseenter/onmouseleave -- 没有冒泡属性鼠标经过/离开触发
onfocus获取鼠标焦点触发
onblur失去鼠标焦点触发
onmousemove鼠标移动触发
onmouseup鼠标弹起触发
onmousedown鼠标按下触发
// 鼠标点击事件 onclick
var btn = document.getElementById('btn');
btn.onclick = function() {
    alert('点击了按钮');
}

注意:mouseenter 和mouseover的区别:mouseover 鼠标经过自身盒子会触发,经过子盒子还会触发。 mouseenter 只会经过自身盒子触发

3.3.1 禁止鼠标右键菜单 contextmenu

document.addEventListener('contextmenu', function (e) {
    // 阻止 鼠标右键菜单 的默认行为
    e.preventDefault();
})

3.3.2 禁止鼠标选中 selectstart

document.addEventListener('selectstart', function (e) {
    // 阻止 鼠标选中 的默认行为
    e.preventDefault();
})

3.3.3 常用鼠标事件对象

event对象代表事件的状态,跟事件相关的一系列信息的集合。现阶段我们主要是用鼠标事件对象 MouseEvent 和键盘事件对象 KeyboardEvent

鼠标事件对象说明
e.clientX返回鼠标相对于 浏览器窗口可视区X坐标
e.clientY返回鼠标相对于 浏览器窗口可视区Y坐标
e.pageX返回鼠标相对于 文档页面 的X坐标
e.pageY返回鼠标相对于 文档页面 的Y坐标
e.screenX返回鼠标相对于 电脑屏幕 的X坐标
e.screenY返回鼠标相对于 电脑屏幕 的Y坐标

鼠标移动,图片跟随移动

<style>
    img {
        /* 图片尺寸是 96 80 */
        position: absolute;
        top: 2px;
    }
</style>

<img src="images/angel.gif" alt="">
<script>
    var img = document.querySelector('img');
    // 注册 mousemove 鼠标移动 事件
    document.addEventListener('mousemove', function (e) {
        img.style.left = e.pageX - 48 + 'px';
        img.style.top = e.pageY - 40 + 'px';
        // -48 -40 是为了让图片始终位于 鼠标的中间位置
    })
</script>

3.9 常用键盘事件

键盘事件触发条件
onkeyup键盘按键松开被触发(推荐)
onkeydown键盘按键按下被触发
onkeypress键盘按键按下被触发,但是不识别功能键:ctrl shift 左右箭头等
keyCode键盘事件对象属性,返回该键的 ASCII 值

注意事项:

  • 如果使用 addEventListener 不需要加 on
  • onkeydown 和 onkeyup 不区分字母大小写,所以返回的keyCode大小写一样,onkeypress 区分字母大小写,所以返回的keyCode的大小写不一样
  • 三个事件的执行顺序是: keydown - keypress - keyup
  • onkeydown 如果一直按着不松开,会多次触发

3.4 注册事件(绑定事件)

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

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

3.4.1 传统注册方式

特点

利用 on 开头的事件,例如 onclick onmouseover onfocus 等s

特点: 注册事件的唯一性

同一个元素同一个事件只能设置一个处理函数,最后注册的处理函数将会覆盖前面注册的处理函数

语法结构

<button onclick=“alert('hi~')”></button>
btn.onclick = function() {}

3.4.2 方法监听注册方式 addEventListener

特点

w3c 标准 推荐方式

addEventListener() 它是一个方法

lIE9 之前的 IE 不支持此方法,可使用 attachEvent() 代替

特点:同一个元素同一个事件可以注册多个监听器

按注册顺序依次执行

语法结构

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

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

3个参数:

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

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

useCapture: 可选参数,是一个布尔值,默认是 false,就是冒泡

3.4.3 attachEvent 事件监听方式 (IE9之前)

eventTarget.attachEvent(eventNameWithOn, callback) 

eventTarget.attachEvent()方法将指定的监听器注册到 eventTarget(目标对象) 上,当该对象触发指定的事件时,指定的回调函数就会被执行

2个参数:

eventNameWithOn:事件类型字符串,比如 onclick 、onmouseover ,这里要带 on

callback: 事件处理函数,当目标触发事件时回调函数被调用

IE8 及早期版本支持

3.4.4 注册事件兼容性解决方案

function addEventListener(element, eventName, fn) {
    // 判断当前浏览器是否支持 addEventListener 方法
    if (element.addEventListener) {
        element.addEventListener(eventName, fn);  // 第三个参数 默认是false
    } else if (element.attachEvent) {
        element.attachEvent('on' + eventName, fn);
    } else {
        // 相当于 element.onclick = fn;
        element['on' + eventName] = fn;
    }
}

3.5 解绑(删除)事件

// 传统事件 解绑
eventTarget.onclick = null;

// 方法监听注册方式 解绑
eventTarget.removeEventListener(type, listener[, useCapture]);
eventTarget.detachEvent(eventNameWithOn, callback); --- IE9 之前

3.5.1 解绑事件 兼容性

function removeEventListener(element, eventName, fn) {
      // 判断当前浏览器是否支持 removeEventListener 方法
      if (element.removeEventListener) {
        element.removeEventListener(eventName, fn);  // 第三个参数 默认是false
      } else if (element.detachEvent) {
        element.detachEvent('on' + eventName, fn);
      } else {
        element['on' + eventName] = null;
 }

3.6 DOM事件流

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

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

DOM事件流分为3个阶段: 捕获阶段、当前目标阶段、冒泡阶段

事件冒泡: 事件开始时由最具体的元素接收,然后逐级向上传播到到 DOM 最顶层节点的过程

事件捕获: 由 DOM 最顶层节点开始,然后逐级向下传播到到最具体的元素接收的过程

image-20220403111012100

DOM事件流过程:

我们向水里面扔一块石头,首先它会有一个下降的过程,这个过程就可以理解为从最顶层向事件发生的最具体元素(目标点)的捕获过程;之后会产生泡泡,会在最低点( 最具体元素)之后漂浮到水面上,这个过程相当于事件冒泡

image-20220403111145464

注意事项:

  • JS 代码中只能执行捕获或者冒泡其中的一个阶段
  • onclick 和 attachEvent(IE9以下注册事件) 只能得到冒泡阶段
  • addEventListener(type, listener[, useCapture])第三个参数如果是 true,表示在事件捕获阶段调用事件处理程序;如果是 false(不写默认就是false),表示在事件冒泡阶段调用事件处理程序
  • 有些事件是没有冒泡的,比如 onblur、onfocus、onmouseenter、onmouseleave

捕获阶段

<div class="father">
    <div class="son">son盒子</div>
</div>
<script>
    // dom 事件流 三个阶段
    // 捕获阶段 如果addEventListener 第三个参数是 true 那么则处于捕获阶段  document -> html -> body -> father -> son
    // 点击 son div,会依次输出:document father  son  
    var son = document.querySelector('.son');
    var father = document.querySelector('.father');

    son.addEventListener('click', function () {
        alert('son');
    }, true);

    father.addEventListener('click', function () {
        alert('father');
    }, true);

    document.addEventListener('click', function () {
        alert('document');
    }, true);
</script>

冒泡阶段

<div class="father">
    <div class="son">son盒子</div>
</div>
<script>
    // dom 事件流 三个阶段
    // 冒泡阶段 如果addEventListener 第三个参数是 false 或者 省略 那么则处于冒泡阶段  son -> father ->body -> html -> document
    // 点击 son div,会依次输出:son  father  document
    var son = document.querySelector('.son');
    var father = document.querySelector('.father');

    son.addEventListener('click', function () {
        alert('son');
    }, false);

    father.addEventListener('click', function () {
        alert('father');
    }, false);

    document.addEventListener('click', function () {
        alert('document');
    })
</script>

3.7 事件对象 event

event 对象代表事件的状态,比如键盘按键的状态、鼠标的位置、鼠标按钮的状态;

事件发生后,跟事件相关的一系列信息数据的集合都放到这个对象里面,这个对象就是事件对象 event,它有很多属性和方法

这个 event 是个形参,系统帮我们设定为事件对象,不需要传递实参过去

我们注册事件时, event 对象就会被系统自动创建,并依次传递给事件监听器(事件处理函数)

eventTarget.onclick = function(event) {
    
} 
eventTarget.addEventListener('click', function(event) {
    
})
// 这个 event 就是事件对象,我们还喜欢的写成 e 或者 evt 

件对象本身的获取存在兼容问题

标准浏览器中是浏览器给方法传递的参数,只需要定义形参 e 就可以获取到

在 IE6~8 中,浏览器不会给方法传递参数,如果需要的话,需要到 window.event 中获取查找

兼容性方案:e = e || window.event

事件对象常用的属性和方法

e.target:返回触发事件的对象

<button>点击</button>
<script>
    var btn = document.querySelector('button');
    btn.addEventListener('click', function (e) {
        console.log(e.target); // 返回:<button>点击</button>
        console.log(window.event.srcElement); // IE6-8 标准
    });
</script>

e.srcElement:是IE6-8使用

this 和 e.target区别

this 是事件绑定的元素, 这个函数的调用者(绑定这个事件的元素) ,谁调用就指向谁

e.target 是事件触发的元素,谁点击就指向谁

 <div>123</div>
    <ul>
        <li>aaa</li>
        <li>bbb</li>
        <li>ccc</li>
    </ul>
    <script>
        // 1. e.target 返回的是触发事件的对象(元素)  this 返回的是绑定事件的对象(元素)
        // 区别 : e.target 点击了那个元素,就返回那个元素
        //         this 哪个元素绑定了这个点击事件,那么就返回谁

        var div = document.querySelector('div');
        div.addEventListener('click', function (e) {
            // e.target 谁点击(触发)了那个元素,就返回哪个对象
            console.log(e.target); // 返回:<div>123</div>

            // 哪个元素绑定了这个事件,就返回哪个对象
            console.log(this); // 返回:<div>123</div>
        })

        var ul = document.querySelector('ul');
        ul.addEventListener('click', function (e) {
            console.log(this); // 返回:ul对象,因为是ul绑定了事件
            console.log(e.currentTarget); // 返回和this一样,但是ie6-8不兼容

            // e.target 指向我们点击的那个对象 谁触发了这个事件 我们点击的是li e.target 指向的就是li
            console.log(e.target);
        })
    </script>

e.type:返回事件类型

例如:click、focus、blur,不带 on

<button>点击</button>
<script>
    var btn = document.querySelector('button');
    btn.addEventListener('click', function (e) {
        console.log(e.type); // 返回:click
    });
</script>

e.preventDefault() 阻止默认事件

<button>点击</button>
<script>
    var btn = document.querySelector('button');
    btn.addEventListener('click', function (e) {
        // 阻止默认事件
        e.preventDefault();
        
        // IE6-8标准
        window.event.returnValue;
    });
</script>

e.stopPropagation() 阻止冒泡

<button>点击</button>
<script>
var btn = document.querySelector('button');
btn.addEventListener('click', function (e) {
    // 阻止事件 冒泡
    e.stopPropagation();

    // IE6-8标准
    window.eventcancelBubble=true;
});

// 兼容性写法:
if(e && e.stopPropagation){
    e.stopPropagation();
}else{
    window.event.cancelBubble = true;
}

</script>

3.8 事件委托(代理、委派)

事件委托也称为事件代理, 在 jQuery 里面称为事件委派

3.8.1 原理

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

<ul>
    <li>知道了1</li>
    <li>知道了2</li>
    <li>知道了3</li>
    <li>知道了4</li>
    <li>知道了5</li>
</ul>
<script>
    // 事件委托的核心原理:给父节点添加侦听器, 利用事件冒泡影响每一个子节点
    // 给 ul 注册点击事件,然后利用事件对象的 target 来找到当前点击的 li,因为点击 li,事件会冒泡到 ul 上, ul 有注册事件,就会触发事件监听器
    var ul = document.querySelector('ul');
    ul.addEventListener('click', function (e) {
        // e.target 获取每个点击的对象 
        e.target.style.backgroundColor = 'red';
    })
</script>

3.8.2 作用

我们只操作了一次 DOM ,提高了程序的性能

4 操作元素

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

4.1 改变元素的内容

element.innerText

从起始位置到终止位置的内容, 但它去除 html 标签, 同时空格和换行也会去掉

element.innerHTML

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

element.innerText和element.innerHTML,不仅可以设置元素的内容,还可以获取元素的内容

 <button>点击显示当前系统时间</button>
  <div></div>
  <script>
    // 1. 获取元素 
    var btn = document.querySelector('button');
    var div = document.querySelector('div');

    // 2. 注册时间并添加事件处理函数
    btn.onclick = function () {
      // 点击修改 元素的内容 显示  
      div.innerHTML = getDate();
    }

    // 获取当前系统时间
    function getDate() {
      var date = new Date();
      console.log(date)

      var y = date.getFullYear();
      var m = date.getMonth() + 1;
      m = m < 10 ? '0' + m : m;
      var d = date.getDate();
      d = d < 10 ? '0' + d : d;

      var arr = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
      return '今天是' + y + '年' + m + '月' + d + '日 ' + arr[+date.getDay()] + '';
    }

4.2 元素的属性操作

常见的元素属性操作有: src、href、 id、alt、title

element.src=新的属性
  <button id="ldh">刘德华</button>
  <button id="zxy">张学友</button> <br>
  <img src="images/ldh.jpg" alt="我是刘德华" title="刘德华">

  <script>

    // 1. 获取元素
    var ldh = document.getElementById('ldh');
    var zxy = document.getElementById('zxy');
    var img = document.querySelector('img');

    // 2. 注册事件 并添加事件处理程序
    zxy.onclick = function () {
      img.src = 'images/zxy.jpg';
      img.title = '张学友';
      img.alt = '我是张学友图片';
    }

    ldh.onclick = function () {
      img.src = 'images/ldh.jpg';
      img.title = '刘德华';
      img.alt = '我是刘德华图片';
    }
  </script>

4.3 自定义属性操作

4.3.1 获取属性值

// 获取 内置属性值
element.属性  

// 获取自定义属性值
element.getAttribute('属性');

4.3.2 设置属性值

// 只能设置 内置属性值
element.属性 = ‘值’ 

// 设置 自定义 属性值
element.setAttribute('属性', '值'); 

4.3.3 移除属性

element.removeAttribute('属性');

4.4 h5 自定义属性

自定义属性目的:是为了保存并使用数据。有些数据可以保存到页面中而不用保存到数据库中。

自定义属性获取是通过 getAttribute(‘属性’) 获取。

但是有些自定义属性很容易引起歧义,不容易判断是元素的内置属性还是自定义属性,因此,H5给我们新增了自定义属性:

H5规定自定义属性data-开头做为属性名并且赋值

设置属性

// 设置属性 data-index
<div data-index=“1”></div>

// 通过 js 设置属性 data-index
element.setAttribute(‘data-index’, 2)

获取属性

element.getAttribute(‘data-index’);

H5新增 element.dataset.index 或者 element.dataset[‘index’] 的方式获取 自定义属性,但是只有 ie 11才开始支持

4.5 表单元素的属性操作

利用 DOM 可以操作如下表单元素的属性

  type、value、checked、selected、disabled
  <button>按钮</button>
  <input type="text" value="输入内容">

  <script>
    // 1. 获取元素
    var btn = document.querySelector('button');
    var input = document.querySelector('input');

    // 2. 注册事件 并添加事件处理程序
    btn.onclick = function () {
      // 修改input里面的内容
      input.value = '我是新的输入内容';
      // 设置 btn 不可再次点击
      // this 指向的是事件函数的调用者 btn
      this.disabled = true;
    }
  </script>

this 指向的是事件函数的调用者

4.6 样式属性操作

可以通过 JS 修改元素的大小、颜色、位置等样式

JS 里面的样式采取驼峰命名法 ,比如 fontSize、 backgroundColor

JS 修改 style 样式操作,产生的是行内样式,CSS 权重比较高

4.6.1 element.style 行内样式操作

element.style 获得修改元素样式 ,一般是样式比较少,功能简单的的时候使用

// 1. 获取元素
var div = document.querySelector('div');
// 2. 注册事件 处理程序
div.onclick = function() {
    // div.style里面的属性 采取驼峰命名法 
    this.style.backgroundColor = 'purple';
    this.style.width = '250px';
    this.style.display = 'none';
    lis[i].style.backgroundPosition = '0 -' + index + 'px';
}

4.6.2 element.className 类名样式操作

修改元素的className更改元素的样式, 适合于样式较多或者功能复杂的情况

var test = document.querySelector('div');
test.onclick = function () {
    this.className = 'first change';
}

5 节点操作

网页中的所有内容都是节点(标签、属性、文本、注释等),在DOM 中,节点使用 node 来表示

节点至少拥有nodeType(节点类型)、nodeName(节点名称)和nodeValue(节点值)这三个基本属性

元素节点 nodeType 为 1

属性节点 nodeType 为 2

文本节点 nodeType 为 3 (文本节点包含文字、空格、换行等)

一般情况下,节点操作主要操作的是元素节点

利用 DOM 树可以把节点划分为不同的层级关系,常见的是父子兄层级关系

5.1 父亲节点

node.parentNode

parentNode 属性可返回某节点的父节点,注意是最近的一个父节点

如果指定的节点没有父节点则返回 null

  <div class="demo">
    <div class="box">
      <span class="erweima">×</span>
    </div>
  </div>
  <script>
    var erweima = document.querySelector('.erweima');
    // 得到的是离元素最近的父级节点 box, 如果找不到父节点就返回为 null
    console.dir(erweima.parentNode)
  </script>

5.2 子节点

// 返回包含指定节点的子节点的集合--不推荐
parentNode.childNodes(标准) 

返回值里面包含了所有的子节点,包括元素节点,文本节点等

如果只想要获得里面的元素节点,则需要专门处理,

parentNode.children(非标准)-- 推荐使用

parentNode.children 是一个只读属性,返回所有的子元素节点。它只返回子元素节点,其余节点不返回

虽然children 是一个非标准,但是得到了各个浏览器的支持,因此我们可以放心使用

第一个和最后一个子节点

// 返回第一个子节点,找不到则返回null。同样,也是包含所有的节点 -- 不推荐
parentNode.firstChild 

// 返回最后一个子节点,找不到则返回null。同样,也是包含所有的节点-- 不推荐
parentNode.lastChild 


// 以下 两个方法有兼容性问题,IE9 以上才支持
// 返回第一个子元素节点,找不到则返回null -- 推荐使用
parentNode.firstElementChild

// 返回最后一个子元素节点,找不到则返回null -- 推荐使用
parentNode.lastElementChild

5.3 兄弟节点

// 返回当前元素的下一个兄弟元素节点,找不到则返回null。同样,也是包含所有的节点 -- 不推荐
node.nextSibling
// 返回当前元素上一个兄弟元素节点,找不到则返回null。同样,也是包含所有的节点 -- 不推荐
node.previousSibling   


// 注意:下面的个方法有兼容性问题, IE9 以上才支持

// 返回当前元素下一个兄弟元素节点,找不到则返回null -- 推荐
node.nextElementSibling
// 返回当前元素上一个兄弟节点,找不到则返回null -- 推荐
node.previousElementSibling

5.4 新增节点

5.4.1 创建节点

document.createElement('tagName')

document.createElement() 方法创建由 tagName 指定的 HTML 元素。因为这些元素原先不存在,是根据我们的需求动态生成的,所以我们也称为动态创建元素节点

5.4.2 添加节点

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

// 方法将一个节点添加到父节点的指定子节点前面
node.insertBefore(child, 指定元素) 

5.4.3 删除节点

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

简易留言板案例

  <style>
    * {
      margin: 0;
      padding: 0;
    }

    body {
      padding: 100px;
    }

    textarea {
      width: 200px;
      height: 100px;
      border: 1px solid pink;
      outline: none;
      resize: none;
    }

    ul {
      margin-top: 50px;
    }

    li {
      width: 300px;
      padding: 5px;
      background-color: rgb(245, 209, 243);
      color: red;
      font-size: 14px;
      margin: 15px 0;
    }
  </style>

<textarea name="" id=""></textarea>
  <button>发布</button>
  <ul></ul>
  <script>
    // 1. 获取元素
    var btn = document.querySelector('button');
    var textarea = document.querySelector('textarea');
    var ul = document.querySelector('ul');

    // 2. 给btn 注册事件并添加事件处理程序
    btn.onclick = function () {
      // 2.0 获取textarea输入框的内容
      var value = textarea.value;
      if (value.length == 0) {
        alert('内容不可为空!');
        return;
      }

      // 2.1  创建元素 li
      var li = document.createElement('li');
      li.innerHTML = value;
      //  2.2  添加元素
      // ul.appendChild(li); // 这个是添加到ul父元素的最后
      ul.insertBefore(li, ul.children[0]); // 添加到第一个
      // 2.3 添加成功后 清空输入框
      textarea.value = '';
    }
  </script>

6 BOM 概述

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

BOM 由一系列相关的对象构成,并且每个对象都提供了很多方法与属性

6.1 DOM 和 BOM

DOMBOM
文档对象模型浏览器对象模型
DOM 就是把**「文档」**当做一个「对象」来看待把**「浏览器」**当做一个「对象」来看待
DOM 的顶级对象是 documentBOM 的顶级对象是 window
DOM 主要是操作页面元素BOM 主要是浏览器窗口交互的一些对象
DOM 是 W3C 标准规范BOM 是浏览器厂商在各自浏览器上定义的,兼容性较差

6.2 BOM的构成

BOM 比 DOM 更大,它包含 DOM

window 包含 document、location、navigation、screen、history

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

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

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

    var num = 10;
    console.log(num);  // 返回:10
    console.log(window.num);  // 返回:10
    
    function fn() {
        console.log(11);
    }
    fn(); // 返回:11
    window.fn();  // 返回:11
    

在调用的时候可以省略 window,例如经常使用的:alert() prompt()

7 window 对象常用事件

7.1 窗口加载事件 onload

window.onload 是窗口 (页面)加载事件,当文档内容完全加载完成会触发该事件(包括图像、脚本文件、CSS 文件等), 就调用的处理函数

有了 window.onload 就可以把 JS 代码写到页面元素的上方,因为 onload 是等页面内容全部加载完毕,再去执行处理函数

window.onload 传统注册事件方式 只能写一次,如果有多个,会以最后一个 window.onload 为准;如果使用 addEventListener 则没有限制

// 当文档对象加载完成时 会触发此事件,包含 图像、脚本文件、CSS等全部加载完毕

window.onload = function(){
    
}
// 或者 
window.addEventListener("load",function(){
    
});

DOMContentLoaded 事件触发时,仅当DOM加载完成,不包括样式表,图片,flash等等。Ie9以上才支持

如果页面的图片很多的话, 从用户访问到onload触发可能需要较长的时间, 交互效果就不能实现,必然影响用户的体验,此时用 DOMContentLoaded 事件比较合适

document.addEventListener('DOMContentLoaded',function(){})

7.2 调整窗口大小事件 onresize

window.onresize 是调整窗口大小加载事件, 当触发时就调用的处理函数

只要窗口大小发生像素变化,就会触发这个事件

window.onresize = function(){}

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

window.innerWidth 获取的是 当前屏幕的宽度

8 定时器

8.1 setTimeout()

setTimeout() 方法用于设置一个定时器,该定时器在指定时间后执行调用函数

window.setTimeout(调用函数, [延迟的毫秒数]);
毫秒数如果为空,则立即执行 调用函数

停止定时器
window.clearTimeout(timeoutID)
timeoutID是定时器的标识符 

说明:

  1. window 可以省略
  2. 这个调用函数可以直接写函数,或者写函数名或者采取字符串‘函数名()' -- 不推荐 三种形式
  3. 延迟的毫秒数省略默认是 0,单位是毫秒
  4. 因为定时器可能有很多,所以我们经常给定时器赋值一个标识符

8.2 setInterval()

setInterval() 方法重复调用一个函数,每隔这个时间,就去调用一次回调函数

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

停止定时器
window.clearInterval(intervalID);
timeoutID是定时器的标识符 

说明:

  1. window 可以省略
  2. 这个调用函数可以直接写函数,或者写函数名或者采取字符串 '函数名()' 三种形式
  3. 间隔的毫秒数省略默认是 0,单位是毫秒,表示每隔多少毫秒就自动调用这个函数
  4. 因为定时器可能有很多,所以我们经常给定时器赋值一个标识符
  5. 第一次执行也是间隔毫秒数之后执行,之后每隔毫秒数就执行一次

9 this指向

this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,一般情况下this的最终指向的是那个调用它的对象

现阶段,我们先了解一下几个this指向

  1. 全局作用域、普通函数、定时器 中this指向全局对象window

    console.log(this);  // 指向 window
    
    function fn() {
        console.log(this);  // 指向 window
    }
    
    setTimeout(function () {
        console.log(this);  // 指向 window
    }, 1000);
    
  2. 方法调用中,谁调用这个方法,this指向谁

    var obj = {
        sayHi: function () {
            console.log(this); // this指向的是 obj 这个对象
        }
    }
    obj.sayHi();
    
    
    var btn = document.querySelector('button');
    btn.addEventListener('click', function () {
        console.log(this); // this指向的是btn这个按钮对象
    })
    
  3. 构造函数中this,指向构造函数的实例

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

10 JS 执行队列

10.1 JS 是单线程

JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。比如我们对某个 DOM 元素进行添加和删除操作,不能同时进行, 应该先进行添加,之后再删除。

单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。这样所导致的问题是: 如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉

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

10.2 同步和异步

10.2.1 概述

同步:前一个任务结束后再执行后一个任务,程序的执行顺序与任务的排列顺序是一致的、同步的

异步:可以同时执行多个任务:你在做一件事情时,因为这件事情会花费很长时间,在做这件事的同时,你还可以去处理其他事情

同步和异步的本质区别:这条流水线上各个流程的执行顺序不同

10.2.2 同步任务和异步任务

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

异步任务:

JS 的异步是通过回调函数实现的,一般而言,异步任务有以下三种类型:

1、普通事件,如 click、resize 等

2、资源加载,如 load、error 等

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

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

10.3 JS 执行机制

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

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

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

image-20220403182425017

事件循环:由于主线程不断的重复获得任务、执行任务、再获取任务、再执行,所以这种机制被称为事件循环( event loop)

11 window 常用对象

11.1 location 对象

window 对象给我们提供了一个 location 属性,用于获取或设置窗体的 URL,并且可以用于解析 URL 。 因为这个属性返回的是一个对象,所以我们将这个属性也称为 location 对象

11.1.1 url 统一资源定位符

是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的 URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它

URL 的一般语法格式为:

protocol://host[:port]/path/[?query]#fragment

http://www.itcast.cn/index.html?name=andy&age=18#link
组成说明
protocol通信协议,常用的是 http https matio ftp等
host主机(域名) www.itcast.cn
port端口号(可选) 默认是80
pathname路径 由0个或多个 / 隔开的字符串,一般用来表示主机上的一个文件或目录
search参数,以键值对的形式,& 隔开
hash# 后面的,一般用户锚点

11.1.2 location对象的方法

对象方法说明
location.href=new url跳转到指定页面,记录历史path,新的页面会有回退按钮
location.assign()功能等同于 location.href
location.replace()替换当前页面,不记录历史path,所以新的页面没有回退按钮
location.reload()重新加载(刷新)页面,相当于F5;如果要强刷 Ctrl+F5,传参 true 即可

11.2 navigation 对象

navigator 对象包含有关浏览器的信息,它有很多属性,我们最常用的是 userAgent,该属性可以返回由客户机发送服务器的 user-agent 头部的值

if((navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i))) {
   // 手机端打开的页面
 } else {
   // PC端打开的页面
 }

11.3 history 对象

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

history 对象方法作用
back()后退
forward()前进
go()前进、后退功能,如果参数是1,前进一页;如果参数是-1,后退一页

12 元素偏移量 offset

offset 系列相关属性可以动态的得到该元素的位置(偏移)、大小等

  • 获得元素距离带有定位父元素的位置 left 和 top
  • 获得元素自身的大小(宽度高度)
  • 注意: 返回的数值都不带单位

常用属性

属性作用
ele.offsetParent返回当前元素带有定位的父元素(带有定位),如果父元素都没有定位,返回body
ele.offsetLeft返回当前元素相对于父元素(带有定位)的左方的偏移
ele.offsetTop返回当前元素相对于父元素(带有定位)的上方的偏移
ele.offetWidth返回当前元素包含padding、边框、内容区域的宽度,不带单位
ele.offetHeight返回当前元素包含padding、边框、内容区域的高度,不带单位

image-20220405112441469

鼠标移动时,计算相对于盒子内的位置

  <style>
    .box {
      width: 300px;
      height: 300px;
      background-color: pink;
      margin: 200px;
    }
  </style> 

<div class="box"></div>
  <script>
    // 计算 鼠标移动时,相对于盒子内的坐标
    var box = document.querySelector('.box');
    box.addEventListener('mousemove', function (e) {
      // 鼠标相对于 文档的x距离  -  盒子相对于 文档的 偏移 
      var x = e.pageX - this.offsetLeft;
      var y = e.pageX - this.offsetTop;
      this.innerHTML = 'x坐标是' + x + ' y坐标是' + y;
    });
  </script>

拖动移动小盒子,并且不要超出大盒子的范围

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

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>Document</title>
  <style>
    .big {
      width: 500px;
      height: 500px;
      border: 1px red solid;
      margin: 30px auto;
      position: relative;
    }

    .small {
      width: 300px;
      height: 300px;
      background-color: yellow;
      cursor: move;
      position: absolute;
      left: 0;
      top: 0;
    }
  </style>
</head>

<body>
  <div class="big">
    <div class="small"></div>
  </div>
  <script>
    // 1. 获取元素
    var big = document.querySelector('.big');
    var small = document.querySelector('.small');

    // 拖动小盒子 在大盒子内移动(不能移出大盒子)
    small.addEventListener('mousedown', function (e) {
      // 获得鼠标在盒子内的坐标
      var x = e.pageX - small.offsetLeft;
      var y = e.pageY - small.offsetTop;

      // 鼠标移动,重新设置小盒子的位置
      document.addEventListener('mousemove', move);

      // 鼠标松开,移除 鼠标移动事件
      document.addEventListener('mouseup', function () {
        document.removeEventListener('mousemove', move);
      })

      function move(e) {
        var newBoxLeft = e.pageX - x;
        if (newBoxLeft <= 0) {
          newBoxLeft = 0;
        } else if (newBoxLeft > big.offsetWidth - small.offsetWidth > 0) {
          newBoxLeft = big.offsetWidth - small.offsetWidth
        }
        small.style.left = newBoxLeft + 'px';

        var newBoxTop = e.pageY - y;
        if (newBoxTop <= 0) {
          newBoxTop = 0;
        } else if (newBoxTop > big.offsetHeight - small.offsetHeight) {
          newBoxTop = big.offsetHeight - small.offsetHeight;
        }
        small.style.top = newBoxTop + 'px';
      }
    });
  </script>
</body>

</html>

拖动的模态框

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

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>Document</title>
  <style>
    .login-header {
      width: 100%;
      text-align: center;
      height: 30px;
      font-size: 24px;
      line-height: 30px;
    }

    ul,
    li,
    ol,
    dl,
    dt,
    dd,
    div,
    p,
    span,
    h1,
    h2,
    h3,
    h4,
    h5,
    h6,
    a {
      padding: 0px;
      margin: 0px;
    }

    .login {
      display: none;
      width: 512px;
      height: 280px;
      position: fixed;
      border: #ebebeb solid 1px;
      left: 50%;
      top: 50%;
      background: #ffffff;
      box-shadow: 0px 0px 20px #ddd;
      z-index: 9999;
      transform: translate(-50%, -50%);
    }

    .login-title {
      width: 100%;
      margin: 10px 0px 0px 0px;
      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 0px auto;
      line-height: 40px;
      font-size: 14px;
      border: #ebebeb 1px solid;
      text-align: center;
    }

    .login-bg {
      display: none;
      width: 100%;
      height: 100%;
      position: fixed;
      top: 0px;
      left: 0px;
      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: #ebebeb 1px solid;
      text-indent: 5px;
    }

    .login-input {
      overflow: hidden;
      margin: 0px 0px 20px 0px;
    }

    .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: #ebebeb solid 1px;
      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. 点击弹出层, 模态框和遮挡层 显示 display:block;
      2. 点击关闭按钮,模态框和遮挡层 隐藏 display:none;
      3. 在页面中拖拽的原理: 鼠标按下并且移动,之后松开鼠标
        触发事件是鼠标按下 mousedown, 鼠标移动mousemove 鼠标松开 mouseup
        拖拽过程:  鼠标移动过程中,获得最新的值赋值给模态框的left和top值, 这样模态框可以跟着鼠标走了
        鼠标按下触发的事件源是 最上面一行,就是  id 为 title 
        鼠标的坐标 减去 鼠标在盒子内的坐标, 才是模态框真正的位置。
        鼠标按下,我们要得到鼠标在盒子的坐标。
        鼠标移动,就让模态框的坐标  设置为  : 鼠标坐标 减去盒子坐标即可,注意移动事件写到按下事件里面。
        鼠标松开,就停止拖拽,就是可以让鼠标移动事件解除  
    */

    // 1. 获取元素
    var link = document.querySelector('#link');
    var login_dialog = document.querySelector('.login');
    var mask = document.querySelector('.login-bg');
    var closeBtn = document.querySelector('#closeBtn');
    var title = document.querySelector('#title');

    // 2. 点击 弹出 模态框
    link.addEventListener('click', function () {
      login_dialog.style.display = 'block';
      mask.style.display = 'block';
    });

    // 3. 点击 关闭 模态框
    closeBtn.addEventListener('click', function () {
      login_dialog.style.display = 'none';
      mask.style.display = 'none';
    });

    // 4. 开始拖拽
    // (1) 当我们鼠标按下, 就获得鼠标在盒子内的坐标
    title.addEventListener('mousedown', function (e) {
      // 获得鼠标在盒子内的坐标
      var x = e.pageX - login_dialog.offsetLeft;
      var y = e.pageY - login_dialog.offsetTop;


      // 给document添加 鼠标移动事件,重新设置模态框的位置
      document.addEventListener('mousemove', move);

      //  鼠标弹起,就让鼠标移动事件移除
      document.addEventListener('mouseup', function () {
        document.removeEventListener('mousemove', move);
      })
      function move(e) {
        login_dialog.style.left = e.pageX - x + 'px';
        login_dialog.style.top = e.pageY - y + 'px';
      }
    });
  </script>
</body>

</html>

13 元素可视去区 client

client 相关属性来获取元素可视区的相关信息。通过 client 系列的相关属性可以动态的得到该元素的边框大小、元素大小等

属性作用
ele.clientLeft返回元素左边框的大小
ele.clientTop返回元素上边框的大小
ele.clientWidth返回当前元素包含padding、内容区域的宽度,不含边框,不带单位
ele.clientHeight返回当前元素包含padding、内容区域的高度,不含边框,不带单位

image-20220405125739835

     <style>
        div {
            width: 200px;
            height: 200px;
            background-color: pink;
            border: 10px solid red;
            padding: 10px;
        }
    </style>
    
    <div></div>
    <script>
        // client 宽度 和 offsetWidth 最大的区别就是 不包含边框大小
        var div = document.querySelector('div');
        console.log(div.offsetWidth); // 返回:240 - 包含边框大小
        console.log(div.clientWidth); // 返回:220
        console.log(div.clientLeft); // 返回:10  元素左边框的大小
        console.log(div.clientTop); // 返回:10   元素上边框的大小
    </script>

14 元素滚动 scroll

使用 scroll 系列的相关属性,可以动态的得到该元素的大小、滚动距离

如果浏览器的高(或宽)度不足以显示整个页面时,会自动出现滚动条。当滚动条向下滚动时,页面上面被隐藏掉的高度,我们就称为页面被卷去的头部滚动条在滚动时会触发 onscroll 事件

属性作用
ele.scrollLeft返回被卷去的左侧距离
ele.scrollTop返回被卷去的上侧距离
ele.scrollWidth返回当前元素实际宽度,不带单位
ele.scrollHeight返回当前元素实际高度,不带单位

image-20220405150346695

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

仿淘宝侧边栏案例

<!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>仿淘宝固定右侧侧边栏</title>
  <style>
    .slider-bar {
      position: absolute;
      left: 50%;
      top: 300px;
      margin-left: 600px;
      width: 45px;
      height: 130px;
      background-color: pink;
    }

    .w {
      width: 1200px;
      margin: 10px auto;
    }

    .header {
      height: 150px;
      background-color: purple;
    }

    .banner {
      height: 250px;
      background-color: skyblue;
    }

    .main {
      height: 1000px;
      background-color: yellowgreen;
    }

    span {
      display: none;
      position: absolute;
      bottom: 0;
    }
  </style>
</head>

<body>
  <div class="slider-bar">
    <span class="goBack">返回顶部</span>
  </div>
  <div class="header w">头部区域</div>
  <div class="banner w">banner区域</div>
  <div class="main w">主体部分</div>
  <script>
    //1. 获取元素
    var sliderbar = document.querySelector('.slider-bar');
    var banner = document.querySelector('.banner');
    var main = document.querySelector('.main');
    var goBack = document.querySelector('.goBack');

    // banner 区域距离顶部的距离
    var bannerTop = banner.offsetTop;
    // 侧边栏距离 banner 的距离
    var sliderbarTop = sliderbar.offsetTop - bannerTop;
    // main距离 顶部的距离
    var mainTop = main.offsetTop;

    // scroll 滚动事件
    document.addEventListener('scroll', function () {
      console.log(sliderbar.offsetTop)
      // 2. 当滚动到大于等于banner位置的时候,侧边栏 定位变为 fixed ,否则是 absolute;
      // 同时 重新设置 slidebar的top属性:侧边栏距离 banner 的距离
      if (window.pageYOffset > bannerTop) {
        sliderbar.style.position = 'fixed';
        sliderbar.style.top = sliderbarTop + 'px';
      } else {
        sliderbar.style.position = 'absolute';
        sliderbar.style.top = '300px';
      }

      // 2. 当滚动到大于等于main位置的时候,显示 返回顶部按钮
      if (window.pageYOffset > mainTop) {
        goBack.style.display = 'block';
      } else {
        goBack.style.display = 'none';
      }
    });

    // 返回顶部
    goBack.addEventListener('click', function () {
      // window.scroll(0, 0);
      animate(window, 0);
    });

    // 动画函数 上下 移动动画
    function animate(obj, target, callback) {
      clearInterval(obj.timer);
      obj.timer = setInterval(function () {
        var step = (target - window.pageYOffset) / 10;
        step = step > 0 ? Math.ceil(step) : Math.floor(step);
        if (window.pageYOffset == target) {
          // 停止动画 本质是停止定时器
          clearInterval(obj.timer);
          callback && callback();
        }
        window.scroll(0, window.pageYOffset + step);
      }, 15);
    }
  </script>
</body>

</html>

页面被卷去的头部兼容性解决方案

1、 声明了 DTD,使用 document.documentElement.scrollTop

2、 未声明 DTD,使用 document.body.scrollTop

3、 新方法 window.pageYOffset 和 window.pageXOffset,IE9 开始支持

function getScroll() {
    return {
        left: window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft||0,
        top: window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0
    };
} 


// 使用的时候 
getScroll().left

15 offset client scroll区分

属性作用
ele.offsetWidth返回元素包含padding、边框、内容区域的宽度,不带单位
ele.clientWidth返回元素包含padding、内容区域的宽度,不包含边框,不带单位
ele.scrollWidth返回元素实际宽度,不带单位

1、 offset系列 经常用于获得元素位置 offsetLeft offsetTop

2、 client 经常用于获取元素大小 clientWidth clientHeight

3、 scroll 经常用于获取滚动距离 scrollTop scrollLeft

4、 注意页面滚动的距离通过 window.pageXOffset 获得

image-20220405112441469

image-20220405125739835

image-20220405150346695

16 JS 动画

核心原理:通过定时器 setInterval() 不断移动盒子位置

封装的animate.js文件

/*
 动画原理:
 1. 获得盒子当前位置  
 2. 让盒子在当前位置上每次移动一定的步长:(目标值 - 现在的位置) / 10 做为每次移动的距离 
 3. 利用定时器不断重复这个操作(注意一定要加一个条件来终止定时器)
 3. 利用定时器不断重复这个操作
 4. 注意此元素需要添加定位, 才能使用element.style.left
​
 匀速动画:盒子是当前的位置 +  固定的值 10 
 缓动动画:盒子当前的位置 + 变化的值  (目标值 - 现在的位置) / 10)
​
 */// obj元素对象  target目标  callback回调函数
function animate(obj, target, callback) {
    // 首先先清除定时器
    clearInterval(obj.timer);
​
    // 创建一个 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();
            // }
​
            // 简写
            callback && callback();
        }
        // 每次移动 step 的步长:(目标值 - 现在的位置) / 10
        obj.style.left = obj.offsetLeft + step + 'px';
    }, 15);
}

点击按钮 向左向右移动到指定的位置

<!DOCTYPE html>
<html lang="en"><head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    div {
      position: absolute;
      left: 100px;
      top: 100px;
      width: 100px;
      height: 100px;
      background-color: pink;
    }
  </style>
</head><body>
  <script src="animate1.js"></script>
  <div></div>
  <button class="btn500">向右移动到800</button>
  <button class="btn100">向左移动到100</button>
  <script>
    /*
    动画原理:
    1. 获得盒子当前位置  
    2. 让盒子在当前位置上每次移动一定的步长:(目标值 - 现在的位置) / 10 做为每次移动的距离 
    3. 利用定时器不断重复这个操作(注意一定要加一个条件来终止定时器)
    3. 利用定时器不断重复这个操作
    4. 注意此元素需要添加定位, 才能使用element.style.left
​
    匀速动画:盒子是当前的位置 +  固定的值 10 
    缓动动画:盒子当前的位置 + 变化的值  (目标值 - 现在的位置) / 10)
​
    */
​
    // 1. 获取元素
    var div = document.querySelector('div');
    var btn500 = document.querySelector('.btn500');
    var btn100 = document.querySelector('.btn100');
​
    // 2. 点击按钮移动盒子到指定的位置
    btn500.addEventListener('click', function () {
      // 调用函数animate
      animate(div, 500);
    })
    btn100.addEventListener('click', function () {
      // 调用函数animate
      animate(div, 100, function () {
        div.style.backgroundColor = 'red';
      });
    })
  </script>
</body></html>

17 本地存储

特性:

1、 数据存储在用户浏览器

2、 设置、读取方便、甚至页面刷新不丢失数据

3、 容量较大,sessionStorage约5M、localStorage约20M

4、 只能存储字符串,可以将对象JSON.stringify() 编码后存储

window.sessionStoragewindow.localStorage
生命周期:关闭浏览器窗口生命周期:永久生效,除非手动删除 ,否则关闭页面也会存在
在同一个窗口(页面)下,数据可以共享可以多窗口(页面)共享:(同一浏览器都可以共享)
以键值对的形式存储使用以键值对的形式存储使用
存储数据:sessionStorage.setItem(key, value)存储数据:localStorage.setItem(key, value)
获取数据:sessionStorage.getItem(key)获取数据:localStorage.getItem(key)
删除数据:sessionStorage.removeItem(key)删除数据:localStorage.removeItem(key)
删除所有数据:sessionStorage.clear()删除所有数据:localStorage.clear()