javascript DOM BOM 笔记

Web API

API的概念

API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。

  • 任何开发语言都有自己的API
  • API的特征输入和输出(I/O)
  • API的使用方法(console.log())

Web API的概念

浏览器提供的一套操作浏览器功能和页面元素的API(BOM和DOM)

此处的Web API特指浏览器提供的API(一组方法),Web API在后面的课程中有其它含义

掌握常见的浏览器提供的API的调用方式

利用javascript调用DOM 或者 BOM 提供的API方法 来实现通过js 操作页面上的标签 文档 或者浏览器的功能

MDN-Web API

JavaScript的组成

在这里插入图片描述

ECMAScript - JavaScript的核心

定义了javascript的语法规范

JavaScript的核心,描述了语言的基本语法和数据类型,ECMAScript是一套标准,定义了一种语言的标准与具体实现无关

BOM - 浏览器对象模型

一套操作浏览器功能的API

通过BOM可以操作浏览器窗口,比如:弹出框、控制浏览器跳转、获取分辨率等

DOM - 文档对象模型

一套操作页面元素的API

DOM可以把HTML看做是文档树,通过DOM提供的API可以对树上的节点进行操作

JavaScript DOM开发

当网页被加载时,浏览器会创建页面的文档对象模型(Document Object Model) , DOM 是 W3C(万维网联盟)的标准。DOM 定义了访问 HTML 和 XML 文档的标准>。

我们经常使用JavaScript来操纵DOM,不过DOM其实的语言无关的。它并不是JavaScript的一部分。

官方地址:
w3c
cssom

HTML DOM 模型被构造为对象的树:DOM树

<!DOCTYPE html>
<html>
<head>
  <title>My title</title>
</head>
<body>
  <a href="">My link</a>
  <h1>My header</h1>
</body>
</html>

在这里插入图片描述

DOM基础名词

  • 文档:一个网页可以称为文档
  • 节点:网页中的所有内容都是节点(标签、属性、文本、注释等)
  • 元素:网页中的标签
  • 属性:标签的属性

DOM API分类

DOM的学习主要围绕操作 标签节点与用户事件交互两个方面 , 我们先看一下所有需要学习的DOM API分类

DOM的属性 节点名称 节点类型 节点
对元素对象的 增 删 改 查
对元素属性的 增 删 改 查
对元素位置的获取
键盘事件与鼠标事件  事件监听 事件方法 事件对象  [点击,滚动,移入,移出,输入,键入]

Node(节点)基础分类

DOM1级定义了一个Node接口,该接口由DOM中所有节点类型实现。这个Node接口在JS中是作为Node类型实现的。在IE9以下版本无法访问到这个类型,JS中所有节点都继承自Node类型,都共享着相同的基本属性和方法。
Node有一个属性nodeType表示Node的类型,它是一个整数,其数值分别表示相应的Node类型,具体如下:

Node.ELEMENT_NODE:1 //标签 *
Node.ATTRIBUTE_NODE:2 //属性 *
Node.TEXT_NODE:3 //文本 *
Node.CDATA_SECTION_NODE:4 //子节点一定为TextNode
Node.ENTITY_REFERENCE_NODE:5
Node.ENTITY_NODE:6
Node.PROCESSING_INSTRUCTION_NODE:7 //命令节点
Node.COMMENT_NODE:8  //注释
Node.DOCUMENT_NODE:9  //最外层的Root element,包括所有其它节点 *
Node.DOCUMENT_TYPE_NODE:10  // DTD,<!DOCTYPE………..>
Node.DOCUMENT_FRAGMENT_NODE:11 //文档片段节点 
Node.NOTATION_NODE:12 //DTD中的Nation定义

获取节点类型nodeType、节点名称nodeName、节点内容

获取节点类型:直接节点.nodeType

获取标签名称:直接节点.nodeName,返回大写的标签名称

获取节点内容:直接节点.nodeValue,注意标签节点是没有值的,为null

<!DOCTYPE html>
<html>

<head>
  <title>My title</title>
</head>

<body>
  <a href="">My link</a>
  <h1 id="title">My header</h1>
  <p >213123</p>
  <img src="" alt="">

  <script>
    console.log(document.nodeType);//9
    var oTitle = document.querySelector('#title')
	console.log(oTitle.nodeType);//1
    console.log(oTitle.nodeName);//H1
    console.log(oTitle.nodeValue);//null 标签本身是没有值的,My header是文本节点也是一个节点,在讨论节点关系上,文本节点并不属于标签节点的节点内容
  	console.log(oTitle.childNodes[0]);//"My header"
    console.log(oTitle.childNodes[0].nodeType);//3
    console.log(oTitle.childNodes[0].nodeValue);//My header
    </script>
</body>

</html>

Element类型

Element提供了对元素标签名,子节点和特性的访问,我们常用HTML元素比如div,span,a等标签就是element中的一种。Element有下面几条特性:
(1)nodeType为1
(2)nodeName为元素标签名,tagName也是返回标签名
(3)nodeValue为null,标签本身是没有值的,一般用不上
(4)parentNode可能是Document或Element
(5)子节点可能是Element,Text,Comment,Processing_Instruction,CDATASection或EntityReference

Text类型

Text表示文本节点,它包含的是纯文本内容,不能包含html代码,但可以包含转义后的html代码。Text有下面的特性:
(1)nodeType为3
(2)nodeName为#text
(3)nodeValue为文本内容
(4)parentNode是一个Element
(5)没有子节点

Attr类型

Attr类型表示元素的特性,相当于元素的attributes属性中的节点,它有下面的特性:
(1)nodeType值为2
(2)nodeName是特性的名称
(3)nodeValue是属性的值
(4)parentNode为null

Comment类型

Comment表示HTML文档中的注释,它有下面的几种特征:
(1)nodeType为8
(2)nodeName为#comment
(3)nodeValue为注释的内容
(4)parentNode可能是Document或Element
(5)没有子节点

Document

Document表示文档,在浏览器中,document对象是HTMLDocument的一个实例,表示整个页面,它同时也是window对象的一个属性。Document有下面的特性:
(1)nodeType为9
(2)nodeName为#document
(3)nodeValue为null
(4)parentNode为null
(5)子节点可能是一个DocumentType或Element

DocumentType

DocumentType表示文档的DTD声明,用于确定文档版本,确定对应的API集与属性解析规则:
(1)nodeType为10
(2)nodeName为#document-fragment
(3)nodeValue为null
(4)parentNode为null

DocumentFragment类型

DocumentFragment是所有节点中唯一一个没有对应标记的类型,它表示一种轻量级的文档,可能当作一个临时的仓库用来保存可能会添加到文档中的节点。DocumentFragment有下面的特性:
(1)nodeType为11
(2)nodeName为#document-fragment
(3)nodeValue为null
(4)parentNode为null

DOM API 节点对象选择器

通过以下方法可以获取DOM节点对象

var oHeader = document.getElementById('header');
//获取ID名称为 header 的标签 返回对应的节点对象(单个) 如果文档内有多个 ID名称为 header 的标签 只获取第一个

var aP = document.getElementsByTagName('p');
//获取文档内的所有 p 标签 返回一个 DOM集合(NodeList) 类数组对象 哪怕没有找到只有一个也返回类数组集合

var aDes = document.getElementsByClassName('des red'); //IE9
//获取文档内的所有类名为 des且又red 的标签 返回一个 DOM集合(NodeList) 类数组对象 哪怕没有找到只有一个也返回类数组集合

var oHeader = document.querySelector('#header'); 
//querySelector是H5 新增的DOM API 参数使用合法的css选择器即可  返回复合条件的第一个元素 (唯一)

var aDes = document.querySelectorAll('.des');
//querySelectorAll是H5 新增的DOM API 参数使用合法的css选择器即可  返回复合条件的节点集合 类数组对象(非唯一)

//在chrome控制台中 可以直接使用 
//$() 替代 document.querySelector()
//$$() 替代  document.querySelectorAll()

var body = document.body; //直接获取body节点对象

//直接通过id名称拿到对应的节点对象 不推荐 变量冲突 和变量污染的问题
//<div id="wrap">
//    <p> INSERT Lorem ipsum dolor sit amet consectetur, adipisicing elit. Molestias </p>
//</div>
console.log(wrap);
console.log(window.wrap);

通过标签的属性的值来获取节点对象

优点:就地取材,即使不知道表单的内容时什么也可以获取表单的内容,更好的可维护性和可扩展性,多用于调查问卷

注:只能获取一个节点对象,谁在上面就获取谁

<!DOCTYPE html>
<html lang="zh-cn">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    .header .logo-wrap .pic .left .icon p {
      width: 80px;
      height: 40px;
      background-color: #222;
      color: #fff;
    }
  </style>
</head>
<body>
  <p class="xp">4343</p>
  <input id="wom" name='sex' type="radio">
  <label for="wom"></label>
  <input id="hum" class="xp" name='sex' type="radio">
  <label for="hum"></label>
  <p class="show-box">你选中了</p>
  <script>
    console.log(document.querySelector(`[class  ='xp']`))// <p class="xp">4343</p>
    console.log(document.querySelector(`[class  ='show-box']`))//<p class="show-box">你选中了</p>
    console.log(document.querySelector(`[for  ='wom']`))//<label for="wom">女</label>
    console.log(document.querySelector(`[id  ='hum']`))// <input id="hum" class="xp" name='sex' type="radio">
  </script>
</body>
</html>

案例:根据多选的结果来改变显示的内容

<!DOCTYPE html>
<html lang="zh-cn">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .header .logo-wrap .pic .left .icon p {
      width: 80px;
      height: 40px;
      background-color: #222;
      color: #fff;
    }
  </style>
</head>
<body>
  <input id="hum" name='sex' type="radio">
  <label for="hum"></label>
  <input id="wom" name='sex' type="radio">
  <label for="wom"></label>
  <p class="show-box">你选中了</p>
  <script>
    var aInput = document.querySelectorAll('input');
    var oShow = document.querySelector('.show-box');

    //方法一:可扩展 可维护 不需要知道表单的内容是什么
    document.addEventListener('click', function (e) {
      if (e.target.tagName.toLowerCase() === 'input') {
        oShow.innerText = '你选中了 ' + document.querySelector(`[for=${e.target.id}]`).innerText;
      }
    }, false);

    //方法二:要自己整理一份映射表
    var selectValue = {
      hum: '男',
      wom: '女'
    }
    document.addEventListener('click', function (e) {
      if (e.target.tagName.toLowerCase() === 'input') {
        oShow.innerText = '你选中了 ' +  selectValue[e.target.id]
      }
    }, false);
  </script>
</body>
</html>

元素命名规则

   获取的DOM元素命名

   前缀 o  only 获取单一节点对象 querySelector  getElementById

   前缀 a  all  DOM节点集合 NodeList集合

   getElementsByClassName getElementsByTagName

   querySelectorAll

DOM 节点对象属性

DOM节点对象拥有一系列属性 用于存储该节点的状态 信息

只要在标签上的属性都可以用.属性名称来获取对应的属性值

标签属性通过元素直接.操作是既可以读取也可以修改的

element.title //设置或返回元素的title属性
element.textContent //设置或返回一个节点和它的文本内容
element.innerText //设置或返回一个节点和它的文本内容
element.tagName //作为一个字符串返回某个元素的标记名(大写)
element.className //获取标签的class属性值

nodelist.length //返回节点列表的节点数目。
nodelist.item(idx) //返回某个元素基于文档树的索引 同 nodelist[idx]
var oWrap = document.querySelector('#wrap');
var aLi = document.querySelectorAll('#list li');
console.log(aLi.item(2),aLi[2])
//aLi.item(2)跟aLi[2]是一样的
<!DOCTYPE html>
<html lang="zh-cn">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>标签对象属性2</title>
  <style>
    img {
      transform: scale(3);
      border: 3px solid red;
      padding: 10px;
    }
  </style>
</head>

<body>
  <!-- -->
  <img id="pic" style="outline:1px;" class="photo red" src="images/bird.png" width="100" height="100" alt="bird"
    title="你猜猜我是什么鸟">

  <p>你好 我是p</p>

  <div id="wrap">
    <h3>我是标题</h3>
    我是wrap内容
  </div>
  <script>
    var oImg = document.querySelector('#pic');

    //标签属性 通过元素直接 .操作可以读取 也可以 修改
    console.log(oImg.id);//pic
    console.log(oImg.className);//photo red
    console.log(oImg.src);
    console.log(oImg.height);//100
    console.log(oImg.width);//100
    console.log(oImg.alt);//bird
    console.log(oImg.title);//你猜猜我是什么鸟

    oImg.title = 'ooo';//可以改变属性值

    //CSSOM  oImg.style是一个虚拟化对象
    console.log(oImg.style.padding); // 空,只能获取到直接写到标签上的行内样式style
    console.log(oImg.style.cssText);//outline:1px;
    console.log(oImg.style.outline);//1px
    oImg.style.padding = '90px';//可以直接设置属性,直接加到行内样式style中

    var oP = document.querySelector('p');
    console.log(oP.textContent);//你好 我是p
    console.log(oP.innerText);//你好 我是p

    var oWrap = document.querySelector('#wrap');
    //获取元素内的文本内容 (过滤掉标签)
    console.log(oWrap.textContent);
    //我是标题
    //我是wrap内容
    console.log(oWrap.innerText);
    //我是标题
    //我是wrap内容

    //获取元素内的所有内容(包含标签)
    console.log(oWrap.innerHTML);
    //<h3>我是标题</h3>
    //我是wrap内容
  </script>
</body>

</html>

DOM API 获取标签属性

通过以下方法可以获取DOM元素的属性

var oHeader = document.getElementById('header');
var aImg = document.getElementsByTagName('img');

console.log(oHeader.attributes); //节点属性对象 拥有长度属性

console.log(oHeader.id); //指定属性获取

console.log(aImg[0].src);//指定属性获取

console.log(aImg.getAttribute('src')); //通过 getAttribute方法获取实际 属性值

