一、window
window 对象表示一个包含 DOM 文档的窗口,其 document 属性指向窗口中载入的 DOM 文档 。
1、window 属性和方法
在有标签页功能的浏览器中,每个标签都拥有自己的 window 对象;也就是说,同一个窗口的标签页之间不会共享一个 window 对象。
1.1、几个浏览器的高度
window.screen.height==window.screen.availHeight
表示手机的屏幕高度,在一部手机中是固定的。不同浏览器打开,都不会变。
window.innerHeight==document.documentElement.clientHeight
表示浏览器中,除去顶部地址栏,下部工具栏之外,暴露给用户,可以看的见的中间区域。也就是实际的网页浏览高度,不同浏览器不同。
document.body.clientHeight
body 元素的高度。
如果设置 body 的 height:30px; 那么这个属性就是 30。
下面是示意图,在 pc 端同理
1.2、window.performance
Web Performance API 允许网页访问某些函数来测量网页和 Web 应用程序的性能,包括 Navigation Timing API 和高分辨率时间数据。
performance.now()
该方法返回一个 DOMHighResTimeStamp 对象,该对象表示从某一时刻(译者注:某一时刻通常是 navigationStart 事件发生时刻)到调用该方法时刻的毫秒数。
1.3、devicePixelRatio
返回当前显示设备的物理像素分辨率与 CSS 像素分辨率之比。
物理像素分辨率表示,你的电脑的硬件部分,在出场时,已经确定。比如1920x1080
而 css 像素分辨率,则可以动态调整。
当电脑显示设置为 100%时,物理像素分辨率和 css 像素分辨率是相等的。即 devicePixelRatio 为 1
window.devicePixelRatio //1
而如果你将电脑设置为 150%;或者在电脑设置 100%时,同时将浏览器分辨率调成 150%,那么 devicePixelRatio 都为 1.5
window.devicePixelRatio //1.5
你可以将 devicePixelRatio 为 1.5 理解为,1px 的 css 样式,占用了电脑硬件屏幕分辨路的 1.5 个 dpi,所以肉眼看起来电脑上的文字更大些。
这样就保证了你设置1px的css样式,在不同的设备上显示的大小是一致的。
这种放大的效果在某些情况下,会导致 html 中的元素失真,典型的便是 canvas 变得模糊。
下面是矫正代码
//size为原本的大小
let scale = window.devicePixelRatio;
canvas.width = Math.floor(size * scale);
canvas.height = Math.floor(size * scale);
1.4、URLSearchParams对象
想想下我们如何获取一个url中的query部分。
例如
https://www.baidu.com/s?tn=baidu&wd=测试
通常是先找到?号,截取,然后通过&分割。再循环遍历......
感觉有些麻烦。
幸运的是有个URLSearchParams方式,专门用来处理query字符串。
有两种方式可以获得URLSearchParams对象。
new URLSearchParams(queryStr) 返回URLSearchParams对象
new URL(url).searchParams(key) 返回URLSearchParams对象
这两种方式的区别在于new URLSearchParams只会解析query字符串,不解析完整的url。
而new URL可以接受完整的url,并且在内部自动截取形成query字符串。
看例子吧
let url = new URL("https://www.baidu.com?name=baidu");
url.searchParams.get("name"); //baidu
let urlParams = new URLSearchParams("https://www.baidu.com?name=baidu");
urlParams.get('name');//null
/**出问题了,找不到name,返回了null,因为URLSearchParams并不解析整个url,所以它把https://www.baidu.com?name=baidu中的=号前面的https://www.baidu.com?name都当做key
*/
urlParams.get('https://www.baidu.com?name'); //baidu
//所以对于URLSearchParams我们只需要传入query部分即可
let urlParams = new URLSearchParams("?name=baidu"); //或者name=baidu都可以
urlParams.get("name"); //baidu
简单吧。
由于两种方式返回的都是URLSearchParams对象,所以除了都有get方法之外,还有其他方法
//其他
//得到URLSearchParams对象 urlParams
urlParams.has("name"); //true 是否有name键
urlParams.get("name"); // baidu 通过键获取值
urlParams.get("foo"); // null
urlParams.append("topic", "webdev");//name=baidu&topic=webdev 追加
urlParams.toString(); // name=baidu&topic=webdev
urlParams.set("name", "alibaba");//修改
urlParams.delete("name");
urlParams.getAll("name"); // "?name=baidu&name=tecent" 返回数组["baidu","tecent"],而get("name")只能得到baidu
//其他
1.4、编码/解码
在前端js中,也可以对数据进行编码和解码。
首先,编码和解码是两两匹配的。
window.encodeURI() 编码
window.decodeURI() 解码window.encodeURIComponent() 编码
window.decodeURIComponent() 解码window.btoa() 编码
window.atob() 解码window.escape(已废弃) 编码
window.unescape(已废弃) 解码
即,你不能对window.btoa()编码后的字符串,用window.decodeURIComponent()解码,而要用window.atob()解码。
1.4.1、window.btoa() / window.atob()
btoa(str) 返回一个 base64 编码的 ASCII 字符串。如果str中含有unicode字符,则无法编码,并且触发 InvalidCharacterError 异常。
let encodedData = window.btoa("Hello, world"); // 编码
let decodedData = window.atob(encodedData); // 解码
window.btoa("中国") //编码unicode码
1.4.2、window.encodeURI() / window.decodeURI()
encodeURI(URI) 函数通过将特定字符的每个实例替换为一个、两个、三或四转义序列来对URI进行编码。
但是如果URI中含有下列字符,则忽略对应字符的编码。
; , / ? : @ & = + $ # - _ . ! ~ * ' ( ) 字母 数字
也就是说对中文可以编码的。例如
window.encodeURI("http://username:password@www.example.com:80/path/to/file.php?foo=你好&bar=中国")
//http://username:password@www.example.com:80/path/to/file.php?foo=%E4%BD%A0%E5%A5%BD&bar=%E4%B8%AD%E5%9B%BD
//将“你”编码为“%E4%BD%A0”
1.4.3、window.encodeURIComponent() / window.decodeURIComponent()
encodeURIComponent(URI)函数通过将一个,两个,三个或四个表示字符的UTF-8编码的转义序列替换某些字符的每个实例来编码 URI.
如果URI中含有下列字符,则忽略对应字符的编码。
- _ . ! ~ * ' ( ) 字母 数字
可以发现,encodeURIComponent 编码的范围要比 encodeURI大。encodeURIComponent除了中文还能编码; , / ? : @ & = + $ #
window.encodeURIComponent("http://username:password@www.example.com:80/path/to/file.php?foo=你好&bar=中国")
//"http%3A%2F%2Fusername%3Apassword%40www.example.com%3A80%2Fpath%2Fto%2Ffile.php%3Ffoo%3D%E4%BD%A0%E5%A5%BD%26bar%3D%E4%B8%AD%E5%9B%BD"
可以发现,将 http://的双斜杠也编码了。感觉有些太过。
所以一般情况下,encodeURI用来编码URI,而encodeURIComponent用来编码URI中的query参数,以避免服务器收到不可预知的参数。
比如,一个用户可能会输入"Thyme &time=again"作为comment变量的一部分,其中包含空格和&。
如果不使用encodeURIComponent对此内容进行转义,服务器得到的将是comment=Thyme%20&time=again。请注意,"&"符号和"="符号产生了一个新的键值对,所以服务器得到两个键值对(一个键值对是comment=Thyme,另一个则是time=again),而不是一个键值对。
但是如果用encodeURIComponent编码,
window.encodeURIComponent("Thyme &time=again")
//"Thyme%20%26time%3Dagain"
会得到一个完整的内容
1.5、requestAnimationFrame / cancelAnimationFrame()
window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。
回调函数执行次数通常是每秒 60 次,但在大多数遵循 W3C 建议的浏览器中,回调函数执行次数通常与浏览器屏幕刷新次数相匹配。但是却和 setInterval(),不同,因为 setInterval 设置的秒,并不一定准确在对应秒后执行,而是需要看是否有其他资源在执行,如果有,则等待。实际可能大于等于设置的秒数,而 requestAnimationFrame 会准确的按照浏览器渲染的频率想匹配
//兼容性处理
var requestAnimationFrame =
window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
var cancelAnimationFrame =
window.cancelAnimationFrame || window.mozCancelAnimationFrame;
var start = window.mozAnimationStartTime; // 只有Firefox支持mozAnimationStartTime属性,其他浏览器可以使用Date.now()来替代.
var myReq;
function step(timestamp) {
var progress = timestamp - start;
d.style.left = Math.min(progress / 10, 200) + "px";
if (progress < 2000) {
myReq = requestAnimationFrame(step); //必须再调用一次,这样就形成了递归
}
}
myReq = requestAnimationFrame(step);
//当不需要是取消动画,比如 在vue destoryed中
window.cancelAnimationFrame(myReq);
1.6、getSelection()
此方法不必传入具体的 dom,选中哪个区域或者光标在哪个输入框, 此方法会获取到那个 dom。 返回一个selection对象
window.getSelection();
1.7、scroll() / scrollBy() / scrollTo()
scroll()滚动至文档中的绝对位置(类似于 css 中的 display:absolute), scrollBy()滚动指定的距离(类似于 css 中的 display:relative)
scrollTo 和 scroll 一致。
window.scroll({
top: 100,
left: 100,
behavior: 'smooth' //表示是否平滑的过渡还是即可调到指定位置,同css属性scroll-behavior
});
window.scrollBy({
top: 100,
left: 100,
behavior: "smooth"
});
scroll() scrollBy() scrollTo()合称为 scrollOptions API
1.8、console
console.log()并不是js规范的一部分,而是依赖于宿主环境,即浏览器或node。
对于浏览器而言,console.log这种I/O操作非常耗费性能的。比如在控制台打印1000个console.log,那浏览器就会非常卡。
所以浏览器通过console.log输出的仅仅是个引用。而不是个完整的对象。 直到你点击console.log的那个对象,将对象展开后,才能看到一个完整的对象。
let obj = {
name1:"1",
name2:"2",
name3:"3",
name4:"4",
name5:"5",
name6:"6",
name7:"7",
name8:"8",
name9:"9",
};
console.log("obj=",obj);
obj.name9 = "10";
点击鼠标展开对象
发现name9不是9而是10。
明白这个对于我们进行代码调试非常重要。尤其在不同的代码行中,及时的打印出某个对象的信息时。
既然console.log在调试时,打印出的不是代码运行到那一刻的真实值。那我们就要简单的改造下。
进行深拷贝。
let obj = {
name1:"1",
name2:"2",
name3:"3",
name4:"4",
name5:"5",
name6:"6",
name7:"7",
name8:"8",
name9:"9",
};
console.log("obj========",JSON.parse(JSON.stringify(obj)));
obj.name9 = "10";
展开对象后和我们预期的一样,name9为9,并不是10
2、window 事件
通常在定义 window 中的事件时,有两种方式:
- window.onXXX = function(){};
- window.addEventListener("XXX", ()=>{}, {capture:true, once:true, passive:true});
那么这两种方式有区别吗? 有区别,但也没区别。
因为虽然定义的方式不同,可最终都会走到事件中。
但是又有区别,尤其在多个组件中,同时定义相同的事件,那么 window.onXXX 会后面定义的把前面定义的相同事件覆盖掉。最终只会执行最后定义的那个。而 window.addEventListener 不同,定义几个,执行几次,而且还可以再 options 中设置 once,capture,passive 等选项。
以 passive 为例,它可以忽略事件得默认行为,不会阻止它。当你在 js 操作时,html 中会立马响应到。最典型得可以使得触摸事件和 scroll 事件不再卡顿,非常流畅。
通常有 XXX 方式,那么对应也会有 onXXX
2.1、onbeforeunload
当窗口即将被卸载(关闭)时,会触发该事件.此时页面文档依然可见,且该事件的默认动作可以被取消.
需要注意的是,点击浏览器标签页的 x 号前,如果没有对页面进行过任何“操作”,则不会弹框直接关闭,如果进行过操作,则会弹出确认框。
这里所指的“操作”包括,但不限于,input 输入,打开 console,复制页面的一段文字等等。
window.onbeforeunload = () => {
let isIE = xxxxxxxx; //忽略通过user-agent判断ie浏览器的方法
if (isIE) {
return "The system may be not save your changes.";
} else {
return false; //其他浏览器会弹出自己的文字,如上图。是中文
}
};
2.2、onhashchange
当 一个窗口的 hash (URL 中 # 后面的部分)改变时就会触发 hashchange 事件(参见 location.hash)。
window.onhashchange = (newURL, oldURL)=>{
//newURL 当前页面新的URL
//oldURL 当前页面旧的URL
};
2.3、onload / DOMContentLoaded
DOMContentLoaded 指 html 页面加载完毕就会触发,至于一些异步资源的比如 img,video 等等,它不关心。
而 onload 表示除了 html 加载完,img,video 等也加载完,才执行
window.addEventListener("load", function () {
});
window.addEventListener("DOMContentLoaded", function () {
});
看到没: JavaScript event that fires when the DOM is loaded, but before all page assets are loaded (CSS, images, etc.). 说的再清楚不过了
2.4、onmouseover、onmouseout / onmouseenter、onmouseleave
onmouseover、onmouseout:鼠标移动到自身时候会触发事件,同时移动到其子元素身上也会触发事件 onmouseenter、onmouseleave:鼠标移动到自身是会触发事件,但是移动到其子元素身上不会触发事件
onmouseover 和 onmouseout 支持性一致
onmouseenter 和 onmouseleave 支持性一致
2.5、onscroll / onwheel
onwheel 事件在鼠标滚轮在元素上下滚动时触发,必须滚轮要转,至于页面滚不滚动,它不关心。
onscroll 事件在元素滚动条在滚动时触发,必须滚动条存在且上下运动。
触发方式有:滚轮滚动,鼠标按住滚动条拖动,键盘上下键滚动,js 脚本去滚动如 scrollTo,scrollBy,scrollByLines, scrollByPages 等。
在这里有个特殊,在手机端,虽然没有鼠标,但是手指触摸上下滚动,也是触发 onscroll 的。
什么? 支持率这么一点点? 大大出乎我的意料
2.6、copy 事件/cut 事件/ paste 事件
window 的复制/剪切, 粘贴事件。
window.addEventListener("copy", function (e) {
//将复制的数据,存入到剪切板中
e.clipboardData.setData("abc", "Hello, world!");
e.preventDefault();
});
window.addEventListener("paste", function (e) {
//再从剪切板中取出数据
let data = e.clipboardData.getData("abc");
e.preventDefault();
});
这三个事件支持性一致
2.7、error 事件
当资源加载失败或无法使用时,会在 Window 对象触发 error 事件。例如:script 执行时报错。
window.addEventListener('error', (event) => {
});
2.8、orientationchange 事件
orientationchange 事件在设备的纵横方向改变时触发。
window.addEventListener("orientationchange", function() {
});
pc 上几乎都不支持,这也能理解,毕竟 pc 上是使用 resize 的,只有手机才会使用 横竖屏。
2.9、rejectionhandled 事件 / unhandledrejection 事
当 Promise 被 rejected 且有 rejection 处理器时会在全局触发 rejectionhandled。
当 Promise 被 reject 且没有 reject 处理器的时候,会触发 unhandledrejection 事件;
window.addEventListener("rejectionhandled", event => {
}, false);
window.addEventListener("unhandledrejection", event => {
});
function getName() {
return Promise.reject("error");
}
let p = getName(); //触发unhandledrejection
setTimeout(() => {
p.catch((err) => {}); //触发rejectionhandled
}, 1000);
2.10、storage 事件
当 localStorage 被修改时,将触发 storage 事件。
注意:
- 只有在同域名的不同文件之间,才能够监听到,
- 同一个文件中,是监听不到的(合理,同一个文件直接拿就行了,用 storage 监听多此一举)
- 通过 iframe 引入的另一个站点,也可以监听到
2.11、online 事件 / offline 事件
online 当网络连接时,触发,
offline 当网络断开时触发
online 和 offline 支持性一致
二、element(DOM 对象)
1、element 属性和方法
1.1、clientHeight / clientWidth 属性
- clientHeight:元素内部的高度,包含内边距,但不包括水平滚动条、边框和外边距。
- clientWidth:元素内部的宽度,包含内边距,但不包括垂直滚动条、边框和外边距。
此属性会将获取的值四舍五入取整数。
document.querySelector("span").clientWidth
clientHeight 和 clientWidth 支持性一致
1.2、scrollHeight / scrollWidth 属性
类似于上方的 clientHeight / clientWidth,不同在于 clientHeight / clientWidth 在元素设置 overflow 后,不包含隐藏不可见的高度部分。而 scrollHeight / scrollWidth 却包含隐藏的那部分高度。
我觉得利用这个特性来判断是否超长,如果 scrollWidth 大于 clientWidth,则表示超长,此时可以对于那些超长后显示...的元素在浮上去后展示一个自定义的 toolip.
scrollHeight 和 scrollWidth 支持性一致
1.3、getBoundingClientRect()方法
返回元素的大小及其相对于视口的位置(眼睛看的见的文档区域)。
这个方法超级好用。不管你的元素在什么位置,它都会计算出来当前元素 相当于 视口的边缘的位置,和滚动条无关。
返回值为
{
bottom: xx, //元素底部离视口顶部的距离
height: xx, //元素高度,和元素的clientHeight属性一致,但比它精确,会保留小数
left: xx, //元素左边离视口左侧的距离
right: xx, //元素右边离视口左侧的距离
top: xx, //元素上部离视口顶部的距离
width: xx, //元素宽度,和元素的clientWidth属性一致,但比它精确,会保留小数
}
这个方法的支持性为 100%, 良心啊,为什么我才知道这个方法?
1.4、scrollIntoView()方法
如果父元素定义了 overflow,并产生了滚动条,里面有个子元素,在滚动条滑动后,看不到了。那么可以让这个子元素执行这个方法,让元素滚动到父元素可视区域内。
可以定义滚动条可视区域的顶部,底部,还是左边,右边。
也可以定义平滑的滚动过来,还是一瞬间滚动过来。
这个方法的好处是自动计算里面子元素距离可见区域多少,不需要给数值进行人为干涉,而 scroll()或 scrollTo()方法,必须人为计算滚动条的距离,然后给个数值,才能滚动到某个位置。
首先看个例子,
div1 超过屏幕的区域有个 div2,超过 div2 的区域有个 span1,此时我们可以通过调用 span1 元素的 scrollIntoView 方法,让 span1,滚动到 div2 的可见区域,也就是向左边滚动,但是有个坏处,就是 div1 也会向上滚动,直到 div2 能被用户看到。
而 scroll()或 scrollTo()方法在 span1 滚动到 div2 的可见区域时,div1 不动。不会产生其他附加作用,直到你慢慢滑屏,才会看到 span1,已经停留在 div2 的可视范围了。
1.5、scroll() / scrollTo() / scrollBy() 方法
scrollTo 方法可以使界面滚动到给定元素的指定坐标位置。
以产生滚动条的那个父元素为基准,
scroll() scrollBy() scrollTo()合称为 scrollOptions API
2、element 事件
2.1、contextmenu 事件
contextmenu 事件会在用户尝试打开上下文菜单时被触发。该事件通常在鼠标点击右键或者按下键盘上的菜单键时被触
<p id="noContextMenu">这个段落右键菜单已被禁用。</p>
noContext = document.getElementById('noContextMenu');
noContext.addEventListener('contextmenu', e => {
e.preventDefault();
});
单击右键,不会出现标准的右键菜单项目
2.2、copy 事件/cut 事件/ paste 事件
dom 的复制/剪切, 粘贴事件。
<div class="source">
Try copying text from this box...
</div>
<div class="target" contenteditable="true">
...and pasting it into this one
</div>
let sourceDom = document.querySelector(".source");
sourceDom.addEventListener("copy", function (e) {
//将数据存入剪切板
e.clipboardData.setData("abc", "Hello, world!");
e.preventDefault();
});
let targetDom = document.querySelector(".target");
targetDom.addEventListener("paste", function (e) {
//从剪切板取出来
let data = e.clipboardData.getData("abc");
//自定义转化数据
data = data.toUpperCase();
//获取光标位置
const selection = window.getSelection();
if (!selection.rangeCount) return false;
selection.deleteFromDocument();
//将转换后的数据插入到光标位置
selection.getRangeAt(0).insertNode(document.createTextNode(data));
e.preventDefault();
});
这三个事件支持性一致
2.3、focusin 事件 / focusout 事件
当元素获得焦点时,focusin 事件被触发。
focusin 事件和 focus 事件之间的主要区别在于 focus 不会冒泡。
当元素即将失去焦点时,focusout 事件被触发。
focusout 事件和 blur 事件之间的主要区别在于 blur 不会冒泡。
2.4、onscroll / onwheel
类似于 window 的 onscroll 和 onwheel。只不过绑定对象为 element
let sourceDom = document.querySelector(".source");
sourceDom.addEventListener("scroll", function (e) {
console.log("111");
});
sourceDom.addEventListener("wheel", function (e) {
console.log("111");
});
三、document(文档对象)
1、document 属性和方法
1.1、characterSet
返回当前文档的字符编码,但有相当一部分浏览器未实现,可使用原始的 charset 代替
document.characterSet || document.charset
1.2、compatMode
表明当前文档的渲染模式是怪异模式/混杂模式还是标准模式。
document.compatMode // BackCompat怪异模式, CSS1Compat标准模式
1.3、defaultView
返回当前 document 对象所关联的 window 对象,如果没有,会返回 null。
document.defaultView //返回还是window,我为什么不直接 使用window呢?
上面的代码很平淡,不足为奇,可是下面的就不一定了
<div class="source" style="height: 200px; overflow: auto;">
<object type="text/html" style="width: 100%; height: 100%;"> </object>
</div>
let doc = document.querySelector("object").contentDocument; //得到一个document对象
doc.defaultView; //接着得到一个window
得到这个 window 后,有什么用呢?
我们可以想象一个场景,当我们缩放 window 窗口时,会触发 window 的 onresize 事件,但是缩放 div,却不会触发 onresize 事件,因为 dom 没有 onresize,那么如何监听一个 div 的 resize 呢?
就是上述的方式,在 div 中套一个 object。我不监听 div,我监听 div 中的 object,一旦 div 变了,object 不也就变了吗?
然后通过 object 返回的一个 window 对象,就自然可以绑定 onresize 事件了
document.querySelector("object").contentDocument.defaultView .addEventListener("resize", () => {
//只要div尺寸变化,object尺寸就变化,resize就能监听到。不管何种原因导致的尺寸变化。都会监听到。
});
除此之外,我有一遍专门介绍如何彻底解决 div 尺寸变化问题的文章,有兴趣的伙伴可以阅读
1.4、designMode
控制整个文档是否可编辑。有效值为 "on" 和 "off" 。
默认值为 "off" 。如果设置为"on",则好比给 html 所有的元素都添加了 contenteditable 属性。
1.5、documentElement
会返回文档对象(document)的根元素。
可以通过 document.documentElement.clientHeight 来获取浏览器的可用高度,这个高度和 html 或者 body 的 style 上的 height 无关,只与浏览器的上方地址栏,下方工具栏等有关,和 window.innerHeight 相等。
2、document 事件
2.1、onscroll / onwheel
类似于 window 的 onscroll 和 onwheel。只不过绑定对象为 document
document.addEventListener("scroll", function (e) {
console.log("111");
});
document.addEventListener("wheel", function (e) {
console.log("111");
});
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。