页面加载事件
<script>
window.addEventListener('load', function() {}) // 页面加载完执行
// DOM加载完成,不包括图片、样式等,ie9以上支持
window.addEventListener('DOMContentLoaded', function() {})
</script>
窗口大小事件
window.addEventListener('resize', function() {
console.log(window.innerWidth)
})
绑定事件
<audio id="aud" controls></audio>
<button onclick="go()">点击</button>
<script>
function go() {
console.log(aud);
}
</script>
xxx.onclick = function (){} // 添加事件
xxx.onclick = null // 移除事件
<xxx onclick=""></xxx>
xxx.addEventListener('click', function(){}, false)
xxx.removeEventListener('click', function(){})
// 默认是false,不阻止冒泡,IE8及以下不支持
xxx.attachEvent('onclick', function(){})
xxx.detachEvent('onclick', function(){})
事件冒泡:由最具体的元素接收,并往上传播
事件捕获:由最不具体的元素接收,并往下传播
DOM事件流:事件捕获 -> 目标阶段 -> 事件冒泡
右键菜单
// 禁止右击菜单
document.addEventListener('contextmenu', function(e) {
e.preventDefault()
})
禁止选中文字
document.addEventListener('selectstart', function(e) {
e.preventDefault()
})
鼠标在页面的位置
e.clientX、e.clentY // 鼠标在可视区域的位置(不受滚动条的影响)
e.pageX、e.pageY // 鼠标在文档的位置(受滚动条的影响)
e.screenX、e.screenY // 用得少,鼠标在屏幕的位置
元素偏移量offset
offsetTop、offsetLeft 距离父元素的位置(父元素需要定位,不然一直往上找,直到body)
offsetWidth、offsetHeight 元素自身的大小(border+padding+content)
offsetParent 父元素的DOM节点(带有定位的父元素,不然一直往上找,直到body)
client系列
clientTop、clientLeft 获取自身边框的大小
clientWidth、clientHeight 元素自身的大小(padding+content)
鼠标在元素内的位置
e.pageX - this.offsetLeft
e.pageY - this.offsetTop
键盘事件
keyup // 按下松开后执行
keydown // 按下执行,能识别所有键
keypress // 按下执行,不能识别功能键,比如ctrl、shift、左右箭头键
执行顺序:keydown、keypress、keyup
阻止事件冒泡
function stopBubble(e) {
if (e.stopPropagation) {
e.stopPropagation()
} else {
window.event.cancelBubble = true;
}
}
阻止默认事件
node.addEventListener('click', stopDefault)
function stopDefault(e) {
e.preventDefault();
window.event.returnValue = false; // ie678
}
node.onclick = function() {
return false; // 只限于传统方式
}
事件委托
当所有子元素都需要绑定相同的事件的时候,可以把事件绑定在父元素上,这就是事件委托 优点有:
- 绑定在父元素上只需要绑定一次,节省性能
- 子元素不需要每个都去绑定同一事件
- 如果后续又有新的子元素添加,由于事件委托的原因,自动接收到父元素的事件监听
Set、Map
set
set的用法:常用于数组去重,Set里不会有重复值
let set = new Set(['a','b','c'])
set.add('xxx') // 添加
set.clear() // 清空
set.delete('xxx') // 删除
set.size // 查看长度
set.has('xxx') // 查看是否有该属性
set.forEach() // 循环
set.keys()
set.values()
set.entries()
注意:引用类型和NaN(会去重)
// 两个对象都是不同的指针,所以没法去重
const set1 = new Set([1, {name: 'xxx'}, 2, {name: 'xxx'}])
console.log(set1) // Set(4) { 1, { name: 'xxx' }, 2, { name: 'xxx' } }
// 如果是两个对象是同一指针,则能去重
const obj = {name: '林三心'}
const set2 = new Set([1, obj, 2, obj])
console.log(set2) // Set(3) { 1, { name: '林三心' }, 2 }
map
map的用法:Map对比object最大的好处就是,key不受类型限制
const map = new Map() // 定义map
map.set('xxx', 'yyy') // 添加键值对使用 set(key, value)
map.delete('xxx') // // 删除
map.has('xxx') // 查看是否含有某个key
map1.get('xxx') // 获取key的value
map.size // 长度
map.clear()
map.keys()
map.values()
map.entries()
map.forEach()
// 把map转为对象
Object.fromEntries(map)
Object.entries是把对象转成数组,而Object.fromEntries则是把键值对转为对象
includes、indexOf
includes可以检测`NaN`,indexOf不能检测`NaN`
includes内部使用了`Number.isNaN`对`NaN`进行了匹配
获取DOM元素
document可以换成Dom节点,那么就是搜索范围就是Dom节点的子节点内
| 方法 | 描述 | 返回类型 |
|---|---|---|
| document.getElementById() | id | Dom对象 |
| document.getElementsByTagName() | 标签名 | 数组 |
| document.getElementsByClassName() | class(不支持IE8及以下) | 数组 |
| document.getElementsByName(name) | 标签的属性name | 数组 |
| document.querySelector(选择器) | 选择器获取dom | 第一个dom对象 |
| document.querySelectorAll(选择器) | 选择器获取dom | 数组 |
| document.body | 获取body | dom对象 |
| document.documentElement | 获取html | dom对象 |
| document.all | 获取所有元素 | dom对象 |
操作DOM元素有哪些方法
| 标题 | 描述 |
|---|---|
| createElement | 创建一个标签节点 |
| createTextNode | 创建一个文本节点 |
| cloneNode(deep) | 复制一个节点,连同属性与值都复制,deep为true时,连同后代节点一起复制,不传或者传false,则只复制当前节点 |
| createDocumentFragment | 创建一个文档碎片节点 |
| appendChild | 追加子元素 |
| removeChild | 删除子元素 |
| insertBefore(newN,oldN) | 将元素插入前面 |
| replaceChild(newN,oldN) | 替换子元素 |
| getAttribute | 获取节点的属性 |
| dataset | 获取节点的自定义(data-)属性(兼容性没有上面的好) |
| createAttribute | 创建属性 |
| setAttribute | 设置节点属性 |
| romoveAttribute | 删除节点属性 |
| element.attributes | 将属性生成类数组对象 |
| firstElementChild | 获取第一个子元素节点(不支持IE8及以下浏览器) |
| lastElementChild | 获取最后一个子元素节点(不支持IE8及以下浏览器) |
| firstChild | 获取第一个子节点 |
| lastChild | 获取最后一个子节点 |
| childNodes | 获取子节点(包括文本节点,IE8及以下没有包括文本节点) |
| children | 获取子元素节点(获取子节点,推荐使用) |
| parentNode | 获取父节点 |
| parentElement | 获取父元素节点 |
| previousSibling | 获取当前节点的前一个节点 |
| nextSibling | 获取当前节点的后一个节点 |
| previousElementSibling | 获取当前节点的前一个元素节点(不支持IE8及以下浏览器) |
| nextElementSibling | 获取当前节点的后一个元素节点(不支持IE8及以下浏览器) |
| currentStyle.width | 获取样式(只有IE支持) |
| getComputedStyle(node, null).width | 获取样式(不支持IE8及以下浏览器) |
| style.width | 设置样式 |
| className | 设置类名 |
| className+ | 添加类名 |
nodeType
元素节点:1 属性节点:2 文本节点:3 注释节点:8
轮播图
<!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" />
<link rel="stylesheet" href="//at.alicdn.com/t/font_3352572_lggtl00ced8.css" />
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
#parent {
position: relative;
width: 800px;
height: 300px;
overflow: hidden;
margin: 20px auto;
}
ul {
list-style: none;
display: flex;
padding: 0;
position: absolute;
box-sizing: border-box;
}
ul>li {
width: 800px;
height: 300px;
}
ul>li>img {
width: 100%;
height: 100%;
}
ol {
list-style: none;
display: flex;
position: absolute;
bottom: 10px;
left: 50%;
padding: 0;
transform: translate(-50%);
}
ol>li {
border: 1px solid pink;
width: 14px;
height: 14px;
border-radius: 50%;
margin: 2px;
cursor: pointer;
}
.bgdc {
background-color: pink;
}
#left,
#right {
display: none;
position: absolute;
top: 50%;
z-index: 10;
font-size: 30px;
transform: translate(0, -50%);
cursor: pointer;
color: green;
}
#left {
left: 10px;
}
#right {
right: 10px;
}
#animation {
position: relative;
width: 200px;
height: 200px;
border: 1px solid pink;
}
</style>
</head>
<body>
<div id="parent">
<ul id="ul">
<li>
<img src="https://img2.baidu.com/it/u=2579945444,3600776594&fm=253&fmt=auto&app=138&f=JPG?w=1000&h=450" />
</li>
<li>
<img src="https://img1.baidu.com/it/u=1646921750,2843986497&fm=253&fmt=auto&app=138&f=JPEG?w=658&h=188" />
</li>
<li>
<img src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.zcool.cn%2Fcommunity%2F014a4055e6bc4032f875a13257daa2.jpg&refer=http%3A%2F%2Fimg.zcool.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1653294775&t=1af8964bd76099aadad8a31f8d3b1855"
/>
</li>
<li>
<img src="https://img0.baidu.com/it/u=3116932885,1264952736&fm=253&fmt=auto&app=138&f=JPEG?w=1846&h=500" />
</li>
<li>
<img src="https://img0.baidu.com/it/u=2527001186,1884851821&fm=253&fmt=auto&app=138&f=JPG?w=1295&h=500" />
</li>
</ul>
<ol id="ol"></ol>
<div id="left" class="iconfont icon-left-circle-fill"></div>
<div id="right" class="iconfont icon-right-circle-fill"></div>
</div>
<script>
window.onload = function() {
var parent = document.getElementById("parent");
var ul = document.getElementById("ul");
var ol = document.getElementById("ol");
var left = document.getElementById("left");
var right = document.getElementById("right");
parent.onmouseenter = function() {
left.style.display = "block";
right.style.display = "block";
clearInterval(timer);
timer = null;
};
parent.onmouseleave = function() {
left.style.display = "none";
right.style.display = "none";
timer = setInterval(() => {
right.click();
}, 2000);
};
for (var i = 0; i < ul.children.length; i++) {
var element = document.createElement("li");
element.setAttribute("index", i);
ol.appendChild(element);
element.onclick = function() {
for (var m = 0; m < ol.children.length; m++) {
ol.children[m].className = "";
}
this.className += "bgdc";
circle = num = this.getAttribute("index");
// ul.style.left = -this.getAttribute("index") * parent.offsetWidth + "px";
animationCursom(
ul, -this.getAttribute("index") * parent.offsetWidth
);
};
}
ol.children[0].className += "bgdc";
var num = 0;
var circle = 0;
ul.appendChild(ul.children[0].cloneNode(true));
left.onclick = function() {
if (num === 0) {
num = ul.children.length - 1;
ul.style.left = -num * parent.offsetWidth + "px";
}
num--;
// ul.style.left = -num * parent.offsetWidth + "px";
animationCursom(ul, -num * parent.offsetWidth);
circle--;
if (circle < 0) {
circle = ol.children.length - 1;
}
for (var m = 0; m < ol.children.length; m++) {
ol.children[m].className = "";
}
ol.children[circle].className += "bgdc";
};
right.onclick = function() {
if (num === ul.children.length - 1) {
ul.style.left = 0;
num = 0;
}
num++;
// ul.style.left = -num * parent.offsetWidth + "px";
animationCursom(ul, -num * parent.offsetWidth);
circle++;
if (circle === ol.children.length) {
circle = 0;
}
for (var m = 0; m < ol.children.length; m++) {
ol.children[m].className = "";
}
ol.children[circle].className += "bgdc";
};
var timer = setInterval(() => {
right.click();
}, 2000);
function animationCursom(node, target, callback) {
clearInterval(node.timer);
node.timer = null;
node.timer = setInterval(() => {
if (node.offsetLeft === target) {
clearInterval(node.timer);
node.timer = null;
callback && callback();
}
// 缓动动画:(目标位置-现在的位置)/10
var step = (target - node.offsetLeft) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
node.style.left = node.offsetLeft + step + "px";
}, 10);
}
};
</script>
</body>
</html>
放大镜
<!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>
.parent {
width: 300px;
height: 300px;
border: 1px solid gray;
position: relative;
cursor: move;
}
.parent>img {
width: 100%;
height: 100%;
display: block;
}
.select {
display: none;
width: 100px;
height: 100px;
/* background-color: rgba(204, 204, 33, 0.5); */
background-color: yellow;
opacity: 0.5;
filter: Alpha(opacity=50);
position: absolute;
z-index: 1;
top: 0;
}
.show {
width: 300px;
height: 300px;
border: 1px solid gray;
position: absolute;
left: 102%;
top: 0px;
z-index: 99;
display: none;
overflow: hidden;
}
.show>img {
position: relative;
width: auto;
height: auto;
}
</style>
</head>
<body>
<div id="parent" class="parent">
<img src="https://img1.baidu.com/it/u=1187162286,25291177&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=662" alt="" />
<div id="select" class="select"></div>
<div id="show" class="show">
<img src="" alt="" />
</div>
</div>
<script>
window.onload = function() {
var parent = document.getElementById("parent");
var select = document.getElementById("select");
var show = document.getElementById("show");
parent.onmouseover = function() {
select.style.display = "block";
show.style.display = "block";
show.children[0].src = parent.children[0].src;
};
parent.onmouseout = function() {
select.style.display = "none";
show.style.display = "none";
};
parent.onmousemove = function(event) {
event = event || window.event;
var x = event.clientX - this.offsetLeft - select.clientWidth / 2;
var y = event.clientY - this.offsetTop - select.clientHeight / 2;
var maxX = this.clientWidth - select.clientWidth;
var maxY = this.clientHeight - select.clientHeight;
if (x <= 0) {
x = 0;
} else if (x >= maxX) {
x = maxX;
}
if (y <= 0) {
y = 0;
} else if (y >= maxY) {
y = maxY;
}
/**
* (遮罩移动距离/遮罩最大移动距离)*放大图片最大移动距离 = 放大图片移动距离
*/
select.style.left = x + "px";
select.style.top = y + "px";
show.children[0].style.left =
(x / maxX) * (show.clientWidth - show.children[0].clientWidth) +
"px";
show.children[0].style.top =
(y / maxY) * (show.clientHeight - show.children[0].clientHeight) +
"px";
};
};
</script>
</body>
</html>
拖拽功能
var box = document.getElementById("box");
drag(box)
// 封装的方法,只需要传dom节点
function drag(node) {
node.onmousedown = function (event) {
event = event || window.event // 兼容ie8及以下
var x = event.clientX - this.offsetLeft;
var y = event.clientY - this.offsetTop;
this.setCapture && this.setCapture(); // 兼容ie8及以下
document.onmousemove = function (event) {
event = event || window.event // 兼容ie8及以下
node.style.left = event.clientX - x + 'px';
node.style.top = event.clientY - y + 'px';
}
document.onmouseup = function () {
document.onmousemove = null;
document.onmouseup = null;
node.releaseCapture && node.releaseCapture() // 兼容ie8及以下
}
return false
};
}
全局作用域、局部作用域、块级作用域、作用域链
<script>
var str = 'lxh' // 全局变量
function fun() {
var num = 25 // 局部变量
}
if() {
let obj = {} // 块级作用域{}
}
function lxh() {
var arr = []
function hj() {
console.log(arr) // 通过作用域链获取arr
}
}
</script>
创建对象object
let obj1 = {} // 通过字面量创建对象
let obj2 = new Object()
转数据类型
转string字符串
变量.toString()
String(变量) // 强制转换
变量 + '' //隐式转换,常用
转number数字
Number(变量) // 强制转换
parseInt(变量) // 转为整型
parseFloat(变量) // 转为浮点类型
变量 - 0 (隐式转换-*/,常用)
转为boolean布尔
Boolean()
false:''、0、NAN、null、undefined
判断数据类型
typeof
用法:typeof obj 或 typeof(obj)
返回的是小写的字符串类型
用于判断原始类型比较方便
| 原始类型 | 检查类型 |
|---|---|
| String | string |
| Number | number |
| Boolean | boolean |
| undefined | undefined |
| null | object |
| Array | object |
| Object | object |
| Function | function |
| BigInt | bigint |
| Symbol | symbol |
instanceof
用法:str instanceof String
返回一个布尔值
只能判断对象的具体类型,不能判断原始类型
通过检查对象的原型链上是否有类型的原型
constructor
用法:fun.constructor === Function
返回一个布尔值
是原型prototype上的一个属性
null和undefined没有constructor,所以这种方法不能判断
constructor在类继承的时候会出错
Object.prototype.toString.call()
这个方式最靠谱
Object.prototype.toString.call('') ; // [object String]
Object.prototype.toString.call(1) ; // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(Symbol()); //[object Symbol]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(new Function()) ; // [object Function]
Object.prototype.toString.call(new Date()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(new RegExp()) ; // [object RegExp]
Object.prototype.toString.call(new Error()) ; // [object Error]
Object.prototype.toString.call(document) ; // [object HTMLDocument]
Object.prototype.toString.call(window) ; //[object global] window 是全局对象 global 的引用
浅拷贝、深拷贝
深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型。 浅拷贝是内存指向同一地址。深拷贝是内层指向不同地址。
实现浅拷贝
Object.assign()
只有一层是深拷贝,第二层以后就是浅拷贝
let obj = {xxx: {xxx: 'xxx'} }
let objNew = Object.assign({}, obj)
Array.prototype.concat()、Arrar.prototype.slice()
concat、slice不会改变原对象,返回一个新对象
let arr = [1, 2, {xxx: 'xxx' } ]
let arrNew = arr.concat()
let arrNew = arr.slice()
手写
function cloneShallow(obj) {
if(typeof obj === 'object' && obj !== null) {
return Array.isArray(obj) ? [...obj] : {...obj}
} else {
return obj;
}
}
实现深拷贝
JSON.parse(JSON.stringify())
缺点:对象里的函数、undefined,实现不了深拷贝
let arr = [1, {xxx: 'xxx' }, function() {}]
let arrNew = JSON.parse(JSON.stringify(arr))
lodash函数库
import _ from 'lodash'
let obj = {
xxx: 'xxx'
yyy: {
zzz: {
kkk: 'kkk'
}
}
}
let objNew = _.cloneDeep(obj)
手写递归
hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(也就是,是否有指定的键)。
function deepClone(obj, map = new Map()) {
if (typeof obj !== "object" || obj === null) return obj;
if (map.get(obj)) return map.get(obj);
let result = {};
if (Object.prototype.toString.call(obj) === "[object Array]") result = [];
map.set(obj, result);
for (const key in obj) {
if (Object.hasOwnProperty.call(obj, key)) {
result[key] = deepClone(obj[key], map);
}
}
return result;
}
防抖、节流
防抖常用在:搜索框
function debounce(fun, time = 1000) {
let timer = null;
return function() {
timer && clearTimeout(timer);
timer = setTimeout(() => {
fun.apply(this, [...arguments]);
timer = null;
}, time)
}
}
节流常用在:按钮点击
function throttle(fun, time = 2000) {
let timer = null;
return function () {
if(timer) return;
timer = setTimeout(() => {
fun.apply(this, [...arguments]);
timer = null;
}, time)
};
}
运算符的优先级
常见的一下判断[]、null、undefined等
undefined === undefined // true
undefined >= undefined // false 隐式转换为NaN >= NaN NaN不等于NaN,所以为false
null === null // true
null >= null // true 隐式转换为0 >= 0 所以为true
Boolean([]) // true
Boolean(![]) // false
[] == [] // false
[] === [] // false
[] == ![] // true
!优先级高于==,[]不是假值,右边为布尔值,false先转数字0
左边为对象,[]调用toString为'',''转换为0
最终为0 == 0
0.1 + 0.2 === 0.3 // false
js中小数是浮点数,需转二进制进行运算,有些小数无法用二进制表示,所以只能取近似值,所以造成误差
解决方法:
先变成整数运算,然后再变回小数
toFixed() 性能不好,不推荐
// 0.5.toFixed(1) 为0.5 会四舍五入,括号里是保留小数位数,注意不能处理整数,不然报错
数组
判断是否为数组:Array.isArray(xxx)
✅改变原数组、❌不改变原数组
- pop 删除数组最后一项,返回被删除项 ✅
- push 在数组后添加元素,返回数组长度 ✅
- shift 删除数组第一项,并返回被删除项 ✅
- unshift 数组开头添加元素,返回新数组长度 ✅
- splice 截取数组,返回被截取的区间 ✅
- sort 排序一个数组,返回修改后的数组 ✅
- reserve 反转一个数组,返回修改后的数组 ✅
- flat 数组扁平化 ❌
- concat 连接数组 ❌
- slice 截取数组,返回被截取的区间 ❌
- some 数组有符合规则的一项就返回true ❌
- every 数组每一项都符合规则才返回true ❌
- reduce 接收上一个return和数组的下一项 ❌
- map 操作数组每一项并返回一个新数组,不能结束循环 ❌
- filter 对数组所有项进行判断,返回符合规则的新数组 ❌
- join 将一个数组所有元素连接成字符串并返回这个字符串 ❌
- forEach 遍历数组,没有返回值,结束本次循环return,跳出循环体抛出一个错误 ❌
升序 [].sort((a, b) => a-b)
降序 [].sort((a, b) => b-a)
随机 [].sort(() => Math.random - 0.5)
最大值 [].reduce((a, b) => a > b ? a : b)
最小值 [].reduce((a, b) => a < b ? a : b)
navigator
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)) {
window.location.href = ""; //手机
} else {
window.location.href = ""; //电脑
}
window.navigator.onLine // 是否有网,包括内网,返回布尔值
window常用API
- history 操纵浏览器的记录
back() forward() go(-1) length pushState() replaceState()
- location操作刷新按钮和地址栏
search // 获取参数
href // 获取url、跳转
assign() // 跳转,可以后退
replace() // 跳转,不能后退
reload() // 刷新,相当于f5
reload(true) // 强制刷新,相当于ctrl+f5
滚动条和坐标
e.pageX、e.pageY是页面的坐标
e.pageX - this.offsetLeft、e.pageY - this.offsetTop 在盒子里的坐标
元素被卷去的头部element.scrollTop、页面被卷去的头部window.pageYOffset(IE8及以下不支持)
网页可见区域宽:document.body.clientWidth (不包括边线的宽border)
网页可见区域高:document.body.clientHeight(不包括边线的高border)
网页可见区域宽:document.body.offsetWidth (包括边线的宽border)
网页可见区域高:document.body.offsetHeight(包括边线的高border)
网页正文全文宽:document.body.scrollWidth
网页正文全文高:document.body.scrollHeight
网页被卷去的高:document.body.scrollTop
网页被卷去的左:document.body.scrollLeft
网页正文部分上:window.screenTop
网页正文部分左:window.screenLeft
屏幕的高:window.screen.height
屏幕的宽:window.screen.width
屏幕可用工作区高度:window.screen.availHeight(不包括Windows任务栏)
屏幕可用工作区宽度:window.screen.availWidth (不包括Windows任务栏)
浏览器的宽:window.outerWidth (包括导航栏、地址栏等)
浏览器的高:window.outerHeight(包括导航栏、地址栏等)
当前窗口的宽:window.innerWidth (不包括导航栏、地址栏等)
当前窗口的高:window.innerHeight(不包括导航栏、地址栏等)
滚动到具体位置:
document.documentElement.scrollTop = 300; // 不需要加单位
document.documentElement.scrollTo(0, 300); // (水平, 垂直)
document.getElementById("ID").scrollIntoView();
/*
scrollIntoView(true) ==> {block:"start",inline:"nearest"}
scrollIntoView(false) ==> {block:"end",inline:"nearest"}
滚动到具体某元素
`behavior`:定义动画过渡效果, "auto"或 "smooth(平滑滚动)" 之一。默认为 "auto"。
- `block`:定义垂直方向的对齐, "start", "center", "end", 或 "nearest"之一。默认为 "start"。
- `inline`:定义水平方向的对齐, "start", "center", "end", 或 "nearest"之一。默认为 "nearest"。
*/
获取元素距离父元素顶部和左边距离
`offsetTop offsetLeft`元素相对父元素上边、左边的距离
const ID = document.getElementById("id")
ID.parentNode.scrollTop = ID.offsetTop;
最底部:
parent.scrollTop = parent.scrollHeight - parent.clientHeight;
倒计时
function supplement(num) {
return num.toString().padStart(2, 0);
}
function countDown(overdueTime = "2022-4-24 0:0:0") {
const time = (+new Date(overdueTime) - +new Date()) / 1000;
const day = supplement(parseInt(time / 60 / 60 / 24)) + "天";
const hours = supplement(parseInt((time / 60 / 60) % 24)) + "小时";
const minutes = supplement(parseInt((time / 60) % 60)) + "分钟";
const second = supplement(parseInt(time % 60)) + "秒";
return day + hours + minutes + second;
}
let timer = setInterval(() => {
const date = countDown();
console.log(date);
if (date === "00天00小时00分钟00秒") {
clearInterval(timer);
timer = null;
}
}, 1000);
Math 常用方法
- Math.max(...arr) 取arr中的最大值
- Math.min(...arr) 取arr中的最小值
- Math.ceil(小数) 小数向上取整
- Math.floor(小数) 小数向下取整
- Math.round(小数) 小数四舍五入
- Math.sqrt(num) 对num进行开方
- Math.pow(num, m) 对num取m次幂
- Math.random() 取0(包括)到1(不包括)的随机数
Math.round(-222.5) // -222
Math.round(-222.51) // -223
Math.round(num * 100) / 100 // 保留两位小数
// 得到一个两数之间的随机整数,包括两个数在内
Math.floor(Math.random()*(max - min + 1)) + min
promise
function fun() {
return new Promise((resolve, reject) => {
setTimeout(() => {}, 1000)
})
}
fun().then(res => {}).catch(err => {}).finally(() => {})
Promise.all([]) 全部成功才会返回成功
Promise.rase([]) 返回最快的成功或者失败
Promise.any([]) 返回最快的成功,全部失败则会报错
Promise.allSettled([]) 返回数组
?.和??
obj && obj.xxx obj?.xxx
arr && arr[1] arr?.[1]
fun && fun() fun?.()
const a = 0 || 'xxx' // xxx
const b = '' || 'xxx' // xxx
const c = false || 'xxx' // xxx
const d = undefined || 'xxx' // xxx
const e = null || 'xxx' // xxx
?? 只有null和undefined才算假值
const d = undefined ?? 'xxx' // xxx
const e = null ?? 'xxx' // xxx
||= 和 &&=
- 或等于(||=) a ||= b 等同于 a || (a = b);
- 且等于(&&=) a &&= b 等同于 a && (a = b);
浏览器从输入url地址到看到界面的过程
- 通过DNS域名解析
- 建立TCP连接(三次握手)
- 发送HTTP请求
- 服务器处理请求并返回HTTP报文
- 关闭TCP连接(四次挥手)
- 浏览器解析渲染页面
原型
// es5
原型通过构造函数的prototype属性
通过对象的__proto__
// es6
通过类的prototype
let obj = {
name: 'lxh',
}
obj.__proto__.age = 25
console.log(obj.age)
let Fun = function(name) {
this.name = name
}
Fun.prototype.age = 25
let fun = new Fun('lxh')
console.log(fun.age)
class Lxh {
constructor(name) {
this.name = name
}
}
Lxh.prototype.title = 'liaoxianhui'
let lxh = new Lxh('lxh')
console.log(lxh.title)
console.log(lxh.name)
通过id获取数组里的父数据或者当前数据
/**
* 在数组里通过id获取数据
* @param {*} arr 数组
* @param {*} id 唯一标识
* @param {*} childArr 子数组的名称
* @param {*} bool 是否返回父数据
* @returns 返回数据对象
*/
function queryParentElement(arr, id, childArr = 'children', bool = false) {
let fallback = {}
arr.some((item) => {
if (item.id === id) {
fallback = item
return true
} else {
if (item[childArr].length) {
fallback = queryParentElement(item[childArr], id, childArr, bool)
}
if (Object.keys(fallback).length) {
bool && (fallback = item)
return true
}
}
})
return fallback
}
1到100的和
// 方法1
function fun(num = 100, data = 0, sum = 0) {
let newData = data;
let newSum = sum + newData;
return newData === num ? newSum : fun(num, ++newData, newSum);
}
console.log(fun());
// 方法2
function add(n) {
if (n == 1) return 1;
return add(n - 1) + n;
}
console.log(add(100));
获取url参数
const url = window.location.search
const urlSearchParams = new URLSearchParams(url)
const objParams = Object.fromEntries(urlSearchParams.entries())
如果要兼容IE,可以使用`url-search-params-polyfill`这个插件
安装:npm install url-search-params-polyfill --save
需要引入: import 'url-search-params-polyfill'
使用: const params = new URLSearchParams(window.location.search);
new操作符为什么能创建一个实例对象?
分析一下new的整个过程:
- 创建一个空对象
- 继承构造函数的原型
- this指向对象,并调用构造函数
- 返回对象
实现一下new:
function myNew (fn, ...args) {
const obj = {}
obj.__proto__ = fn.prototype
fn.apply(obj, args)
return obj
}
cookies
/**
* 存储到cookies
* @param {*} key
* @param {*} value
* @param {*} date
* 1、1d、1D一天
* 1h、1H一小时
* 1m、1M一分钟
* 1s、1S一秒
*/
function setCookie(key, value, date) {
var expires = new Date();
date = typeof date === "string" ? date.toLowerCase() : date;
if (
typeof date === "number" ||
(typeof date === "string" && date.includes("d"))
) {
typeof date === "string" ?
expires.setTime(
expires.getTime() + date.split("d")[0] * 60 * 60 * 24 * 1000
) :
expires.setTime(expires.getTime() + date * 60 * 60 * 24 * 1000);
} else if (typeof date === "string" && date.includes("h")) {
expires.setTime(expires.getTime() + date.split("h")[0] * 60 * 60 * 1000);
} else if (typeof date === "string" && date.includes("m")) {
expires.setTime(expires.getTime() + date.split("m")[0] * 60 * 1000);
} else if (typeof date === "string" && date.includes("s")) {
expires.setTime(expires.getTime() + date.split("s")[0] * 1000);
}
document.cookie =
key +
"=" +
encodeURIComponent(value) +
(date ? ";expires=" + expires.toGMTString() : "");
}
/**
* 传key获取到单个cookies、不传获取全部
* @param {*} key
* @returns 获取全部和当个没有就返回数组,单个有就返回字符串
*/
function getCookie(key) {
var arr = [];
var val = arr;
document.cookie &&
document.cookie.split(";").forEach((item) => {
arr.push([
item.split("=")[0].trim(),
decodeURIComponent(item.split("=")[1]),
]);
});
key &&
arr.forEach((arrInside) => {
if (arrInside[0] === key) {
val = arrInside[1];
}
});
return val;
}
/**
* 查询key在cookies里是否存在
* @param {*} key
* @returns Boolean
*/
function checkCookie(key) {
var arr = [];
var flag = false;
document.cookie &&
document.cookie.split(";").forEach((item) => {
arr.push(item.split("=")[0].trim());
});
document.cookie &&
arr.forEach((item) => {
if (item === key) {
flag = true;
}
});
return flag;
}
/**
* 删除单个传key、不传删除全部
* @param {*} key
*/
function deleteCookie(key) {
var arr = [];
document.cookie &&
document.cookie.split(";").forEach((item) => {
arr.push(item.split("=")[0].trim());
});
!key &&
document.cookie &&
arr.forEach((item) => {
document.cookie = item + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT";
});
if (key && document.cookie) {
document.cookie = key + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT";
}
}
移动端
var asd = document.getElementById("asd");
var pageX = 0;
var pageY = 0;
var x = 0;
var y = 0;
// 触摸时触发
asd.addEventListener("touchstart", function(e) {
console.log(e.touches); // 触摸屏幕的手指数量
console.log(e.targetTouches); // 触摸dom元素的手指数量
console.log(e.changedTouches); // 手指状态发生的改变,可以从无到有,从有到无
// 实际开发中触摸元素的用的比较多就是targetTouches
// 实现拖到元素
// 获取手指初始坐标 、 初始位置
pageX = e.targetTouches[0].pageX;
pageY = e.targetTouches[0].pageY;
x = this.offsetLeft;
y = this.offsetTop;
});
// 触摸到移动触发
asd.addEventListener("touchmove", function(e) {
// 移动改变位置
this.style.left = x + e.targetTouches[0].pageX - pageX + "px";
this.style.top = y + e.targetTouches[0].pageY - pageY + "px";
e.preventDefault(); // 阻止屏幕滚动的默认行为
});
// 触摸离开触发
asd.addEventListener("touchend", (e) => {
// e.touches、e.targetTouches 就是空的了 e.changedTouches还有
});