console.log(aImg.hasAttribute('src')); // 判断节点对象是否含有 某个 属性

getAttribute和element.属性获取属性的不同

getAttribute获取的是标签上的属性值,是未处理的,是什么就是什么

element.属性获取属性是元素对象的属性值,是经过处理的

<body>
  <img id="pic" style="outline:1px;" class="photo red" src="images/bird.png" width="1000px" height="100" alt="bird"  title="你猜猜我是什么鸟" joker='你好'>
<script>
    var oImg = document.querySelector('#pic');
 
    console.log(oImg.getAttribute('src'));//images/bird.png
    console.log(oImg.src);//file:///D:/%E%B/images/bird.png  转换的地址

    oImg.width = '1000';//对象的实例化属性上去写,所以用这种方法不能写成oImg.width = '1000px'
    oImg.setAttribute('width', '1000px')//直接再标签的属性上去写
    console.log(oImg.getAttribute('width')) //1000px  字符串
    console.log(oImg.width);//1000  数字

    console.log(oImg.getAttribute('joker'))//'你好'
    console.log(oImg.joker);//undefined,实例化对象没有这个值
    console.log(oImg.hasAttribute('joiker'));//false
</script>
</body>

不要用setAttribute设置布尔值

不要使用setAttribute将标签的某个属性设置为布尔值,因为setAttribute设置的值全部都是字符串,就算设置属性为false,在标签上也是字符串的‘false’,隐式转换的话就为true了。

<!DOCTYPE html>
<html lang="zh-cn">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
  <input id="hum" name='sex' type="radio">
  <label for="hum"></label>
  <input id="wom" name='sex' type="radio">
  <label for="wom"></label>
  <script>
    var ohum =document.querySelector('#hum')
    // ohum.checked = false;  //false
    ohum.setAttribute('checked', false); //true
    console.log(ohum.checked)
  </script>
</body>
</html>

DOM API 获取样式

DOM样式获取分为获取 行内style样式 和 实际计算后样式两种

//行内样式
console.log(oImg.style); //CSSOM对象
console.log(aImg.style.outline); //标签行内样式 style属性中存在的样式的值,获取不到不是行内样式的
//注意:setAttribute一般用来设置属但不用来设置样式,设置样式用element.style.样式名称 = 样式值 
//element.style.样式名称中,样式名称用驼峰命名法,如font-style,应为fontStyle,应为-再js中是运算符,会发生计算

//getComputedStyle获取标签最终表现出来的实际样式
console.log(window.getComputedStyle(oImg, null)['font-size']); 
console.log(window.getComputedStyle(oImg, null)['fontSize']); 
//主流浏览器,getComputedStyle方法用font-style或fontStyle都可以取到值,因为CSSOM对象的属性值是作为字符串存在的,为了统一建议都用驼峰命名法
console.log(oImg.currentStyle['border']); //老版本IE

//兼容函数写法
function getStyle(obj, attr) {
    return obj.currentStyle ? obj.currentStyle[attr] : getComputedStyle(obj, false)[attr];
}

DOM API 获取节点内容

获取节点内容主要掌握 获取节点文本内容 获取实际内容两种

目前所有浏览器都支持innerText,除非做很老的开发,如政府单位或事业单位的开发会用上,但是开发的话也不会用原生会用框架,所以只需要了解即可

console.log(oHeader.innerHTML); //获取标签内的实际内容(包括标签)
console.log(oHeader.innerText);  //设置标签中间的文本内容,应该使用innerText属性,谷歌火狐支持,ie8支持
console.log(oHeader.textContent); //设置标签中间的文本内容,应该使用textContent属性,谷歌火狐支持,ie8不支持

//textContent 与 innerText兼容处理
function getInnerText(element) {
    if(typeof element.textContent=="undefined"){
        return element.innerText;
    }else{
        return element.textContent;
    }
}

function setInnerText(element) {
    var key = typeof element.textContent == ''undefined'' ? 'innerText' : 'textContent'
    element[key] = text
}

用innerHTML赋值后的注意点:

用了innerHTML后的元素对象,已经被擦写后重新赋值,不是原来的元素对象,所以如果要对修改后的标签元素做样式或属性修改,需要重新获取修改后的元素对象

<!DOCTYPE html>
<html lang="zh-cn">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>innerHTML</title>
  <style>
    p {
      font-size: 28px;
    }
  </style>
</head>
<body>
  <div class="wrap">
    <p>你猜猜<span></span>是谁</p>
  </div>
  <script>
    var oWrap = document.querySelector('.wrap');
    var oP = oWrap.querySelector('p'); //把标签实例化为元素对象 oP (魂)

    oP.style.color = 'red';
    console.log(oWrap.innerHTML);//拿到的是标签内部标签 文本的 字符串表现 (皮)
    oWrap.innerHTML = oWrap.innerHTML + '';//这里的<p>你猜猜<span>我</span>是谁</p>已经不是原来的了,是重新赋值后的结果,虽然长得一样

    //oP = oWrap.querySelector('p'); 对新的p标签获取他的元素对象赋值给oP
    oP.style.color = 'blue'; //赋值没生效,颜色还是红色,如果要生效的画要重新获取一下op
  </script>
</body>
</html>

DOM API 获取节点相关节点

我们可以通过方法获取节点的相关联节点 后代 父亲 兄弟等

console.log(oHeader.children); //获取子元素 只能或取子代标签
console.log(oHeader.childNodes); //获取元素内的节点列表 文本,制表符,换行符都算一个节点,不常用

console.log(oHeader.firstChild); //获取第一个子节点(包含文本)
console.log(oHeader.firstElementChild); //获取第一个子标签节点

console.log(oHeader.lastChild); //获取最后一个子节点(包含文本)
console.log(oHeader.lastElementChild); //获取最后一个子标签节点

console.log(aP[0].parentElement); //父元素
console.log(aP[0].parentNode); //父节点 用哪个有可以,因为文本不可能有下属标签

console.log(aP[1].nextElementSibling); //下一个兄弟标签节点
console.log(aP[1].nextSibling); //下一个兄弟节点(计算文本节点)

console.log(aP[2].previousElementSibling); //上一个兄弟标签节点
console.log(aP[2].previousSibling); //上一个兄弟节点(计算文本节点)

console.log(oP.querySelector('p:nth-of-type(3)'));//获取第几个子节点

案例

<body>
  <div class="item">
    222
    <h3 class="title">龙牌洗发水</h3>
    <p class="des">龙的传人真正的洗发水 </p>
    <p class="price">¥999999</p>
  </div>

  <div class="item">
    <h3 class="title">龙牌洗发水</h3>
    <p class="des">龙的传人真正的洗发水 </p>
    <p class="price">¥999999</p>
  </div>
  <script>
    var oItem = document.querySelector('.item');
    var oTitle = oItem.querySelector('h3');
    var itemChild = oItem.children;

    //获取子元素/节点
    console.log(oItem.children); //元素 获取不到222文本节点,只能获取到标签元素
    console.log(oItem.childNodes); //获取元素内的节点列表 文本,制表符,换行符都算一个节点,不常用

    //获取第一个子元素/节点
    console.log(oItem.firstChild); //222获取元素第一个子(节点:标签 文本)
    console.log(oItem.firstElementChild); //<h3 class="title">龙牌洗发水</h3>获取元素第一个子元素(只指代标签 不包括文本节点)

    //获取最后一个子元素/节点
    console.log(oItem.lastChild); //获取元素最后一个子(节点:标签 文本)
    console.log(oItem.lastElementChild); //<p class="price">¥999999</p>获取元素最后一个子元素(只指代标签 不包括文本节点)

    //获取父元素 Node节点 Element元素,两个没什么区别,因为父元素不可能是文本节点
    console.log(oTitle.parentElement); //获取父元素
    console.log(oTitle.parentNode); //获取父元素

    //sibilings 兄弟
    console.log(oTitle.nextElementSibling);//<p class="des">龙的传人真正的洗发水 </p>下一个兄弟元素
    console.log(oTitle.nextSibling);//下一个兄弟节点(包含文本节点)

    console.log(oTitle.previousElementSibling);//null上一个兄弟元素
    console.log(oTitle.previousSibling);//222上一个兄弟节点(包含文本节点)

    console.log(oTitle.nextElementSibling.nextElementSibling);// <p class="price">¥999999</p>
    console.log(oTitle.parentNode.lastElementChild);// <p class="price">¥999999</p>
  </script>
</body>

DOM API 创建与添加节点标签

通过相关方法可以创建节点 添加节点到HTML文档中

//实例化方式创建
var cP = document.createElement("p"); //创建标签节点

var content = document.createTextNode("你好"); //创建文本节点

cP.appendChild(content); //添加content到cP节点中
document.body.appendChild(cP); //添加cP到body标签中进行渲染
document.body.append(cP); //append 是H5 WEB API 新增方法

---------------------------------------
    
var oWrap = document.createElement('div'); //tagName 标签名称
oWrap.className = 'wrap';
oWrap.innerText = '我是wrap标签 我是通过appendChild方法添加到body中的';
oWrap.style.fontSize = '30px';
oWrap.style.color = 'red';
document.body.appendChild(oWrap);

var oP = document.createElement('p');
oP.innerText = '我是p标签 我会被添加到 oWrap中去';

oWrap.appendChild(oP);
---------------------------------------
    
//通过文本标签(字面量方式)方式添加节点   
var htmlTxt = '<div class="wrap"><p style="color:red;font-size:30px;">我是p我是.wrap的子元素 </p></div>';
document.body.innerHTML += htmlTxt;

//HTML输出流 直接覆盖body中的内容
document.write('<p>123</p>'); 

----------------------------------------
//在父节点ELE里面的节点A前面添加 新的节点B ,想要谁在前面第一参数就写谁

ELE.insertBefore(B,A);

DOM API 节点替换拷贝

通过相关方法可以实现 节点拷贝 节点

//复制拷贝
var oWrap = document.getElementById('wrap');
//cloneNode方法会对调用它的节点对象进行复制 传参true代表包括该节点的后代节点 不传参数表示只复制该节点本身
var cloneWrap = oWrap.cloneNode(true);
document.body.appendChild(cloneWrap);
-------------------------------------------------------
//替换  newEle如果是已存于页面上的标签元素 会进行剪切操作 直接用自己覆盖ele
var oWrap = document.querySelector('#wrap');
var oDes = oWrap.querySelector('#wrap .des');
var newDes = document.createElement('p');
newDes.innerHTML = '我会替换掉你';
oWrap.replaceChild(newDes,oDes);

// 用newDes替换oWrap内部的oDes

DOM API 节点删除

通过如下方法可以实现DOM节点的删除

element.removeChild(element.children[1]); //element删除了element内的下标为1的子元素
element.remove(); //H5 DOM API 自己删自己

DOM API 节点检查

通过如下方法可以对Node节点对象进行一系列检测

document.hasFocus(); //返回布尔值,检测文档或元素是否获取焦点
element.hasChildNodes(); //返回布尔值,检测节点对象是否包含任何子节点
element.isEqualNode(element2); //返回布尔值,判断element与element2是否是同一个节点
element.hasAttributes(); //返回布尔值,判断当前节点对象是否包含属性
element.hasAttribute(property); //返回布尔值, 判断该节点是否拥有指定的 property 属性

DOM classList 属性

classList 属性返回元素的类名,作为 DOMTokenList 对象。

该属性用于在元素中添加,移除及切换 CSS 类。

classList 属性是只读的,但你可以使用 add() 、 remove() 、toggle()、replace()方法修改它。

console.log(aPicList[0].classList) //指定DOM对象的类名列表

Element.classList.add('w1000'); //添加 如果不存在添加上
Element.classList.add('c368'); //添加 如果存在 就忽略

Element.classList.remove('c368');//删除 如果类名列表 上存在 c368 就删掉

Element.classList.toggle('w1000'); //开关 如果有就删 如果没有就添 

Element.classList.replace('bg333', 'bg222'); //repace(A,B) 把列表中的类名A替换成 B

案例

虚拟DOM渲染

将这个虚拟节点数据 具象化成实际标签

<!DOCTYPE html>
<html lang="zh-cn">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>以下列虚拟对象添加对应的标签到页面上</title>
</head>
<body>
  <script>
    var element = {
      tagName: 'div',
      id: 'header',
      className: 'title fw400 c666 fz28',
      contentText: '我是一个header标签',
      children: [
        {
          tagName: 'h1',
          id: '',
          className: 'logo',
          contentText: '我是logo',
          children: []
        },
        {
          tagName: 'ul',
          id: '',
          className: 'nav',
          contentText: '',
          children: [
            {
              tagName: 'li',
              id: '',
              className: 'list',
              contentText: '导航1',
              children: []
            },
            {
              tagName: 'li',
              id: '',
              className: 'list',
              contentText: '导航2',
              children: []
            },
          ]
        }
      ]
    }
    function drawElement(element, parentEle) {
      var vDom = document.createElement(element['tagName']); 
      element['innerText'] = element['contentText'];
      for (var key in element) {
        element[key] && (vDom[key] = element[key]);
      }
      parentEle.appendChild(vDom);
      if (element['children'].length > 0) {
        for (var i = 0, len = element['children'].length; i < len; i++) {
          drawElement(element['children'][i], vDom)
        }
      }
    }
    drawElement(element, document.body)
  </script>
</body>

</html>

随机切换1~4图片

<!DOCTYPE html>
<html lang="zh-cn">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    img {
      width: 400px;
      height: auto;
    }
  </style>
</head>
<body>
  <!-- 点击img切换图片 随机换 p1 - p4 -->
  <img id='pic' src="images/p1.jpg" alt="">
  <script>
    var oPic = document.querySelector('#pic');

    oPic.onclick = function () {
      var randomIdx = ~~(Math.random() * 4) + 1; //1~4
      this.src = `images/p${randomIdx}.jpg`;
    }
  </script>
</body>
</html>

回流与重绘

document.createDocumentFragment()

<!DOCTYPE html>
<html lang="zh-cn">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>回流与重绘</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }
    div {
      float: left;
      width: 200px;
      height: 200px;
      margin: 10px;
      border: 2px solid red;
      background-color: #368;
      /*隐士切换span的display展示模式为block*/
    }
  </style>
</head>
<body>
  <script>
    /*
      我要动态渲染3个div到页面上
      append appendChild replaceChild remove removeChild insertBefore
      createDocumentFragment 文档片段 文档气泡 感冒药胶囊 临时容器 可以往里面放任何的节点元素 元素列表 等等
    */

    drawHtml(3, 'div');
    function drawDom(num, tagName) {
      var vDom;
      var fragment = document.createDocumentFragment();//文档片段
      console.log(fragment);
      for (var i = 0; i < num; i++) {
        vDom = document.createElement(tagName); //
        fragment.appendChild(vDom); //放到临时容器fragment里
        // document.body.appendChild(vDom); //重复N次 每次渲染一个标签
      }
      document.body.appendChild(fragment);  //一次渲染渲染N个
    }

    function drawHtml(num, tagName) {
      var eleStr = '';
      for (var i = 0; i < num; i++) {
        eleStr += '<' + tagName + '></' + tagName + '>';
      }
      document.body.innerHTML = eleStr;
    }
  </script>
</body>
</html>

获取元素对象下标方法

//方法一
function getElementIdx (item) {
  var elements = item.parentNode.children;
  for (var i = 0, len = elements.length; i < len; i++) {
    if (item === elements[i]) {
      return i;
    }
  }
}
console.log(getElementIdx(aLi, oActive));

//方法二
console.log([].indexOf.call(aLi, oActive))

封装获取元素的方法

function $ (ele) {
  return document.querySelector(ele);
}

function $$ (ele) {
  return document.querySelectorAll(ele);
}

获取元素的实际样式

function getStyle (obj, attr) {
  return obj.currentStyle ? obj.currentStyle[attr] : getComputedStyle(obj, false)[attr];
}

设置任意的标签中间的任意文本内容

function setInnerText (element, text) {
  var key = element.textContent == "undefined" ? 'innerText' : 'textContent';
  element[key] = text;
}

基础事件

用户与文档交互基础事件分为鼠标事件与键盘事件

例如:

  • 当用户点击鼠标时
  • 当网页加载后
  • 当图像加载后
  • 当鼠标移至元素上时
  • 当输入字段被改变时
  • 当 HTML 表单被提交时
  • 当用户敲击按键时

事件句柄

属性此事件发生在何时…
onabort图像的加载被中断。
onblur元素失去焦点。
onchange域的内容被改变。失去焦点的时候才判断内容是否改变
onclick当用户点击某个对象时调用的事件句柄。
ondblclick当用户双击某个对象时调用的事件句柄。
onerror在加载文档或图像时发生错误。
onfocus元素获得焦点。
onkeydown某个键盘按键被按下。
onkeypress某个键盘按键被按下并松开。 中文输入法输入的时候无响应,用oninput可以响应输入法
onkeyup某个键盘按键被松开。
oninputinput输入框接收到输入内容时触发 H5 API
onload一张页面或一幅图像完成加载。
onmousedown鼠标按钮被按下。
onmousemove鼠标被移动。
onmouseover鼠标移到某元素之上。
onmouseout鼠标从某元素移开。
onmouseenter鼠标移到某元素之上。(不支持冒泡)
onmouseleave鼠标从某元素移开。(不支持冒泡)
onmouseup鼠标按键被松开。
onreset重置按钮被点击。
onresize窗口或框架被重新调整大小。
onselect文本被选中。
onsubmit确认按钮被点击。
onunload用户退出页面。
transitionend目标元素的transition行为结束之后,过渡结束之后触发
var oBtn = document.querySelector('#btn');

oBtn.onclick = function(e){
    console.log(e)
   this.value = '不要点我'; 
}

//事件回调函数会回调形参 e Event对象 
//用事件监听addEventListener,要去掉句柄中的‘on’

事件:触发-响应机制

Event接口表示在DOM中发生的任何事件,一些是用户生成的(例如鼠标或键盘事件),而其他由API生成。

事件三要素

  • 事件源:触发(被)事件的元素
  • 事件类型:事件的触发方式(例如鼠标点击或键盘点击)
  • 事件处理程序:事件触发后要执行的代码(函数形式)

事件阶段

事件有三个阶段:

event.eventPhase属性可以查看事件触发时所处的阶段 :

  1. 捕获阶段

  2. 当前目标阶段

  3. 冒泡阶段

1、事件捕获
捕获型事件(event capturing):事件从最不精确的对象(document 对象)开始触发,然后到最精确(也可以在窗口级别捕获事件,不过必须由开发人员特别指定)

2、事件冒泡
冒泡型事件:事件按照从最特定的事件目标到最不特定的事件目标(document对象)的顺序触发。

绑定的事件默认的执行时间是在冒泡阶段执行,而非在捕获阶段(重要)。这也是为什么当父类和子类都绑定了某个事件,会先调用子类绑定的事件,后调用父类的事件

![在在这里插入图片描述

事件对象的属性和方法

  • event.type 获取事件类型
  • clientX/clientY 所有浏览器都支持,窗口位置
  • pageX/pageY IE8以前不支持,页面位置
  • event.target || event.srcElement 用于获取触发事件的元素
  • event.preventDefault() 取消默认行为
event对象属性
属性描述
altKey返回当事件被触发时,“ALT” 是否被按下。
shiftKey返回当事件被触发时,“SHIFT” 键是否被按下。
ctrlKey返回当事件被触发时,“CTRL” 键是否被按下。
metaKey返回当事件被触发时,“meta” 键是否被按下。
button返回当事件被触发时,哪个鼠标按钮被点击。
clientX返回当事件被触发时,鼠标指针的水平坐标。
clientY返回当事件被触发时,鼠标指针的垂直坐标。
keyCode表示非字符按键的unicode值。
isChar布尔值,表示当前按下的键是否表示一个字符
screenX返回当某个事件被触发时,鼠标指针的水平坐标。
screenY返回当某个事件被触发时,鼠标指针的垂直坐标。
pageX事件发生时相对于页面(如viewport区域)的水平坐标。
pageY事件发生时相对于页面(如viewport区域)的垂直坐标。
currentTarget事件冒泡阶段所在的当前DOM元素
target返回触发此事件的元素(事件的目标节点)。
eventPhase返回事件传播的当前阶段。
cancelable返回布尔值,指示事件是否可拥可取消的默认动作。
type返回当前 Event 对象表示的事件的名称。
timeStamp返回事件生成的日期和时间。

event事件一般也要做兼容,firfox兼容window.event

e = e || window.event

事件行为

阻止默认事件行为

很多标签拥有默认的事件行为 比如a标签 点击会执行跳转页面行为 , 我们可以通过代码阻止这些行为

var oLink = document.querySelector('a');

oLink.onclick = function(e){
	e.preventDefault(); 
    //preventDefault它是事件对象(Event)的一个方法,作用是取消一个目标元素的默认行为
}

oLink.onclick = function(e){
	//代码
    return false; //回调函数最后返回false可以阻止默认行为 阻止冒泡行为 返回false
}

//针对a标签href的值设置为javascript:; 由js接管href行为,不会跳转
//注意<a href="">聊天信息内容</a>,不能将href设置为空,也会跳转,不过是跳转回当前页
//<a href="javascript:;">聊天信息内容</a>

注意:在有的开关类型的属性,如checked(默认行为:点击被选中),如果阻止其默认行为,虽然效果上不生效,但是DOM对象会辨识属性为 true

<!DOCTYPE html>
<html lang="zh-cn">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
  <input id="hum" name='sex' type="radio" >
  <label for="hum"></label>
  <input id="wom" name='sex' type="radio">
  <label for="wom"></label>
  <script>
    document.addEventListener('click', function (e) {
      if (e.target.tagName.toLowerCase() === 'input') {
        e.preventDefault();//阻止默认行为会影响单选框 多选框等需要默认行为生效的标签
        //阻止默认行为之后 点击radio 虽然效果上(网页页面上)不生效 但是DOM对象会辨识属性为 true
        console.log(e.target.checked);//true 点击哪个input对应input的checked为true
      }
    }, false);
  </script>
</body>
</html>

阻止事件传递(阻止冒泡)

所有的事件类型都会经历事件捕获但是只有部分事件会经历事件冒泡阶段,例如submit事件就不会被冒泡。

事件的传播是可以阻止的:
w3c规范下,使用stopPropagation()方法
在IE版本下设置eve.cancelBubble = true; 废弃
在捕获的过程中stopPropagation();后,后面的冒泡过程就不会发生了。

var oLink = document.querySelector('a');

oLink.onclick = function(e){
	e.stopPropagation(); 
   
}

事件解绑

事件绑定之后 如果需要解绑 可以销毁

var oBtn = document.querySelector('#btn');
var oClean = document.querySelector('#clean');

oBtn.onclick = function(){
    console.log('btn成功绑定点击事件');
}
oClean.onclick = function(){
    oBtn.onclick = null;
    console.log('btn已解绑点击事件');
}

事件解绑的应用场景:

以下场景,当用户点击弹出查看余额弹框后点击关闭,如果同行直接在页面中修改,使弹框显示,那么还能使用查看余额的点击方法,应该在关闭弹框时就不能查看,关闭时给查看余额点击事件赋值null,清楚内存。

<!DOCTYPE html>
<html lang="zh-cn">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title> 解绑</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    input {
      width: 80px;
      height: 35px;
      font-size: 20px;
      cursor: pointer;
    }

    .mask {
      display: none;
      position: fixed;
      top: 50%;
      left: 50%;
      width: 400px;
      height: 400px;
      padding: 20px;
      box-shadow: 0 0 8px #222;
      transform: translate(-50%, -50%);
    }

    span {
      width: 80px;
      height: 30px;
      padding: 20px;
      background-color: orange;
      line-height: 30px;
      text-align: center;
      cursor: pointer;
    }
  </style>
</head>

<body>
  <input id="btn" type="button" value='弹出弹窗'>
  <div class="mask">
    我是弹窗
    <span id='close'>点我关闭</span>
    <span id="show">点我打印你的银行余额</span>
  </div>
  <script>
    var oBtn = document.querySelector('#btn');
    var oMask = document.querySelector('.mask');
    var oShow = document.querySelector('#show');
    var oClose = document.querySelector('#close');

    oBtn.onclick = function () {
      oMask.style.display = 'block';
      oShow.onclick = function () {
        console.log(`余额为10000000000`);
      }
      oClose.onclick = function () {
        oMask.style.display = 'none';
        oShow.onclick = null; //事件注销
      }
    }
  </script>
</body>
</html>

事件委托

当需要对多个子元素进行循环事件绑定的时候,可以将事件委托与他们的共同父级,通过event对象属性来进行筛选和操作,节省开销。

var oUl = document.querySelector('list');

//监听在oUl上发生的点击事件 利用事件的传递性 获取e.target判断触发事件的实际DOM是否为li 
oUl.onclick = function(e){
    if(e.target.toLowerCase() === 'li'){
    	e.target.style.backgroundColor = '#368';
    }
}

事件分派

当一个容器有多个元素需要绑定同一个事件, 而点击不同元素所需要执行的操作不同时可以进行分派映射

 var oBox = document.querySelector('#box');

var eventMap = {
    'stretch': function (ele) {
        ele.style.width = '400px';
        ele.style.height = '400px';
    },
    'discolor': function (ele) {
        ele.style.backgroundColor = '#368';
    },
    'rotate': function (ele) {
        ele.style.transform = 'rotate(10deg)';
    }
}

document.onclick = function (e) {
    if (eventMap[e.target.id.toLowerCase()]) {
        eventMap[e.target.id.toLowerCase()](oBox);
    }
}

案例

数据模板渲染

需求:拿到后台的数据通过模板字符串的方法渲染到页面

思考:nodeList循环事件绑定与下标获取作用域问题

这种方式是用循环再最开始就给所有的子元素绑定事件,如果子元素比较多,不断的赋值会导致内存存储开销很大,不推荐使用,可以使用事件委托来完成

<!DOCTYPE html>
<html lang="zh-cn">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>事件和渲染 模板</title>
  <style>
    body {
      background-color: #f5f5f5;
      user-select: none;
    }

    ul {
      display: flex;
      justify-content: space-around;
      align-items: center;
      list-style: none;
      width: 960px;
    }

    li {
      width: 234px;
      height: 300px;
      margin-left: 20px;
      padding: 10px;
      background-color: #fff;
      text-align: center;
    }

    .red {
      color: #ff0084;
    }

    .pic {
      width: 160px;
      height: 160px;
    }

    .title {
      font-size: 14px;
      font-weight: 400;
      line-height: 42px;
    }

    .desc {
      width: 180px;
      height: 18px;
      font-size: 12px;
      color: #b0b0b0;
      text-align: center;
      text-overflow: ellipsis;
      white-space: nowrap;
      overflow: hidden;

    }

    .price {
      font-size: 12px;
      color: #444;
      line-height: 28px;
    }
  </style>
</head>

<body>
  <ul id="list">

  </ul>
  <script>
    var responseData = [
      {
        title: '腾讯黑鲨3S',
        des: '骁龙865处理器,120Hz刷新率',
        price: '3999',
        picSrc: '//ebp&q=90'
      },
      {
        title: '小米10 青春版 5G',
        des: '50倍潜望式变焦 / 轻薄5G手机',
        price: '1899',
        picSrc: '//=webp&q=90'
      },
      {
        title: '小米10',
        des: '骁龙865/1亿像素相机',
        price: '3699',
        picSrc: '//cdn.cnp&q=90'
      },
      {
        title: 'Redmi 9A',
        des: '5000mAh长循环大电量,6.53"超大护眼屏幕',
        price: '499',
        picSrc: '//cdn.cp&q=90'
      }
    ];

    var oUl = document.querySelector('#list');
    drawHtml(oUl, formattingData(responseData)); 
    aLi = oUl.querySelectorAll('li');
    /* 
        点击li打印li里数据商品的标题 并且打印 对应li的下标
        把变量变成具体的值 传参可以让变量实参直接在函数内以形参的形式变成值
     */
    //循环给aLi集合内的每一个li元素对象绑定onclick事件函数
    for (var i = 0, len = aLi.length; i < len; i++) {
      (function (idx) {//IIFE (function(形参){})(实参)
        aLi[idx].onclick = function () {
          console.log(idx);
        }
      })(i);
    }
    
    //将数据生成HTML模板字符串
    function formattingData(data) {
      return data.reduce(function (acc, curr, idx, arr) {
        acc += `<li> 
                <img class="pic"
                  src="${curr['picSrc']}"
                  alt="${curr['title']}" width="200" height="200">
                <h3 class="title">${curr['title']}</h3>
                <p class="desc">${curr['des']}</p>
                <p class="price"><span class="red">${curr['price']}</span>元起</p>
              </li>`;
        return acc;
      }, '');
    }

    function drawHtml(parent, htmlStr) {
      parent.innerHTML = htmlStr;
    }
  </script>
</body>

</html>

toggle切换事件代码优化

需求:实现点击切换隐藏和显示

  1. 提取不变的行为 属性赋值操作 DOM api操作

  2. 辨别真正需要改变的是 值 还是 操作 本身

    1. 思考如何将值剥离 => 变量
  3. 值是否可以进行预设 尤其是两个值切换的这种情况下

<!DOCTYPE html>
<html lang="zh-cn">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }
    div {
      width: 200px;
      height: 200px;
      background-color: #368;
    }
    input {
      position: absolute;
      top: 200px;
      left: 200px;
      width: 180px;
      height: 90px;
      border: 0;
      background-color: #222;
      color: #fff;
      font-size: 20px;
    }
  </style>
</head>
<body>
  <!-- 点击input 让div 展示或隐藏-->
  <div id="box">我出来了</div>
  <input id="btn" type="button" value="隐藏">
  <script>
    var oDiv = document.querySelector('#box');
    var oBtn = document.querySelector('#btn');
    var toggle = true; //开关 默认为true 显示
    //三种情况切换推荐用这个
    oBtn.onclick = function () {
      var val = '隐藏', dis = 'block';
      if (toggle) {
        val = '显示';
        dis = 'none';
      }
      toggle = !toggle;
      oDiv.style.display = dis;
      this.value = val;
    }
    //只适用于两种情况切换
    oBtn.onclick = function () {
      oDiv.style.display = toggle ? 'none' : 'block';
      this.value = toggle ? '显示' : '隐藏';
      toggle = !toggle;
    }
  </script>
</body>
</html>

事件委托案例:点击不同按钮变换不同的CSS

在这里插入图片描述

<!DOCTYPE html>
<html lang="zh-cn">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    #box {
      width: 200px;
      height: 200px;
      background-color: #222;
      transition: .5s;
    }

    input {
      width: 80px;
      height: 35px;
      font-size: 20px;
      cursor: pointer;

    }
  </style>
</head>

<body>
  <div id="box"></div>
  <input id="stretch" type="button" value="拉伸">
  <input id="discolor" type="button" value="变色">
  <input id="rotate" type="button" value="旋转">
  <script>
    var oBox = document.querySelector('#box');

    var eventMap = {
      'stretch': function (ele) {
        ele.style.width = '400px';
        ele.style.height = '400px';
      },
      'discolor': function (ele) {
        ele.style.backgroundColor = '#368';
      },
      'rotate': function (ele) {
        ele.style.transform = 'rotate(10deg)';
      }
    }

    document.onclick = function (e) {  
      if (eventMap[e.target.id.toLowerCase()]) {
        eventMap[e.target.id.toLowerCase()](oBox);
      }
    }
  </script>
</body>

</html>

监听器

DOM 事件监听器 addEventListener() 方法 与 attachEvent()

addEventListener() 允许您将事件监听器添加到任何 HTML DOM 对象上,比如 HTML 元素、HTML 对象、window 对象或其他支持事件的对象,比如 xmlHttpRequest 对象。

事件监听

//监听窗口大小变化
window.addEventListener("resize", function(){
    console.log(window.innerWidth,window.innerHeight); // document.documentElement.width 
},false);

----------------------------------------------

oWrap.addEventListener('click',touch,false);
oWrap.attachEvent("onclick",touch);//IE8老版本兼容写法

function touch(e){
    //do somthing
}

注意:
1. addEventListener用于标准浏览器 attachEvent用于老版本IE浏览器
2. addEventListener参数依次为 事件名称(不加on) 事件触发函数 事件触发阶段(true:捕获,false:冒泡)
3. addEventListener 回调函数内部this指向 绑定对象
事件监听与on绑定区别

on事件会被后面的on的事件覆盖, addEventListener 则不会覆盖;

addEventListener可以指定事件回调触发时机 (捕获 or 冒泡) on事件只有 冒泡时刻触发

addEventListener本质是*函数定义与具体调用事件的解耦,实现了同一事件可以调用多个函数,同一函数可以被多个事件调用,推荐使用*

addEventListener和attachEven方法区别
1.方法名不一样
2.参数个数不一样addEventListener三个参数,attachEvent两个参数
3.addEventListener 谷歌,火狐,IE11支持,IE8不支持
attachEvent 谷歌火狐不支持,IE11不支持,IE8支持
4.this不同,addEventListener 中的this是当前绑定事件的对象
attachEvent中的this是window
5.addEventListener中事件的类型(事件的名字)没有on
attachEvent中的事件的类型(事件的名字)有on
立即执行一次的事件监听

方法:执行一次函数并返回函数本身

案例:div宽高与浏览器宽高保持一致,随着浏览器窗口大小的变化跟随着变化,不出现滚动条,始终沾满整个浏览器。

<!DOCTYPE html>
<html lang="zh-cn">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title></title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    html,
    body {
      height: 100%;
    }

    div {
      background-color: #222;
    }
  </style>
</head>

<body>
  <div></div>
  <script>
    const oDiv = document.querySelector('div')
    init();

    function init() {
      window.addEventListener('resize', resizeView(), false)//立即执行一次
    }

    function resizeView() { //浏览器窗口大小变化时调用
      const winWidth = window.innerWidth;
      const winHeight = window.innerHeight;
      console.log(11)
      oDiv.style.width = winWidth + 'px';
      oDiv.style.height = winHeight + 'px';
      return resizeView;
    }
  </script>
</body>

</html>

解除监听

oWrap.removeEventListener("click",touch,false);

oWrap.detachEvent("onclick",touch);

注意:用什么方式绑定事件,就应该用对应的方式解绑事件
1.
对象.on事件名字=事件处理函数--->绑定事件
对象.on事件名字=null;
2.
//removeEventListener不支持匿名函数的解绑行为
对象.addEventListener("没有on的事件类型",命名函数,false);---绑定事件
对象.removeEventListener("没有on的事件类型",函数名字,false);
3.
对象.attachEvent("on事件类型",命名函数);---绑定事件
对象.detachEvent("on事件类型",函数名字);

兼容写法

//为任意一个元素绑定事件:元素,事件类型,事件处理函数
function addEventListener(element,type,fn) {
    if(element.addEventListener){
        //支持
        element.addEventListener(type,fn,false);
    }else if(element.attachEvent){
        element.attachEvent("on"+type,fn);
    }else{
        element["on"+type]=fn;
    }
}
 
//为任意的一个元素解绑某个事件:元素,事件类型,事件处理函数
function removeEventListener(element,type,fn) {
    if(element.removeEventListener){
        element.removeEventListener(type,fn,false);
    }else if(element.detachEvent){
        element.detachEvent("on"+type,fn);
    }else{
        element["on"+type]=null;
    }
}


//优化合并如下
//事件监听的最终兼容写法,兼容attachEvent,addEventListener,也包括匿名函数解绑
/**
 * @description: 函数的功效
 * @param {Object} element 需要监听的DOM对象
 * @param {String} type 事件类型 click mouseenter
 * @param {Function} handler 监听绑定的回调函数
 * @param {Boolean} capture true 捕获阶段监听 false 冒泡阶段监听 
 * @return {JSON} "remove":Function 返回一个用于解除监听的函数
 * @Date: 2020-08-10 22:45:25
 */
function eventListener (element, type, handler, capture){
	capture = capture || false;
    if(element.addEventListener){
        // W3C内核
        element.addEventListener(type, handler, capture);
    }else{
        // IE内核
        element.attachEvent('on'+type, handler, capture);
    }

    return {
        "remove":function(){
            if(element.removeEventListener){
                // W3C内核
                element.removeEventListener(type, handler, capture);
            }else{
                // IE内核
                element.detachEvent('on'+type, handler, capture);
            } 
    	}
    }
}

//用法(绑定与解绑)
var btn = document.getElementById('btn');
var cancel = document.getElementById('cancel');

// 添加监听
var addAlert = eventListener(btn,'click',function(){
    alert(123);
});
eventListener(cancel,'click',function(){
    // 移除监听
    addAlert.remove();
    alert('移除成功');
});

定时器

setTimeout()和clearTimeout()

在指定的毫秒数到达之后执行指定的函数,只执行一次

注:只要使用定时器,都要提前把定时器清除

// 创建一个定时器,1000毫秒后执行,返回定时器的标示
var timerId = setTimeout(function () {
  console.log('Hello World');
}, 1000);

// 取消定时器的执行
clearTimeout(timerId);

setInterval()和clearInterval()

定时调用的函数,可以按照给定的时间(单位毫秒)周期调用函数

// 创建一个定时器,每隔1秒调用一次
var timerId = setInterval(function () {
  var date = new Date();
  console.log(date.toLocaleTimeString());
}, 1000);

// 取消定时器的执行
clearInterval(timerId);

定时器使用注意事项

  1. 终止定时器

​ 终止定时器只是意味着定时器的终结,不代表本次函数也会终结,函数的主动终止只有return和全部代码都执行完毕。

​ 如果在定时器内关掉定时器,那么也会把此次定时器的所有代码执行完毕,如果不想关掉定时器后继续执行本次定时器后的代码,可以在关闭定时器时,在后面return false。

​ 如果要终止函数的话建议不要写return,因为没有写retrun什么会默认return undefined,函数自然终止没有返回值默认也是undefined,所以return false可以明确表达这是通过代码return终止的函数处理

  1. 使用定时器

​ 如果有做定时器的启停功能,如轮播图的自动播放,在开启定时器自动播放时,最好先提前清楚定时器,以防止开启多个定时器,养成良好习惯。

function autoTranslate() {
	clearInterval(timer)
	timer = setInterval(){}
}

setInterval失序问题

1. HTML5规范规定了最小延迟时间不得小于4ms,即如果x小于4,会被当做4来处理
2. 由于javascript单线程 执行队列需要进行插入调整 所以setInterval会出现下述问题
	(1)某些间隔会被跳过了
    (2)多个定时器的代码执行间隔可能会比预期的要小。
3. 定时器调用传入函数名称+() 会导致回调函数直接执行 
4. 通过setTimeout递归自调可以替代setInterval
5. 当前页面处于hide(不可见 离开)状态时 定时器会休眠 但是队列会持续添加 会导致失序

在这里插入图片描述

递归setTimeout实现有序的定时序列
//参数: 毫秒  需要执行的方法
function setInter(s,fn){
    function timeOut(s,fn){
      setTimeout(function(){
        fn();
        timeOut(s,fn);
      },s)
  }
  timeOut(s,fn);
}

鼠标Event坐标位置

鼠标event对象给我们展示了 client offset page screen系列的X Y坐标 可以用于在不同需求下获取鼠标的相对位置

属性说明
clientX以浏览器左上顶角为原点,定位 x 轴坐标
clientY以浏览器左上顶角为原点,定位y轴坐标
offsetX以当前事件的目标对象content左上角为原点,定位x轴坐标
offsetY以当前事件的目标对象content左上角为原点,定位y轴坐标
pageX以Document 对象(即文本窗口)左上角为原点,定位x轴坐标
pageY以Document 对象(即文本窗口)左上角为原点,定位y轴坐标
screenX计算机屏幕左上角为原点,定位x轴坐标
screenY计算机屏幕左上角为原点,定位y轴坐标
layerX最近的绝对定位的父元素(如果没有,则为Document对象)左上角为原点,定位x轴坐标
layerY最近的绝对定位的父元素(如果没有,则为Document对象)左上角为原点,定位y轴坐标
图例

在这里插入图片描述

兼容

在这里插入图片描述

视窗位置与尺寸

获取视口宽高

下面方法是包括滚动条的宽高,不支持 IE8

window.innerWidth
window.innerHeight

width + padding + border + 滚动条 另外 outerWidth 浏览器兼容差,可获取包括工具栏的宽高

获取窗口的宽高

window.outerWidth
window.outerHeight

获取获取窗口的宽高 (包括工具栏 控制台 地址栏),兼容差

页面滚动位置

返回整个页面的滚动的位置,获取滚动的高度pageYOffset/pageXOffset 与 scrollY/scrollX 返回的值一致,前者是后者的别名,建议使用前者,不支持 IE8

window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft

窗口在显示器的位置

标准浏览器使用的是 screenX/screenY,IE 中使用的是 screenLeft/screenTop

window.screenLeft || window.screenX
window.screenTop || window.screenY

getBoundingClientRect

元素占用的空间尺寸和位置

使用方法 getBoundingClientRect() 返回的值见下图:

在这里插入图片描述

IE 只返回 top right bottom left 四个值,如果需要 width height 则需要计算:

function getBoundingClientRect(elem) {
    var rect = elem.getBoundingClientRect()
    return {
        top: rect.top,
        right: rect.right,
        bottom: rect.bottom,
        left: rect.left,
        width: rect.width || rect.right - rect.left,
        height: rect.height || rect.bottom - rect.top
    }
}

client系列

clientWidth/clientHeight

返回元素不含滚动条的尺寸,不包括边框

document.documentElement.clientWidth || document.body.clientWidth
document.documentElement.clientHeight || document.body.clientHeight
  • 如果是 document.documentElement,那么返回的是不包含滚动条的视口尺寸
  • 如果是 document.body,并且是在混杂模式下,那么返回的是不包含滚动条的视口尺寸

clientLeft/clientTop

返回的是计算后的 CSS 样式的 border-left-width/border-top-width 的值,就是边框的宽度
在这里插入图片描述

offset系列

offsetWidth/offsetHeight

同样可以使用 offsetWidth/offsetHeight 来获取元素包括滚动条和边框的尺寸,这个方法返回元素本身的宽高 + padding + border + 滚动条

offsetLeft/offsetTop

相对于最近的祖先定位元素(CSS position 属性被设置为 relative、absolute 或 fixed 的元素)的左右偏移值

offsetLeft/offsetTop 返回元素 X Y 坐标值

偏移量

  • offsetParent用于获取定位的父级元素
  • offsetParent和parentNode的区别
var box = document.getElementById('box');
console.log(box.offsetParent);
console.log(box.offsetLeft);
console.log(box.offsetTop);
console.log(box.offsetWidth);
console.log(box.offsetHeight);

在这里插入图片描述

封装

获取元素相对于windowView的距离

function getPostion(element) {
  var pos = {
    left: 0,
    top: 0
  }
  while (element.offsetParent) {
    pos.left += element.offsetLeft;
    pos.top += element.offsetTop;
    element = element.offsetParent;
    //如果有边框要计算入父级边框 
    pos.left += element.clientLeft;
    pos.top += element.clientTop;
  }
  return pos;
}

scroll系列

元素内容的宽高和滚动距离

scrollWidth/scrollHeight

这个方法返回元素内容区域的宽高 + padding + 溢出内容尺寸

document.documentElement.scrollWidth || document.body.scrollWidth
document.documentElement.scrollHeight || document.body.scrollHeight

  • 如果元素是 document.documentElement,返回的是视口滚动区域宽度和视口宽度中较大的那个
  • 如果元素是 document.body,并且是在混杂模式下,那么返回的是视口滚动区域宽度和视口宽度中较大的那个

scrollLeft/scrollTop

这个方法返回元素滚动条的位置

  • 如果元素是根元素,那么返回 window.scrollY 的值
  • 如果元素是 body,并且在混杂模式下,那么返回的是 window.scrollY 的值

因此可用于处理页面滚动的距离的兼容

在这里插入图片描述

汇总

offset系列:获取元素的宽,,left,top, offsetParent
offsetWidth:元素的宽,有边框
offsetHeight:元素的高,有边框
offsetLeft:元素距离父元素位置的值
offsetTop:元素距离父元素位置的值

scroll系列:卷曲出去的值
scrollLeft:向左卷曲出去的距离
scrollTop:向上卷曲出去的距离
scrollWidth:元素中内容的实际的宽(如果内容很少或者没有内容, 元素自身的宽),没有边框
scrollHeight:元素中内容的实际的高(如果内容很少或者没有内容,元素自身的高),没有边框

client系列:可视区域
clientWidth:可视区域的宽(没有边框),边框内部的宽度
clientHeight:可视区域的高(没有边框),边框内部的高度
clientLeft:左边边框的宽度
clientTop :上面的边框的宽度

getBoundingClientRect:不管元素的当前层级,定位层级 ,top left bottom right x y 都是距离浏览器的左上角
bottom: 底边距离浏览器顶部
height: 元素总高度(height+padding+border)
left: 左边距离浏览器左边
right: 右边距离浏览器左边 right - left = width
top: 顶部距离浏览器顶部
width:  元素总宽度(width+padding+border)
x: 元素左上角顶点距离浏览器左上角顶点的坐标X
y: 元素左上角顶点距离浏览器左上角顶点的坐标Y
      
event鼠标事件的坐标属性,他们都是鼠标指针尖端在触发事件的时候 的坐标位置
clientX clientY:触发事件时鼠标指针距离window窗口左上角的位置
                 坐标原点为 window窗口的左上角 
pageX pageY:触发事件时鼠标指针距离docuemnt文档左上角的位置
             坐标原点为 document文档的左上角 
offsetX offsetY:元素content内容区域的左上角为原点
                以当前事件的目标对象边框内沿左上角为原点,定位x轴坐标
screenX screenY:触发事件时鼠标指针距离 屏幕视窗(显示器)左上角的位置
x y:设置或获取鼠标指针位置相对于父文档的 x y 像素坐标
layerX layerY:触发事件的DOM对象的DOM树上的父级(祖级)中最近的拥有定位属性的元素的左上角为原点 (鸡肋)

视窗系列
window.innerWidth :浏览器可视区域窗口的width + padding + border + 滚动条
window.innerHeight :浏览器可视区域窗口的height + padding + border + 滚动条
outerWidth : 浏览器整体宽度 (包括工具栏 控制台 地址栏)
outerHeight : 浏览器整体高度 (包括工具栏 控制台 地址栏)
window.pageYOffset :页面滚动位置,就是获取滚动的高度
document.documentElement.scrollTop || document.body.scrollTop :获取滚动的高度
window.screenLeft || window.screenX :窗口在显示器的位置,距离显示器左边的距离
window.screenTop || window.screenY :窗口在显示器的位置,距离显示器上面的距离

防抖 节流

在前端开发的过程中,我们经常会需要绑定一些持续触发的事件,如 resize、scroll、mousemove 等等,但有些时候我们并不希望在事件持续触发的过程中那么频繁地去执行函数。

防抖 (debounce)

所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。

ps: 重置普攻,百度翻译要输完停止一定时间后才翻译。

策略
当事件被触发时,设定一个周期延时执行动作,若周期又被触发,则重新设定周期,直到周期结束,执行动作。
在后期有拓展了前缘防抖函数,即执行动作在前,设定延迟周期在后,周期内有事件被触发,不执行动作,且周期重新设定。

案例:停止输入后将输入的字符串翻转

var oInput = $('.input-box');
var oShow = $('.show-box');
var timeOut;//这个timeOut必须是全局变量
oInput.addEventListener('input', function () {
    timeOut && clearTimeout(timeOut);
    timeOut = setTimeout(function () {
        oShow.innerText = translate(oInput.innerText);
    }, 500);
}, false);

function translate(str) {
    return str.split("").reverse().join("");
}

节流 (throttling)

**所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。**节流会稀释函数的执行频率。

对于节流,有多种方式可以实现 时间戳 定时器 束流等。

ps : 技能CD

策略:
固定周期内,只执行一次动作,若没有新事件触发,不执行。周期结束后,又有事件触发,开始新的周期。
特点
连续高频触发事件时,动作会被定期执行,响应平滑

计时器版
var oCon = $('.container');
var num = 0;
var valid = true;
oCon.addEventListener('mousemove', function () {
    if (!valid) {
        return false;
    }
    valid = false;
    setTimeout(function () {
        count();
        valid = true;
    }, 500);
}, false);

function count() {
    oCon.innerText = num++;
}

时间戳版

var oCon = $('.container');
var num = 0;
var time = Date.now();
oCon.addEventListener('mousemove', function () {
    if (Date.now() - time < 600) {
        return false;
    }
    time = Date.now();
    count();
}, false);

function count() {
    oCon.innerText = num++;
}

束流器版

一般用在游戏中,元素以不同频率运动

这样做的好处是可以把所有元素用同一个定时器来管理,用速差来做不同频率的运动

var oCon = $('.container');
var num = 0;
var time = 0;
oCon.addEventListener('mousemove', function () {
    time++;
    if (time % 30 !== 0) {
        return false;
    }
    console.log(time)
    count();
}, false);

function count() {
    oCon.innerText = num++;
}

mousewheel事件

当用户通过鼠标滚轮与页面交互、在垂直方向上滚动页面时,就会触发mousewheel事件,这个事件就是实现全屏切换效果需要用到的。在IE6, IE7, IE8, Opera 10+, Safari 5+中,都提供了 “mousewheel” 事件,而 Firefox 3.5+ 中提供了一个等同的事件:”DOMMouseScroll”。与mousewheel事件对应的event对象中我们还会用到另一个特殊属性—wheelDelta属性。

  1. “mousewheel” 事件中的 “event.wheelDelta” 属性值:返回的值,如果是正值说明滚轮是向上滚动,如果是负值说明滚轮是向下滚动;返回的值,均为 120 的倍数,即:幅度大小 = 返回的值 / 120。
  2. “DOMMouseScroll” 事件中的 “event.detail” 属性值:返回的值,如果是负值说明滚轮是向上滚动(与 “event.wheelDelta” 正好相反),如果是正值说明滚轮是向下滚动;返回的值,均为 3 的倍数,即:幅度大小 = 返回的值 / 3。

scroll事件和mousewheel事件的区别

  1. scroll事件

当元素内容发生滚动的时候 (滚动条滚动)的时候 触发这个事件

只要滚动条的位置发生变化 都会触发

持续高频触发的事件

element.scrollTop 可读写 设置的时候不要写单位

  1. mousewheel事件

mousewheel事件 鼠标滚轮滚动事件

持续高频触发的事件

e.wheelDelta : 向下滚动 为 负值 向上滚动 为 正值

  1. 案例

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <style>
        * {
          margin: 0;
          padding: 0;
        }
        html,
        body {
          height: 100%;
        }
        body {
          height: 1000px;
          background-image: url(images/bird.png);
        }
      </style>
    </head>
    <body>
      <input type="button" value='设置滚动高度为 100'>
      <script src="js/common.js"></script>
      <script>
        document.addEventListener('scroll', function () {
          //如何获取滚动高度 scrollTop
          console.log(document.documentElement.scrollTop || document.body.scrollTop);
    
        }, false);
    
        document.addEventListener('mousewheel', function (e) {
          //firfox兼容 window.event
          e = e || window.event;
          if (e.wheelDelta > 0) {
            console.log(' 向上滚动');
          } else {
            console.log(' 向下滚动');
          }
          console.log(document.documentElement.scrollTop || document.body.scrollTop);
        }, false);
    
        $('input').addEventListener('click', function () {
          document.documentElement.scrollTop = 100;
          console.log(document.documentElement.scrollTop || document.body.scrollTop);
        }, false);
      </script>
    </body>
    </html>
    

自定义滚动条

在这里插入图片描述

<!DOCTYPE html>
<html>
<head lang="en">
  <meta charset="UTF-8">
  <style>
    * {
      user-select: none;
      margin: 0;
      padding: 0;
    }
    .wrap {
      overflow: hidden;
      position: relative;
      width: 300px;
      height: 500px;
      margin: 100px auto;
      box-shadow: 0 0 8px #222;
    }
    .content {
      position: absolute;
      top: 0;
      left: 0;
      padding: 5px 20px 5px 5px;
      color: #333;
      line-height: 22px;
      font-size: 14px;
      text-align: justify;
    }
    .scroll {
      position: absolute;
      top: 0;
      right: 0;
      width: 15px;
      height: 100%;
      background-color: #ccc;
    }
    .bar {
      position: absolute;
      width: 15px;
      height: 80px;
      background-color: #444;
      border-radius: 4px;
      cursor: pointer;
    }
  </style>
</head>
<body>
  <div class="wrap">
    <div class="content">
      浔阳江头夜送客,枫叶荻花秋瑟瑟。主人下马客在船,举酒欲饮无管弦。醉不成欢惨将别,别时茫茫江浸月。忽闻水上琵琶声,主人忘归客不发。寻声暗问弹者谁,琵琶声停欲语迟。移船相近邀相见,添酒回灯重开宴。千呼万唤始出来,犹抱琵琶半遮面。转轴拨弦三两声,未成曲调先有情。弦弦掩抑声声思,似诉平生不得志。低眉信手续续弹,说尽心中无限事。轻拢慢捻抹复挑,初为《霓裳》后《六幺》。大弦嘈嘈如急雨,小弦切切如私语。嘈嘈切切错杂弹,大珠小珠落玉盘。间关莺语花底滑,幽咽泉流冰下难。冰泉冷涩弦凝绝,凝绝不通声暂歇。别有幽愁暗恨生,此时无声胜有声。银瓶乍破水浆迸,铁骑突出刀枪鸣。曲终收拨当心画,四弦一声如裂帛。东船西舫悄无言,唯见江心秋月白。沉吟放拨插弦中,整顿衣裳起敛容。自言本是京城女,家在虾蟆陵下住。十三学得琵琶成,名属教坊第一部。曲罢曾教善才服,妆成每被秋娘妒。五陵年少争缠头,一曲红绡不知数。钿头银篦击节碎,血色罗裙翻酒污。今年欢笑复明年,秋月春风等闲度。弟走从军阿姨死,暮去朝来颜色故。门前冷落鞍马稀,老大嫁作商人妇。商人重利轻别离,前月浮梁买茶去。去来江口守空船,绕船月明江水寒。夜深忽梦少年事,梦啼妆泪红阑干。我闻琵琶已叹息,又闻此语重唧唧。同是天涯沦落人,相逢何必曾相识!我从去年辞帝京,谪居卧病浔阳城。浔阳地僻无音乐,终岁不闻丝竹声。住近湓江地低湿,黄芦苦竹绕宅生。其间旦暮闻何物?杜鹃啼血猿哀鸣。春江花朝秋月夜,往往取酒还独倾。岂无山歌与村笛?呕哑嘲哳难为听。今夜闻君琵琶语,如听仙乐耳暂明。莫辞更坐弹一曲,为君翻作《琵琶行》。感我此言良久立,却坐促弦弦转急。凄凄不似向前声,满座重闻皆掩泣。座中泣下谁最多?江州司马青衫湿。
    </div>
    <div class="scroll">
      <div class="bar"></div>
    </div>
  </div>
  <script>
    var oWrap = document.querySelector('.wrap');
    var oCon = document.querySelector('.content');
    var oScroll = document.querySelector('.scroll');
    var oBar = document.querySelector('.bar');
    var ratio = 0;

    initScroll();
    //初始化函数
    function initScroll() {
      //初始化滚动轴高度  滚动轴高度 = 
      oBar.style.height = oWrap.offsetHeight / oCon.offsetHeight * oScroll.offsetHeight + 'px';
      ratio = (oCon.offsetHeight - oWrap.offsetHeight) / (oScroll.offsetHeight - oBar.offsetHeight);
    }

    var scrollEventMap = {
      isDown: false,
      start: {
        y: 0,
        top: 0
      },
      mousedown: function (e) {
        this.isDown = true;
        this.start.y = e.clientY;
        this.start.top = oBar.offsetTop;
      },
      mousemove: function (e) {
        if (!this.isDown) {
          return false;
        }
        var diffY = e.clientY - this.start.y;
        var _y = diffY + this.start.top;
        scrollBar(_y);
      },
      mouseup: function (e) {
        if (!this.isDown) {
          return false;
        }
        this.isDown = false;
      },
      mousewheel: function (e) {
        var _y = oBar.offsetTop;
        if (e.wheelDelta > 0) {
          _y -= 2;
        } else {
          _y += 2;
        }
        scrollBar(_y);
      }
    }

    oBar.addEventListener('mousedown', touchScroll, false);
    document.addEventListener('mousemove', touchScroll, false);
    document.addEventListener('mouseup', touchScroll, false);
    oWrap.addEventListener('mousewheel', touchScroll, false);

    function scrollBar(_y) {
      _y = Math.max(0, _y);
      _y = Math.min(_y, oScroll.offsetHeight - oBar.offsetHeight);
      oBar.style.top = _y + 'px';
      oCon.style.marginTop = -_y * ratio + 'px';
    }

    function touchScroll(e) {
      if (scrollEventMap[e.type] && typeof scrollEventMap[e.type] === 'function') {
        scrollEventMap[e.type](e);
      }
    }
  </script>
</body>
</html>

封装

function mousewheel(obj,fn){

    obj.onmousewheel===null ? obj.onmousewheel=fun : obj.addEventListener('DOMMouseScroll',fun,false);

    function fun(e){
        var e=e || event,
            num=0;

        if(e.wheelDelta){
            num=e.wheelDelta>0?1:-1;
        }else{
            num=e.detail<0?1:-1;
        }
        fn(num);

        if(e.preventDefault)e.preventDefault();
        return false;
    }
}

//调用
var oDiv=document.getElementById('div');

mousewheel(oDiv,function(dir){
    if(dir>0) alert('向上滚动');
    if(dir<0) alert('往下滚动');
});

scrollIntoView()

ele.scrollIntoView() 让元素滚动到可视区域(HTML5标准),参数 true 与浏览器对齐,false元素在窗口居中显示

运动框架

通过javascript 操作DOM元素的样式,使DOM元素的形状、位置 等外在表现发生改变,同时这个改变是过渡的(transition) width:100px 在.5s 内 变化为 width:200px;

transtion封装animate

使用transition事件,目标元素的transition行为结束之后,过渡结束之后触发

<!DOCTYPE html>
<html lang="zh-cn">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>transtion 运动框架</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    .box {
      position: absolute;
      top: 100px;
      left: 0;
      width: 100px;
      height: 100px;
      background-color: #368;
    }

    .btn {
      width: 80px;
      height: 36px;
      margin: 10px;
      background-color: orange;
      line-height: 35px;
      text-align: center;
      color: #fff;
    }
  </style>
</head>
<body>
  <div class="box"></div>
  <div class="btn">点击</div>
  <script src="js/common.js"></script>
  <script>
    var oBtn = $('.btn');
    var oBox = $('.box');

    oBtn.addEventListener('click', function () {
      animate(oBox, { left: '100px', height: '200px', backgroundColor: 'green' }, 500, 'ease-out',
        function () {
          animate(oBox, {
            left: '200px', height: '100px', backgroundColor: 'blue'
          }, 1000, 'linear',
            function () {
              animate(oBox, {
                left: '1200px', height: '300px', backgroundColor: 'yellow'
              }, 1000, 'linear',
                function () {
                  console.log('三次运动结束')
                });
            });
        });
    }, false);

    function animate(ele, styleJson, time, speed, callback) {
      speed = speed || 'linear';
      time = time || 500;
      ele.style.transition = `${time}ms ${speed}`;
      setStyle(ele, styleJson);
      ele.addEventListener('transitionend', end, false); //transitionend事件
      function end() {
        callback && callback();
        ele.removeEventListener('transitionend', end);//解除监听removeEventListene的时候,必须指定函数名。
        ele.style.transition = `0`;                  //对ele的transtioned事件进行解除监听(解绑),不然下一次动画完成后还会继续执行上一次
      }
    }
  </script>
</body>
</html>

定时器封装animate

<!DOCTYPE html>
<html lang="zh-cn">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>定时器运动框架</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }
    .box {
      position: absolute;
      top: 100px;
      left: 0;
      width: 50px;
      height: 50px;
      background-color: #368;
    }
    .btn {
      width: 80px;
      height: 36px;
      margin: 10px;
      background-color: orange;
      line-height: 35px;
      text-align: center;
      color: #fff;
    }
  </style>
</head>
<body>
  <div class="box"></div>
  <div class="btn">点击</div>
  <script>
    var oBox = document.querySelector('.box');
    var oBtn = document.querySelector('.btn');

    oBtn.addEventListener('click', function () {
      animate(oBox, {
        width: '100px',
        height: '200px',
        left: '200px'
      }, function () {
        animate(oBox, {
          width: '100px',
          height: '100px',
          left: '0px'
        }, function () {
          console.log('两次都结束了');
          animate(oBox, {
            width: '300px',
            height: '300px',
            left: '30px'
          }, function () {
            console.log('两次都结束了')
          });
        });
      });
    }, false);

    function animate(ele, json, callback) {
      clearInterval(ele.time);//保证每一次都只有一个定时器在运行,总之只要用定时器 就先清除
      var toggle = false;
      var currLeft = parseInt(getStyle(ele, 'left'));
      ele.time = setInterval(function () {
        //每次定时循环都暂且认为他们都可以达到最终结果
        toggle = true;
        for (var key in json) {
          var target = parseInt(json[key])
          curr = parseInt(getStyle(ele, key));
          speed = (target - curr) / 10;
          speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
          if (curr === target) {
            //width 先到了指定值了 定时器结束了 
            ele.style[key] = target + 'px';
          }
          ele.style[key] = curr + speed + 'px';
          if (curr !== target) {
            //只要有某一个属性的值不到指定结果 关闭锁
            toggle = false;
          }
        }
        if (toggle) {
          clearInterval(ele.time);
          callback && callback();
        }
      }, 1000 / 60);
    }

    function getStyle (obj, attr) {
      return obj.currentStyle ? obj.currentStyle[attr] : getComputedStyle(obj, false)[attr];
    }
  </script>
</body>
</html>

textarea注意事项

获取textarea的值时使用value获取,不要用innerText或innerHTML

<!DOCTYPE html>
<html lang="zh-cn">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .header .logo-wrap .pic .left .icon p {
      width: 80px;
      height: 40px;
      background-color: #222;
      color: #fff;
    }
  </style>
</head>
<body>
  <textarea id="input">大家好o</textarea>
  <p>我是一片准备好的文章 我会进入textArea里</p>
  <script>
    var oInput = document.querySelector('#input');
    var oP = document.querySelector('p');

    oP.addEventListener('click', function () {
      oInput.value = oP.innerText; //3个都可以设置
      // oInput.innerHTML = oP.innerText;
      // oInput.innerText = oP.innerText;
    }, false);
    oInput.addEventListener('input', function (e) {
      console.log(this.innerText); //获取不到
      console.log(this.innerHTML); //获取初始的内容值
      console.log(this.value); //获取动态内容值
    }, false);
  </script>
</body>
</html>

??运算符 ( 试验中的 运算符 ) (了解)

ECMA 2021新增的试验 运算符 用来替代 || 在参数初始化等场景的使用 , 因为 || 在参数初始化 使用会对

‘’ 0 false NaN null 等隐式转换中 会默认转换为 false的实参生效 导致实参被修改 无法准确达到 使用|| 做参数初始化的目的 (用户如果没有传入对应实参 参数设置为 xxx)

注意事项: ?? 不能和 || 或者 && 在同一行使用 如果非要在同一行代码使用 需要用 ( ) 严格括起来所有的运算短语

param = param || 0; 

param = param ?? 0; 

(param && slid) ?? (x || 0)

DOM基础操作导图

)

在这里插入图片描述

BOM

Browser Object Mode, 浏览器对象模型。没有标准,浏览器厂家约定俗成。

BOM的核心是window,而window对象又具有双重角色,它既是通过js访问浏览器窗口的一个接口,又是一个Global(全局)对象。这意味着在网页中定义的任何对象,变量和函数,都以window作为其global对象。

BOM对象包括

  • Window对象:浏览器中打开的窗口, 顶层对象
  • Navigator对象:浏览器的相关信息
  • Screen对象:客户端显示屏幕的信息
  • History对象:用户在浏览器窗口中访问过的URL
  • Location对象:当前URL的信息

其中,window对象中包含对BOM其他四个对象的只读引用以及Document对象的只读引用

window对象 和 in 表达式

BOM的核心对象是 window

window作为BOM对象时的方法( window.alert()、 window.setInterval()、 window.prompt();)

window作为全局对象,全局变量都是window的属性

in 表达式

in 表达式

a in b

判断 a属性是否存在于 b对象之中

b是否包含a

  <script>
    var obj = {
      a: '1',
      b: undefined
    }
    console.log('b' in obj); //true

    if ('x' in window) {//if for是没有块级作用域的
      var x = 10;
    }
    console.log(x); //10
  </script>

document对象

document对象:实际上是window对象的属性
document == window.document为true,是唯一 一个既属于BOM又属于DOM的对象

document.lastModified

获取最后一次修改页面的日期的字符串表示

做工程化时,用修改时间来判断是否需要资源更新(热更新)

document.referrer

用于跟踪用户从哪里链接过来的

document.title

获取当前页面的标题,可读写

document.URL

获取当前页面的URL,可读写

-------------以下一般不用,都用选择器来获取对应的对象-------------------------

document.anchors[0] / document.anchors[“anchName”]

访问页面中所有的锚

document.forms[0] / document.forms[“formName”]

访问页面中所有的表单

document.images[0] / document.images[“imgName”]

访问页面中所有的图像

document.links [0] / document.links[“linkName”]

访问页面中所有的链接

document.applets [0] / document.applets[“appletName”]

访问页面中所有的Applet

document.embeds [0] / document.embeds[“embedName”]

访问页面中所有的嵌入式对象


document.write(); / document.writeln();

将字符串插入到调用它们的位置

location对象

表示载入窗口的URL,也可用window.location引用它

例:https://weibo.com/newlogin?tabtype=weibo&gid=102803&openLoginLayer=0&url=https%3A%2F%2Fweibo.com%2Fhot%2Fweibo%2F102803

location.href

当前载入页面的完整URL

如“https://weibo.com/newlogin?tabtype=weibo&gid=102803&openLoginLayer=0&url=https%3A%2F%2Fweibo.com%2Fhot%2Fweibo%2F102803”

location.protocol

URL中使用的协议,即双斜杠之前的部分

如“https:”

location.host

服务器的名字,

如"weibo.com" //通常等于host,有时会省略前面的www

location.port

URL声明的请求的端口,默认情况下,大多数URL没有端口信息,如8080

location.pathname

URL中主机名后的部分,执行GET请求的URL中的问号后的部分,又称查询字符串,如?param=xxxx location.hash //如果URL包含#,返回该符号之后的内容,如#anchor1

如"/newlogin"

location.assign(“http:www.baidu.com”);

在当前host后买你拼接assign内的路径,同location.href,新地址都会被加到浏览器的历史栈中

location.replace(“http:www.baidu.com”);

同assign(),但新地址不会被加到浏览器的历史栈中,不能通过back和forward访问

location.reload(true | false);

重新载入当前页面,为false时从浏览器缓存中重载 F5,为true时从服务器端重载Ctrl + F5,默认为false

navigator对象

navigator对象:

包含大量有关Web浏览器的信息,在检测浏览器及操作系统上非常有用,也可用window.navigator引用它

navigator.appCodeName

浏览器代码名的字符串表示 (一般都是Mozilla)

navigator.appName

官方浏览器名的字符串表示 一般都是 Netscape(网景)

navigator.appVersion

浏览器版本信息的字符串表示

例:5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36

操作系统版本:5.0 (Windows NT 10.0; Win64; x64)

浏览器内核版本:AppleWebKit/537.36 (KHTML, like Gecko)

浏览器版本:Chrome/111.0.0.0 Safari/537.36

navigator.cookieEnabled

如果启用cookie返回true,否则返回false

navigator.javaEnabled

如果启用java返回true,否则返回false

navigator.platform

浏览器所在计算机平台的字符串表示

判断用户是在windows、linux、unix

navigator.plugins

安装在浏览器中的插件数组

navigator.taintEnabled

如果启用了数据污点返回true,否则返回false

navigator.userAgent

用户代理头的字符串表示,替代navigator.appVersion ,更加详细

例:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36

navigator.connection

网络状态navigator.connection.effectiveType: “4g”

navigator.product

浏览器实际内核如Gecko

navigator.language

判断用户使用的浏览器语言,返回不同的语言页面,不过一般这个判断由后端来做,了解即可

screen对象

screen对象:用于获取某些关于用户屏幕的信息,也可用window.screen引用它

screen.width/height

屏幕的宽度与高度,以像素计 ,PC端用的少,多用于移动端适配

screen.availWidth/availHeight

窗口可以使用的屏幕的宽度和高度,以像素计

screen.colorDepth

用户表示颜色的位数,大多数系统采用32位

window.moveTo(0, 0); window.resizeTo(screen.availWidth, screen.availHeight);

填充用户的屏幕 (失效 : 用户安全和隐私协议)

18年用户隐私协议,禁用了音频视频自动播放、改变用户浏览器尺寸、改变用户浏览器位置、操作用户文件、读取用户文件等,不允许使用javaScript代替用户座选择,保护用户的自主选择权力和隐私,加强了用户的安全性

history 历史记录

History 对象包含用户(在浏览器窗口中)访问过的 URL。

length 返回浏览器历史列表中的 URL 数量/长度。

方法
back() 加载 history 列表中的前一个 URL。回退键
forward() 加载 history 列表中的下一个 URL。前进键
go() 加载 history 列表中的某个具体页面。
下面一行代码执行的操作与单击两次后退按钮执行的操作一样:

history.go(-2)

BOM 浏览器方法图表

在这里插入图片描述

close() ctrl + w

print() ctrl + p

moveBy()—resizeTo()已禁用,涉及用户隐私和安全

BOM和DOM的结构关系示意图

在这里插入图片描述

BOM导图

在这里插入图片描述

RegExp

什么是正则表达式

正则表达式是描述字符模式的对象。

  • 正则表达式用于对字符串模式匹配及检索替换,是对字符串执行模式匹配的强大工具。

  • StringRegExp都定义了使用正则表达式进行强大的模式匹配和文本检索与替换的函数。

  • 正则表达式主要用来验证客户端的输入数据。可以节约大量的服务器端的系统资源,并且提供更好的用户体验。


创建正则表达式

1、直接量(字面量)

语法:Reg = /pattern/modifiers;

var Reg = /box/gi;
2、new RegExp(实例化)

语法 Reg = new RegExp( pattern , modifiers );
pattern ,modifiers此时是字符串

var Reg = new RegExp(“box”,”gi”);
  • 何种方法创建都是一样的
  • pattern 模式 模板,要匹配的内容
  • modifiers 修饰符
3、不推荐用实例化的方法
var regExp = new RegExp('\d');
console.log(regExp.test('321312'))//false 因为实例化中的'\d'是字符串,要经过转义
console.log(/\d/.test('321312'))//ture

//实例化正确写法  麻烦所以不推荐
var regExp = new RegExp('\\d');
console.log(regExp.test('321312'))//ture

正则表达式用法及区别

1、String中正则表达式方法
方法描述
match(Reg)返回RegExp匹配的包含全部字符串的数组或null,注:断言部分不会匹配到
search(Reg)返回RegExp匹配字符串首次出现的位置
replace(Reg,newStr)newStr替换RegExp匹配结果,并返回新字符串
split(Reg)返回字符串按指定RegExp拆分的数组

使用

var str = 'hello';
var Reg = /e/i;
str.match(Reg);
console.log('你好,我好,大家好'.match(/(好)/));//['好', '好', index: 1, input: '你好,我好,大家好', groups: undefined]
console.log('你3好,我2好,大1家2好'.search(/[\d,]/g));//1
console.log('你3好,我2好,大1家2好'.split(/[\d,]/));//['你', '好', '我', '好', '大', '家', '好']

2、RegExp对象的方法

方 法

方法描述
exec()在字符串中执行匹配搜索,返回首次匹配结果数组,对同一个regExp对象的exec匹配是有记忆的,多次调用会迭代返回下一项符合匹配规则的项目
test()在字符串中测试模式匹配,返回truefalse

使用

var pattern = new RegExp(“box”,”gi”);
pattern.test(str);
pattern.exec(str);

//exec()多次调用会迭代
var regExp = /[\d,]/g;
console.log(regExp.exec('你3好,我2好,大1家2好'));//['3', index: 1, input: '你3好,我2好,大1家2好']
console.log(regExp.exec('你3好,我2好,大1家2好'));//[',', index: 3, input: '你3好,我2好,大1家2好']
console.log(regExp.exec('你3好,我2好,大1家2好'));//['2', index: 5, input: '你3好,我2好,大1家2好']
console.log(regExp.exec('你3好,我2好,大1家2好'));//[',', index: 7, input: '你3好,我2好,大1家2好']

注意区别正则方法和字符串方法使用避免混淆

正则方法:pattern.test(str); 方法的主体是正则表达式
字符串方法:str.match(pattern);方法的主体是字符串


修饰符

修饰符用于执行区分大小写全局匹配:

  • i 忽略大小写匹配
  • g 全局匹配,默认只匹配第一个元素,就不在进行匹配
  • m 执行多行匹配
var patt =  /pattern/i;         //忽略大小写匹配
var patt =  /pattern/g;         //全局匹配
var patt =  /pattern/m;         //执行多行匹配

var urlStr = 'www.baidu.com?wd=你好&q=1&type=2';
console.log(urlStr.match(/([^?&=]+)=([^?&=]*)/g));//['wd=你好', 'q=1', 'type=2']
console.log('helloWorld'.match(/helloworld/i));//['helloWorld', index: 0, input: 'helloWorld', groups: undefined]
console.log('helloWorld'.match(/l[^l]/g));// ['lo', 'ld']

pattern 模式


1、基本匹配

xxx ———————————匹配xxx字符

var Reg = /abc/;

x|y|z —————————-匹配xyz字符

var Reg = /abc|bac|cba/;

2、字符簇[]

[abc]———————————–匹配abc之中的任何一个字符

[^abc]———————————匹配abc字符的

[0-9]` ———————————匹配`0至9`之间的数字
`[a-z]` ———————————匹配`小写a至小写z`的字符
`[A-Z]` ———————————匹配`大写A至大写Z`的字符
匹配中文 `[\u4e00-\u9fa5]

还可以组合

 var Reg  = /hello [0-9a-zA-z]/;

元字符(转义字符)
.` —————————————–匹配`单个字符`,除了换行和行结束符
`\w`—————————————匹配`单词字符`,`数字`,`_`(下划线)  [A-Za-z0-9_]
`\W`—————————————匹配`非`(`单词字符`和`_`(下划线)) 除了小w之外的
`\d` —————————————匹配`数字` [0-9]  digit  
`\D` —————————————匹配`非数字` not digit	
`\s` —————————————匹配空白字符(`空格`)、制表符\t、换行符\n、换页符、 space
`\S` —————————————匹配`非空格`字符  not space
`\b` —————————————匹配`单词边界` ( 除了 (字)字母 数字_ 都算单词边界)
`\B` —————————————匹配`非单词边界`
`\n` —————————————匹配`换行符
`\r` —————————————匹配`回车符
`\t` —————————————匹配`制表符
`\f` —————————————匹配`换页符
`\v` —————————————匹配`垂直制表符
`[\b]` —————————————匹配 `回退符 backspace

特殊的转译字符. \ /

var reg = /\./;//匹配.
var reg = /\\/;//匹配\   
var reg = /\//;//匹配/  
//注:匹配字符串的'/',或'\'时,字符串也要转义
//如:console.log(/\\/.test('\\'));

4、量词

n?———————————匹配0个或一个n的字符串{0,1}
n*———————————匹配0个或多个字符串(任意个) {0,}
n+———————————匹配至少一个n字符串{1,}
n{X}——————————匹配包含X个n的序列的字符串
n{X,Y}————————–匹配包含至少X或至多Y个n的序列的字符串
n{x,}—————————-匹配至少X个n的序列字符串

^n———————————匹配以n开头的字符串
n$———————————匹配以n结尾的字符串


5、贪 婪 惰 性

贪婪: 尽可能多的匹配
惰性: 尽可能少的匹配
前提条件都是要匹配到内容

—— 贪 婪 ———— 惰 性 ——
++?
???
**?
{n}{n}?
{n,m}{n,m}?
{n,}{n,}?

6、子组(子表达式)

子组:使用()小括号,指定一个子表达式后,称之为分组

  • 捕获型
  • 非捕获型

1)、捕获型()

var str = 'abcdefg';
var reg = /(abc)d/;//匹配abcd
var val = reg.exec( str);
console.log( val );   //["abcd", "abc", index: 0, input: "abcdefg"]

var urlStr = 'www.baidu.com?wd=你好&q=1&type=2';
console.log(urlStr.match(/([^?&=]+)=([^?&=]*)/g));

索引0 为匹配的结果
索引1 为第一个子表达式 匹配结果
index :首次匹配成功的索引值,
input: 匹配目标


—— 字符 ——引用
(pattern)匹配pattern并捕获结果自动设置组号,是从1开始的正整数\num

引用是值的引用,匹配结果的引用不是匹配形式引用


**2)、非捕获型 (?😃 **

  • (?:pattern)
  • (?=pattern) 零宽度正向预言
Windows (?=2000) //匹配windows且后面跟2000

console.log('windows2000 windows10'.match(/windows(?=10)/));
//['windows', index: 12, input: 'windows2000 windows10', groups: undefined]

匹配 “Windows2000” 中的 “Windows”
不匹配 “Windows3.1” 中的 “Windows”。

  • (?!pattern) 零宽度负向预言
 Windows (?!2000)//匹配windows且后面非2000

console.log('windows2000 windows10'.match(/windows(?!10)/));
//['windows', index: 0, input: 'windows2000 windows10', groups: undefined]

匹配 “Windows3.1” 中的 “Windows”
不匹配 “Windows2000” 中的 “Windows”。

7、预言模式

1)、先行断言 x(?=y)

当x后面是y的时候匹配x 不是y的时候不匹配

console.log(/哈(?=啦)/.test('哈啦'));//true
console.log(/哈(?=啦)/.test('哈哈'));//false

console.log(/\d+(?=\.)/.test('199'));//false
console.log(/\d+(?=\.)/.test('199.'));//true

2)、后行断言 (?<=y)x

匹配x 当x前面是y的时候

//获取整数位大于0的数值的小数位  注:断言部分是不会匹配到的
console.log('10.13'.match(/(?<=^[1-9]\d*\.)(\d+)/));//['13', '13', index: 3, input: '10.13']

//整数位大于0的数值
//  ^[1-9]\d*(\.\d+)?$
console.log('10.13'.match(/^[1-9]\d*(\.\d+)?$/));// ['10.13', '.13', index: 0, input: '10.13']

3)、正向否定查找 x(?!y)

只有当x后面跟着不是y的时候匹配x

console.log(/海(?!牙)/.test('海贵'));//true

4)、反向否定查找 (?<!y)x

只有当x前面跟着不是y的时候匹配x

 console.log(/(?<!海)牙/.test('海牙'));//false

案例:

var regExp = /(abc)(?:\d+)/;
console.log('abc1abc2dfsd2abc5'.match(regExp));//abc1
console.log('abc123abcde'.replace(regExp, 'bcd'))//bcdabcde
8、regExp中的 $1 $n ()分组
console.log('abc 123'.replace(/([a-z]*).(\d*)/, '($1)\$2'));//(abc)123
console.log(RegExp.$1, RegExp.$2);//abc 123

正则案例

把URL的参数转化为对象

var url = 'www.baidu.com?wd=1&age=&name=海牙&sex=男';
/*
  {
    wd:1,
    age:19,
    name:海牙,
    sex:男
  }
*/
function unParams(urlStr) {
  var o = {};
  var regExp = /((\w*)=([a-zA-Z0-9\.\u4e00-\u9fa5]*)?)?/g;
  urlStr.replace(regExp, function (m, a, key, val) {//(整体匹配项,整体匹配项,$1,$2)
    if (key && val) { /
      o[key] = val;
    }
  })
  return o;
}
console.log(unParams(url)) 

表单输入实时验证

1、利用JSON映射

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>表单输入实时验证</title>
  <style>
    input {
      outline: none;
    }

    input.error {
      border: 1px solid red;
    }

    input.success {
      border: 1px solid green;
    }
  </style>
</head>
<body>
  请输入电话号码: <input name='phone' type="text" placeholder="请输入电话号码"> <br>
  请输入用户名: <input name='user' type="text" placeholder="请输入用户名"><br>
  请输入密码: <input name='pwd' type="password" placeholder="请输入密码"><br>
  请输入年龄: <input name='age' type="text" placeholder=" 请输入年龄">
  <script>
    var regMap = {
      'user': /^[\u4e00-\u9fa5]{2,6}$/,
      'pwd': /^\d{6}$/,
      'age': /[1-9][0-9]/,
      'phone': /^[1][3456789]\d{9}$/
    }
    document.oninput = function (e) {
      if (e.target.tagName.toLowerCase() === 'input') {
        e.target.className = 'error';
        if (regMap[e.target.name].test(e.target.value)) {
          e.target.className = 'success';
        }
      }
    }
  </script>
</body>
</html>

2、利用自定义标签

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

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>表单输入实时验证</title>
  <style>
    input {
      outline: none;
    }

    input.error {
      border: 1px solid red;
    }

    input.success {
      border: 1px solid green;
    }
  </style>
</head>
<body>
  请输入电话号码: <input name='phone' type="text" placeholder="请输入电话号码" data-reg="^[1][3456789]\d{9}$"> <br>
  请输入用户名: <input name='user' type="text" placeholder="请输入用户名" data-reg="^[\u4e00-\u9fa5]{2,6}$"><br>
  请输入密码: <input name='pwd' type="password" placeholder="请输入密码" data-reg="^\d{6}$"><br>
  请输入年龄: <input name='age' type="text" placeholder=" 请输入年龄" data-reg="[1-9][0-9]">
  <script>
    document.oninput = function (e) {
      e.target.className = 'error';
      var reg = new RegExp(e.target.dataset.reg);
      if (reg.test(e.target.value)) {
        e.target.className = 'success';
      }
    }
  </script>
</body>
</html>

格式化时间戳

function formatDate(format, data) { // ◆ 使用prototype定义原型方法
  var data = data || new Date();
  /*
    * eg:format="YYYY-MM-dd hh:mm:ss";
    */
  var o = { // ◆ 键值对形式的数组。只能通过加强的for循环来迭代取值
    "M+": data.getMonth() + 1, // month
    "d+": data.getDate(), // day
    "h+": data.getHours(), // hour
    "m+": data.getMinutes(), // minute
    "s+": data.getSeconds(), // second
    "q+": Math.floor((data.getMonth() + 3) / 3), // quarter (季度)
    "S": data.getMilliseconds()
    // millisecond (毫秒)
  }

  if (/(y+)/.test(format)) {
    format = format.replace(RegExp.$1, (data.getFullYear() + "").substr(4 - RegExp.$1.length));
      // ◆ RegExp.$1 : 取正则表达式中第一个分组匹配到的内容
  }

  for (var k in o) { // ◆ 加强的for循环。k为键值对数组o中的键,故o[k]为对应的值
    if (new RegExp("(" + k + ")").test(format)) {
      format = format.replace(RegExp.$1,
        RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length));// ◆ 这里的逻辑很好
    }
  }
  return format;
}

金额输入验证

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>金额输入修正</title>
</head>

<body>
  <input id="money" type="text" placeholder="请输入金额">
  <script>
    var oMoney = document.querySelector('#money');
    oMoney.oninput = function () {
      this.value = _regValue(this.value);
    }

    /*
    *@static _regValue 金额验证
    *@param {String} str 金额
    *@return {String} 修正后金额
    */
    function _regValue(value) {
      //非数字验证
      value = value.replace(/[^\d\.]+/g, '');

      //强制保留两位小数
      var doubleReg = /^(\d+)\.(\d\d).*$/;
      value = value.replace(doubleReg, '$1.$2');

      //开始非零验证
      value = value.replace(/^(0)(\d)$/, '$2');

      //小数点验
      value = value.replace(/^(\.+)$/, '0.');
      value = value.replace(/^(\d*)(\.+)$/, '$1.');

      //巨大数值
      value = value.replace(/^(\S{8})(\S+)$/, '$1');

      //只保留一个 .
      value = value.replace(/(\d+\.)(\d)(\.+)/, '$1$2');

      return value;
    }

  </script>
</body>
</html>

正则陷阱:lastIndex陷阱

正则在 g 全局修饰下,当循环列表项匹配的时候 lastIndex会干扰结果,当匹配上的时候 lastIndex匹配会从字符串最后一位+1开始计数,匹配不到的时候 lastIndex 重置为 0

var arr = [
  'a.jpg',
  'b.jpg',
  'c.jpg',
  'd.jpg'
];
var regExp = /(\S+)\.jpg/g;
for (var key in arr) {
  console.log(regExp.test(arr[key]), regExp.lastIndex);
}
//true 5
//false 0
//true 5
//false 0

/* 类似以下我们自己写的检索,下一次检索会从记录下的lastIndex开始,正则内部陷阱
  var str = '你好我是123你好吗';
  var idx = 0;
  while (str.indexOf('你', idx) != -1) {
    var x = str.indexOf('你', idx);
    idx = x + 1;
    console.log(x);
  }
*/

常用正则

火车车次

/^[GCDZTSPKXLY1-9]\d{1,4}$/

手机机身码(IMEI)

/^\d{15,17}$/

必须带端口号的网址(或ip)

/^((ht|f)tps?:\/\/)?[\w-]+(\.[\w-]+)+:\d{1,5}\/?$/

网址(url,支持端口和"?+参数"和"#+参数)

/^(((ht|f)tps?):\/\/)?[\w-]+(\.[\w-]+)+([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?$/

统一社会信用代码

/^[0-9A-HJ-NPQRTUWXY]{2}\d{6}[0-9A-HJ-NPQRTUWXY]{10}$/

迅雷链接

/^thunderx?:\/\/[a-zA-Z\d]+=$/

ed2k链接(宽松匹配)

/^ed2k:\/\/\|file\|.+\|\/$/

磁力链接(宽松匹配)

/^magnet:\?xt=urn:btih:[0-9a-fA-F]{40,}.*$/

子网掩码

/^(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])(?:\.(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])){3}$/

linux"隐藏文件"路径

/^\/(?:[^/]+\/)*\.[^/]*/

linux文件夹路径

/^\/(?:[^/]+\/)*$/

linux文件路径

/^\/(?:[^/]+\/)*[^/]+$/

window"文件夹"路径

/^[a-zA-Z]:\\(?:\w+\\?)*$/

window下"文件"路径

/^[a-zA-Z]:\\(?:\w+\\)*\w+\.\w+$/

股票代码(A股)

/^(s[hz]|S[HZ])(000[\d]{3}|002[\d]{3}|300[\d]{3}|600[\d]{3}|60[\d]{4})$/

大于等于0, 小于等于150, 支持小数位出现5, 如145.5, 用于判断考卷分数

/^150$|^(?:\d|[1-9]\d|1[0-4]\d)(?:.5)?$/

html注释

/^<!--[\s\S]*?-->$/

md5格式(32位)

/^([a-f\d]{32}|[A-F\d]{32})$/

版本号(version)格式必须为X.Y.Z

/^\d+(?:\.\d+){2}$/

视频(video)链接地址(视频格式可按需增删)

/^https?:\/\/(.+\/)+.+(\.(swf|avi|flv|mpg|rm|mov|wav|asf|3gp|mkv|rmvb|mp4))$/i

图片(image)链接地址(图片格式可按需增删)

/^https?:\/\/(.+\/)+.+(\.(gif|png|jpg|jpeg|webp|svg|psd|bmp|tif))$/i

24小时制时间(HH:mm:ss)

/^(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d$/

12小时制时间(hh:mm:ss)

/^(?:1[0-2]|0?[1-9]):[0-5]\d:[0-5]\d$/

base64格式

/^\s*data:(?:[a-z]+\/[a-z0-9-+.]+(?:;[a-z-]+=[a-z0-9-]+)?)?(?:;base64)?,([a-z0-9!$&',()*+;=\-._~:@/?%\s]*?)\s*$/i

数字/货币金额(支持负数、千分位分隔符)

/^-?\d+(,\d{3})*(\.\d{1,2})?$/

数字/货币金额 (只支持正数、不支持校验千分位分隔符)

/(?:^[1-9]([0-9]+)?(?:\.[0-9]{1,2})?$)|(?:^(?:0){1}$)|(?:^[0-9]\.[0-9](?:[0-9])?$)/

银行卡号(10到30位, 覆盖对公/私账户, 参考微信支付

/^[1-9]\d{9,29}$/

中文姓名

/^(?:[\u4e00-\u9fa5·]{2,16})$/

英文姓名

/(^[a-zA-Z]{1}[a-zA-Z\s]{0,20}[a-zA-Z]{1}$)/

车牌号(新能源)

/[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领 A-Z]{1}[A-HJ-NP-Z]{1}(([0-9]{5}[DF])|([DF][A-HJ-NP-Z0-9][0-9]{4}))$/

车牌号(非新能源)

/^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领 A-Z]{1}[A-HJ-NP-Z]{1}[A-Z0-9]{4}[A-Z0-9挂学警港澳]{1}$/

车牌号(新能源+非新能源)

/^(?:[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领 A-Z]{1}[A-HJ-NP-Z]{1}(?:(?:[0-9]{5}[DF])|(?:[DF](?:[A-HJ-NP-Z0-9])[0-9]{4})))|(?:[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领 A-Z]{1}[A-Z]{1}[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9 挂学警港澳]{1})$/

手机号(mobile phone)中国(严谨), 根据工信部2019年最新公布的手机号段

/^(?:(?:\+|00)86)?1(?:(?:3[\d])|(?:4[5-7|9])|(?:5[0-3|5-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[1|8|9]))\d{8}$/

手机号(mobile phone)中国(宽松), 只要是13,14,15,16,17,18,19开头即可

/^(?:(?:\+|00)86)?1[3-9]\d{9}$/

手机号(mobile phone)中国(最宽松), 只要是1开头即可, 如果你的手机号是用来接收短信, 优先建议选择这一条

/^(?:(?:\+|00)86)?1\d{10}$/

date(日期)

/^\d{4}(-)(1[0-2]|0?\d)\1([0-2]\d|\d|30|31)$/

email(邮箱)

/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/

座机(tel phone)电话(国内),如: 0341-86091234

/^\d{3}-\d{8}$|^\d{4}-\d{7,8}$/

身份证号(1代,15位数字)

/^[1-9]\d{7}(?:0\d|10|11|12)(?:0[1-9]|[1-2][\d]|30|31)\d{3}$/ 

身份证号(2代,18位数字),最后一位是校验位,可能为数字或字符X

/^[1-9]\d{5}(?:18|19|20)\d{2}(?:0[1-9]|10|11|12)(?:0[1-9]|[1-2]\d|30|31)\d{3}[\dXx]$/

身份证号, 支持1/2代(15位/18位数字)

/(^\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$)|(^\d{6}(18|19|20)\d{2}(0[1-9]|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)$)/
 

护照(包含香港、澳门)

/(^[EeKkGgDdSsPpHh]\d{8}$)|(^(([Ee][a-fA-F])|([DdSsPp][Ee])|([Kk][Jj])|([Mm][Aa])|(1[45]))\d{7}$)/
 

帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线组合

/^[a-zA-Z]\w{4,15}$/
 

中文/汉字

/^(?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0])+$/
 

小数

/^\d+\.\d+$/
 

数字

/^\d{1,}$/
 

html标签(宽松匹配)

/<(\w+)[^>]*>(.*?<\/\1>)?/
 

qq号格式正确

/^[1-9][0-9]{4,10}$/
 

数字和字母组成

/^[A-Za-z0-9]+$/
 

英文字母

/^[a-zA-Z]+$/
 

小写英文字母组成

/^[a-z]+$/
 

大写英文字母

/^[A-Z]+$/
 

密码强度校验,最少6位,包括至少1个大写字母,1个小写字母,1个数字,1个特殊字符

/^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Z])(?=\S*[a-z])(?=\S*[!@#$%^&*? ])\S*$/
 

用户名校验,4到16位(字母,数字,下划线,减号)

/^[a-zA-Z0-9_-]{4,16}$/
 

ip-v4

/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
 

ip-v6

/^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i
 

16进制颜色

/^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/
 

微信号(wx),6至20位,以字母开头,字母,数字,减号,下划线

/^[a-zA-Z][-_a-zA-Z0-9]{5,19}$/
 

邮政编码(中国)

/^(0[1-7]|1[0-356]|2[0-7]|3[0-6]|4[0-7]|5[1-7]|6[1-7]|7[0-5]|8[013-6])\d{4}$/
 

中文和数字

/^((?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0])|(\d))+$/
 

不能包含字母

/^[^A-Za-z]*$/
 

java包名

/^([a-zA-Z_][a-zA-Z0-9_]*)+([.][a-zA-Z_][a-zA-Z0-9_]*)+$/
 

mac地址

/^((([a-f0-9]{2}:){5})|(([a-f0-9]{2}-){5}))[a-f0-9]{2}$/i
 

匹配连续重复的字符

/(.)\1+/

cookie

cookie : 存储数据,当用户访问了某个网站(网页)的时候,我们就可以通过cookie来向访问者电脑上存储数据

1.不同的浏览器存放的cookie位置不一样,也是不能通用的
2.cookie的存储是以域名形式进行区分的
3.cookie的数据可以设置名字的
4.一个域名下存放的cookie的个数是有限制的,不同的浏览器存放的个数不一样
5.每个cookie存放的内容大小也是有限制的,不同的浏览器存放大小不一样

访问cookie

要在服务器环境下

我们通过document.cookie来获取当前网站下的cookie的时候,得到的字符串形式的值,他包含了当前网站下所有的cookie。他会把所有的cookie通过一个分号+空格的形式串联起来

consol.log( document.cookie );
存储cookie
document.cookie = '数据名=值';
设置cookie过期时间

cookie默认是临时存储的,当浏览器关闭进程的时候自动销毁 ,如果我们想长时间存放一个cookie。需要在设置这个cookie的时候同时给他设置一个过期的时间

  • 过期时间必须是一个日期对象转换成的字符串(时间戳.toGMTString()

document.cookie = ‘数据名=值; expires=过期时间’;

var oDate = new Date();
oDate.setDate( oDate.getDate() + 5);

oDate.toGMTString();//转换为日期字符串
document.cookie='age=20; expires='+oDate;

/*-- document.cookie='sex=man\n你好'; */
//转码
var content= encodeURI('man\n你好');
document.cookie='sex='+content+';expires='+oDate;

要找到对应的数据值,可以使用多种方式

cookie封装
  • 设置cookie封装
/* 
    setCookie 设置cookie
    参数1 json{key=value}
    参数2 有效期 单位ms  1000ms Infinity(正无穷)
*/
function setCookie(json, t) {
  var d = new Date(); //当前事件
  d.setTime(d.getTime() + t);
  var time = d.toUTCString();
  if (t === Infinity) {
    time = 'Fri, 31 Dec 9999 13:32:02 GMT';
  }
  for (var key in json) {
    document.cookie = `${key}=${encodeURI(json[key])}; expires=${time}`;//如果值是汉字要encodeURI转码,方便与数据的传输,不然有可能会乱码
  }
}

setCookie({
    name:'hello',
    sex:'man',
    love:'逛街',
    work:'future'
},5);
  • 获取cookie封装
 function getCookie() {
     var cookie = document.cookie;
     var cookieArr = cookie.match(/[^=;\s]+=([^=;]+)(?:;?)/g);
     var argData = {}
     for (var key of arguments) {
         argData[key] = 1;
     }
     return cookieArr.reduce(function (acc, curr) {
         var tempStr = curr.replace(';', '');
         var tempArr = tempStr.split('=');
         if (tempArr[0] && argData[tempArr[0]]) {
             acc[tempArr[0]] = tempArr[1]
         }
         return acc;
     }, {});
getCookie('name','age')
  • 移除cookie
function removeCookie(){
    for(key in arguments){
        var json ={};
        json[arguments[key]]=null;
        setCookie(json,-1);//设置会过期时间到期就会自动删除
    }
}
removeCookie('name');
  • 14
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值