GG资源网

几个非常有意思的javascript知识点总结【实践】(JavaScript地理位置信息API的详细内容)

几个非常有意思的javascript知识点总结【实践】

作者:徐小夕

转发链接:https://mp.weixin.qq.com/s/ZbBTSuO1UPDBWbTKHxI4eA

前言

作为一名前端爱好者, 笔者利用空余时间研究了几个国外网站的源码,发现不管是库,还是业务代码,都会用到了一些比较有意思的API,虽然平时在工作中部分接触过,但是经过这次的研究,觉得很有必要总结一下,毕竟已经2020年了,是时候更新一下技术储备了,本文主要通过实际案例来带大家快速了解以下几个知识点:

  • Observer 原生观察者
  • script标签事件深入 - 移除script标签后事件仍然能执行的原因
  • Proxy/Reflect
  • 自定义事件
  • fileReader API
  • Fullscreen 网页全屏
  • URL API的使用
  • Geolocation 地理位置API的使用
  • Notifications 浏览器原生消息通知
  • Battery Status 设备电量情况

我会对部分API做一些比较有意思的案例,那么开始我们的学习吧~

1. Observer API

Observer是浏览器自带的观察者,它主要提供了Intersection, Mutation, Resize, Performance这四类观察者, 这里笔者重点介绍Intersection Observer.

1.1 Intersection Observer

说简单点就是该api可以异步监听目标元素在根元素里的位置变动,并触发响应事件.我们可以利用它来实现更为高效的图片懒加载, 无限滚动以及内容埋点上报等.接下来我们通过一个例子来说明一下它的使用步骤.

// 1.定义观察者及观察回调
const intersectionObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
console.log(entry)
// ...一些操作
});
},
{
root: document.querySelector('#root'),
rootMargin: '0px',
threshold: 0.5
}
)
// 2. 定义要观察的目标对象
const target = document.querySelector(“.target”);
intersectionObserver.observe(target);复制代码

以上代码就实现了一个基本的Intersection Observer,虽然已有代码中还体现不出什么实质性功能. 接下来介绍一下代码中使用到的参数的含义:

  • callback IntersectionObserver实例的第一个参数, 当目标元素与根元素通过阈值时就会触发该回调.回调中第一个参数是被观察对象列表,一旦被观察对象发生突变就会被移入该列表, 列表中每一项都保留有观察者的位置信息;第二个参数为observer,观察者本身.如下图控制台打印:其中rootBounds表示根元素的位置信息, boundingClientRect表示目标元素的位置信息,intersectionRect表示叉部分的位置信息, intersectionRatio表示目标元素的可见比例.
  • 配置属性 IntersectionObserver实例的第二个参数,用来配置监听属性,具体有以下三个属性:
    • root 所监听对象的具体祖先元素(element)。如果未传入值或值为null,则默认使用顶级文档的视窗。
    • rootMargin 计算交叉时添加到根(root)边界盒bounding box的矩形偏移量, 可以有效的缩小或扩大根的判定范围从而满足计算需要
    • thresholds 一个包含阈值的列表, 按升序排列, 列表中的每个阈值都是监听对象的交叉区域与边界区域的比率。当监听对象的任何阈值被越过时,都会生成一个通知(Notification)。如果构造器未传入值, 则默认值为0。以上属性介绍字面上可能很难理解,笔者花几个草图来让大家有个直观的认知:当我们设置rootMargin为10px时,我们的root会增大影响范围,但目标元素移动到淡红色区域就会被监听到,当然我们还可以设置rootMargin为负值来减少影响区域.其支持的值为百分比和px,如下:

rootMargin: '10px'
rootMargin: '10%'
rootMargin: '10px 0px 10px 10px'复制代码

thresholds可以如下图理解:

由上图所示,当我们设置阈值为[0.25, 0.5]时, 目标元素的25%和50%进入根元素的影响范围时都会触发回调.利用这个特性我们往往可以实现位差动画,或者更根据目标元素的位置变化做不同的交互. 当然Intersection还提供了以下几个方法来控制观察对象:

  • disconnect() 使IntersectionObserver对象停止监听工作
  • takeRecords() 返回所有观察目标的IntersectionObserverEntry对象数组
  • unobserve() 使IntersectionObserver停止监听特定目标元素

了解了使用方法和api之后,我们来看看一个实际应用--实现图片懒加载:

<img src="loading.gif" data-src="absolute.jpg">
<img src="loading.gif" data-src="relative.jpg">
<img src="loading.gif" data-src="fixed.jpg">

<script>
let observerImg = new IntersectionObserver(
(entries, observer) => {
entries.forEach(entry => {
// 替换为正式的图片
entry.target.src = entry.target.dataset.src;
// 停止监听
observer.unobserve(entry.target);
});
},
{
root: documennt.getElementById('scrollView'),
threshold: 0.3
}
);

document.querySelectorAll('img').forEach(img => { observerImg.observe(img) });
</script>复制代码

以上代码就实现了一个图片懒加载功能, 当图片的30%进入根元素时才加载真实的图片,这又让我想起了之前在某条做广告埋点上报时使用react-lazyload的画面.大家还可以利用它实现无限滚动, H5视差动画等有意思的交互场景.

1.2 Mutation Observer和Resize Observer

Mutation Observer主要用来实现dom变动时的监听,同样也是异步触发,对监听性能非常友好. Resize Observer主要用来监听元素大小的变化,相比于每次窗口变动都触发的window.resize事件, Resize Observer有更好的性能和对dom有更细粒度的控制,它只会在绘制前或布局后触发调用. 以上两个api的使用和Intersection使用非常类似,官方资料也写得很全,大家可以好好研究一下.

2. 移除script标签后事件仍然能执行的原因

这个问题主要是之前有朋友问过我,当时的想法就是简单的认为script内的代码执行完之后以及与dom绑定了,存放在了浏览器内存中,最近查了很多资料发现有一个有点意思的解释,放出来大家可以感受一下:

3. Proxy/Reflect

Proxy/Reflect虽然是es6的api,出现也已经有几年了,但是在项目中用的还是比较少,如果是做底层架构方面的工作,还是建议大家多去使用,毕竟vue/react这种框架源码把这些api玩的如火纯青,还是很有必要掌握一下的。

其实我们认真看mdn的介绍或者阮一峰老师的文章,还是很好理解这些api的用法的,接下来我们详细介绍一下这两个api以及应用场景.

3.1 Proxy

const obj = {
name: '徐小夕',
age: '120'
}
const proxy = new Proxy(obj, {
get(target, propKey, receiver) {
console.log('get:' + propKey)
return Reflect.get(target, propKey, receiver)
},
set(target, propKey, value, receiver) {
console.log('set:' + propKey)
return Reflect.set(target, propKey, value, receiver)
}
})
console.log(proxy.name) // get:name 徐小夕
proxy.work = 'frontend' // set:work frontend复制代码

以上代码拦截了obj对象,并重新定义了读写(get/set)方法,这样我们就可以在访问对象时进行额外的操作了.

Proxy还有apply(拦截 Proxy 实例作为函数调用的操作)和construct(拦截 Proxy 实例作为构造函数调用的操作)等属性可以使用,我们可以在对象操作的不同阶段进行拦截,这里我就不一一样举例了.接下来看看Proxy的实际应用场景.

  • 实现数组读取负数的索引

我们一般操作数组大多数都是正向操作的,不能通过指定负数来逆向查找数组,如下图:

我们不能通过arr[-1]来拿到数组的尾部元素(字符串同理),这个时候我们就可以用Proxy来实现这一功能,这是我们的结构有点像环状:

这种实现的好处是如果我们想访问数组的最后一个元素时,我们不需要先拿到长度,再通过索引访问了:

// 原始写法
arr[arr.length -1]
// 通过proxy改造后写法
arr[-1]复制代码

实现代码如下:

function createArray(...elements) {
let handler = {
get(target, propKey, receiver) {
let index = Number(propKey);
if (index < 0) {
propKey = String(target.length + index);
}
return Reflect.get(target, propKey, receiver);
}
};

let target = [];
target.push(...elements);
return new Proxy(target, handler);
}复制代码

我们可以发现以上代码使用proxy来代理数组的读取操作,在内部封装了支持负值查找的功能,当然我们也可以不用proxy来实现同样的功能,这里实现参考阮一峰老师的实现.

  • 利用proxy实现更优雅的校验器

一般我们在做表单校验的时候会写一些if else或者switch判断来实现对不同属性值的校验,同样我们也可以用proxy来优雅的实现它,代码如下:

const formData = {
name: 'xuxi',
age: 120,
label: ['react', 'vue', 'node', 'javascript']
}
// 校验器
const validators = {
name(v) {
// 检验name是否为字符串并且长度是否大于3
return typeof v === 'string' && v.length > 3
},
age(v) {
// 检验age是否为数值
return typeof v === 'number'
},
label(v) {
// 检验label是否为数组并且长度是否大于0
return Array.isArray(v) && v.length > 0
}
}
// 代理校验对象
function proxyValidator(target, validator) {
return new Proxy(target, {
set(target, propKey, value, receiver) {
if(target.hasOwnProperty(propKey)) {
let valid = validator[propKey]
if(!!valid(value)) {
return Reflect.set(target, propKey, value, receiver)
}else {
// 一些其他错误业务...
throw Error(值验证错误${propKey}:${value})
}
}
}
})
}复制代码

有了以上实现模式,我们就可以实现对表单中某个值进行设置时进行校验了,用法如下:

let formObj = proxyValidator(formData, validators)
formObj.name = 333; // Uncaught Error: 值验证错误name:f
formObj.age = 'ddd' // Uncaught Error: 值验证错误age:f复制代码

以上代码中当设置了不合法的值时,控制台将会剖出错误,如果在实际业务中,我们可以给用户做出适当的提醒.

  • 实现请求拦截和错误上报
  • 实现数据过滤

以上几点笔者在之前的文章中也写过,所以这里不在详细介绍了.大家也可以根据实际情况自己实现更加灵活的拦截操作.当然Proxy提供的API远远不止这几个,我们可以在MDN或者其他渠道了解更多高级用法.

3.2 Reflect

Reflect对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 API,更多的应用场景是配合proxy一起使用,在上文中已经用到了.可以将Object对象的一些明显属于语言内部的方法放到Reflect对象上,并修改某些Object方法的返回结果. Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。

4. 自定义事件

CustomEvent API是个非常有意思的api, 而且非常实用, 更重要的是学起来非常简单,而且被大部分现代浏览器支持.我们可以让任意dom元素监听和触发自定义事件,只需要如下操作:

// 添加一个适当的事件监听器
dom1.addEventListener("boom", function(e) { something(e.detail.num) })

// 创建并分发事件
var event = new CustomEvent("boom", {"detail":{"num":10}})
dom1.dispatchEvent(event)复制代码

我们来看看CustomEvent的参数介绍:

  • type 事件的类型名称,如上面代码中的'boom'
  • CustomEventInit 提供了事件的配置信息,具体有以下几个属性
    • bubbles 一个布尔值,表明该事件是否会冒泡
    • cancelable 一个布尔值,表明该事件是否可以被取消
    • detail 当事件初始化时传递的数据

我们可以通过dispatchEvent来触发自定义事件.其实他的用途有很多,比如创建观察者模式, 实现数据双向绑定, 亦或者在游戏开发中实现打怪掉血,比如下面的例子:

笔者上面画了一个打boss的草图, 现在的场景是两个玩家一起打boss, 我们可以在玩家发动攻击的时候触发dispatch掉血的自定义事件, boss监听到事件后将血量自动扣除, 至于不同角色的伤害值,我们可以存放在detail中,然后通过策略模式去分发伤害.笔者曾今在学校开发的H5游戏时就大量采用类似的模式,还是非常有意思的.

5. fileReader

File API使得我们在浏览器端可以访问文件的数据,比如预览文件,获取文件信息(比如文件名,文件内容,文件大小等), 并且可以在前端实现文件下载(可以借助canvas和 window.URL.revokeObjectURL的一些能力).当然我们还可以实现拖拽上传文件这样高用户体验的操作.接下来我们来看看几个实际例子.

  • 显示缩略图

function previewFiles(files, previewBox) {
for (var i = 0; i < files.length; i++) {
var file = files[i];
var imageType = /^image\\//;

if (!imageType.test(file.type)) {
continue;
}

var img = document.createElement("img");
previewBox.appendChild(img); // 假设"preview"就是用来显示内容的div

var reader = new FileReader();
reader.onload = (function(imgEl) {
return function(e) { imgEl.src = e.target.result; };
})(img);
reader.readAsDataURL(file);
}
}复制代码

以上代码可以在reviewBox容器中显示已上传好的图片,当然我们还可以基于此来扩展,利用canvas将图片画到canvas上,然后进行图片压缩,最后再把压缩后的图片上传到服务器.这种方式其实目前很多工具型网站都在用,比如在线图片处理网站,提供的批量压缩图片,批处理水印等功能,套路都差不多,感兴趣的朋友可以尝试研究一下.

  • 封装文件上传组件

这块笔者之前也写过详细的文章,这里就不一一举例了, 文章地址:

  • 3分钟教你用原生js实现具有进度监听的文件上传预览组件
  • 记一次老项目中的跨页面通信问题和前端实现文件下载功能

6. Fullscreen

全屏API主要是让网页能在电脑屏幕中全屏显示,它允许我们打开或者退出全屏模式,以便我们根据需要进行对应的操作,比如我们常用的网页图形编辑器或者富文本编辑器, 为了让用户专心于内容设计,我们往往提供切换全屏的功能供用户使用.由于全屏API比较简单,这里我们直接上代码:

// 开启全屏
document.documentElement.requestFullscreen();
// 退出全屏
document.exitFullscreen();复制代码

以上代码的document.documentElement也可以换成任何一个你想让其全屏的元素.默认情况下我们还可以通过document.fullscreenElement来判断当前页面是否处于全屏状态,来实现屏幕切换的效果.如果是react开发者,我们也可以将其封装成一个自定义hooks来实现与业务相关的全屏切换功能.

7. URL

我们利用URL组件可以做很多有意思的事情.比如我们有个需求需要提取url的参数传给后台,传统的做法是自己写一个方法来解析url字符串,手动返回一个query对象.但是利用URL对象,我们可以很方便的拿到url参数,如下:

let addr = new URL(window.location.href)
let host = addr.host // 获取主机地址
let path = addr.pathname // 获取路径名
let user = addr.searchParams.get("user") // 获取参数为user对应的值复制代码

以上代码可知,我们如果将url转化为URL对象,那么我们就可以很方便的通过searchParams提供的api来拿到url参数而无需自己再写一个方法了.

另一方面,如果网站安全性比较高,我们还可以对参数进行自然数排序然后再加密上传给后端.具体代码如下:

function sortMD5WithParameters() {
let url = new URL(document.location.href);
url.searchParams.sort();
let keys = url.searchParams.keys();
let params = {}

for (let key of keys) {
let val = url.searchParams.get(key);
params[key] = val
};
// ...md5加密
return MD5(params)
}复制代码

8. Geolocation

地理位置 API 通过 navigator.geolocation 提供, 这个浏览器API也比较实用, 我们在网站中可以用此方式确定用户的位置信息,从而让网站有不同的展现,增强用户体验.

举几个有意思的例子可以让大家感受一下:

  • 根据不同地区,网站展示不同的主题:
  • 根据用户所在地区,展示不同推荐内容 这一点电商网站或者内容网站用的比较多, 比如用户在新疆,则给他推荐瓜果类广告, 在北京,则给他推荐旅游景点类广告等,虽然实际应用中往往会更复杂,但是也是一种思路.

其实应用远远不止如此,程序员可以发挥想象来实现更有意思的事情,让自己的网站更智能.接下来笔者就基于promise写一段获取用户位置的代码:

function getUserLocation() {
return new Promise((resolve, reject) => {
if (!navigator.geolocation) {
reject()
} else {
navigator.geolocation.getCurrentPosition(success, error);
}

function success(position) {
const latitude = position.coords.latitude;
const longitude = position.coords.longitude;
resolve({latitude, longitude})
}

function error() {
reject()
}
})
}复制代码

使用方式和结果如下图所示:

我们基于获取到的经纬度调用第三方api(比如百度,高德)就可以获取用户所在为精确位置信息了.

9. Notifications

我们举个实际的例子,比如我们网站内容有更新,通知用户,效果如下:

相关代码如下:

Notification.requestPermission( function(status) {
console.log(status); // 仅当值为 "granted" 时显示通知
var n = new Notification("趣谈前端", {body: "从零搭建一个CMS全栈项目"}); // 显示通知
});复制代码

当然浏览器的Notification还给我们提供了4个事件触发api方便我们做更全面的控制:

  • show 当通知被显示给用户时触发
  • click 当用户点击通知时触发
  • close 当通知被关闭时触发
  • error 当通知发生错误的时候触发

有了这样的事件监听,我们就可以控制当用户点击通知时, 跳转到对应的页面或者执行相关的业务逻辑.如下代码所示:

Notification.requestPermission( function(status) {
console.log(status); // 仅当值为 "granted" 时显示通知
var n = new Notification("趣谈前端", {body: "从零搭建一个CMS全栈项目"}); // 显示通知
n.onshow = function () {
// 消息显示时执行的逻辑
console.log('show')
}
n.click = function () {
// 消息被点击时执行的逻辑
history.push('/detail/1232432')
}
n.close = function () {
// 消息关闭时执行的逻辑
console.log('close')
}
});复制代码

当然我们在使用前需要获取权限,方式也很简单,大家可以在mdn上学习了解.

10. Battery Status

之前的版本中Battery Status API提供了几个事件监听函数来监听电量的变化以及监听设备是否充电,但是笔者看文档时这些api都已经废弃,如下:

  • chargingchange 监听设别是否充电
  • levelchange 监听电量充电等级
  • chargingtimechange 充电时间变化
  • dischargingtimechange 放电时间变化

虽然以上几个看似有用的api已经被弃用,但是笔者亲测谷歌还是可以正常使用的,但是为了让自己代码更可靠,我们可以用其他方式代替,比如用定时器定期去检测电量情况,进而对用户做出不同的提醒.

接下来我们看看基本的用法:

navigator.getBattery().then(function(battery) {
console.log("是否在充电? " + (battery.charging ? "是" : "否"));
console.log("电量等级: " + battery.level * 100 + "%");
console.log("充电时间: " + battery.chargingTime + " s");
console.log("放电时间: " + battery.dischargingTime + "s");
});复制代码

我们可以通过getBattery拿到设备电池信息,这个api非常有用,比如我们可以在用户电量不足时禁用网站动画或者停用一些耗时任务,亦或者是对用户做适当的提醒,改变网站颜色等,对于webapp中播放视频或者直播时,我们也可以用css画一个电量条,当电量告急时提醒用户.作为一个优秀的网站体验师,这一块还是不容忽视的.

推荐javascript学习相关文章

《关于前端174道 JavaScript知识点汇总(一)》

《关于前端174道 JavaScript知识点汇总(二)》

《关于前端174道 JavaScript知识点汇总(三)》

《70个JavaScript知识点详细总结(上)【实践】》

《70个JavaScript知识点详细总结(下)【实践】》

《JavaScript ECMAScript语法概括【思维导图】》

《100个原生JavaScript代码片段知识点详细汇总【实践】》

《都2020年了,你还不会JavaScript 装饰器?》

《首屏时间从12.67s到1.06s,手把手教你如何做到的?》

作者:徐小夕

转发链接:https://mp.weixin.qq.com/s/ZbBTSuO1UPDBWbTKHxI4eA

JavaScript地理位置信息API的详细内容

对于一个Web开发程序员来说,开发工作中一个最有意思的方面就是获取地理位置信息;试想一下,浏览你的网页的用户是在什么地方?程序员可以根据用户的地理位置信息来调整网站的语言、特定产品介绍等。下面我们将要演示的就是通过浏览器里JavaScript地理位置信息API来获取详细地理信息!

检查你的浏览器是否支持地理位置信息API

目前主流的浏览器都已经对JavaScript地理位置信息API有了较好的支持。但如果你还不放心,那么,确认地理位置信息API支持情况最好的方式是浏览器的功能特征测试。

if(\"geolocation\" in navigator) {

//w00t!

}

else {

alert(\"很不幸!你的浏览器并不支持Geolocation API功能\");

}

对于判断浏览器是否支持地理位置API,最主要的就是看看navigator.geolocation这个对象,使用in,而不是简单的使用if(navigator.geolocation),这一点非常重要,因为后者有可能会因此初始化地理位置信息对象,从而占用/锁定了设备资源。

查询地理位置信息

这个navigator.geolocation.getCurrentPosition方法是获取详细位置信息最关键的一个接口:

if(\"geolocation\" in navigator) {

navigator.geolocation.getCurrentPosition(function(position) {

console.log(position);

});

}

一旦你调用了这个方法(如果请求成功,它会执行你在参数里提供的回调方法),浏览器会询问用户是否允许程序获取他们的地理位置信息:

当用户运行网页获取他们的位置信息后,浏览器就可以开始读取地理信息,它会返回给你一个位置信息对象,对象的结构基本是这样的:

// \"Position\" object

{

coords: { \"Coordinates\" object

accuracy: 65,

altitude: 294.4074401855469,

altitudeAccuracy: 10,

heading: -1,

latitude: 43.01256284360166,

longitude: -89.44531987692744,

speed: -1

},

timestamp: 1429722992094269

}

如果你觉得这些地理位置信息(地理经纬度坐标)还不够充足,还想要这些地理坐标属于哪个国家、城市,则你需要再调用其它的第三方数据库——这里我们就不细述了。

这个地理位置信息API在很多移动应用里是最常见的API运用,作为Web程序员,它应该是你必须具备的一项知识技巧。幸运的是,目前所有流行的浏览器都支持了这种技术。祝编程快乐!

以上就是学习JavaScript地理位置信息API的详细内容,更多请关注网站的其它相关文章!

JavaScript地理位置信息API的详细内容 (https://www.wpmee.com/) javascript教程 第1张

由于网站搬家,部分链接失效,如无法下载,请联系站长!谢谢支持!
1. 带 [亲测] 说明源码已经被站长亲测过!
2. 下载后的源码请在24小时内删除,仅供学习用途!
3. 分享目的仅供大家学习和交流,请不要用于商业用途!
4. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!
5. 本站所有资源来源于站长上传和网络,如有侵权请邮件联系站长!
6. 没带 [亲测] 代表站长时间紧促,站长会保持每天更新 [亲测] 源码 !
7. 盗版ripro用户购买ripro美化无担保,若设置不成功/不生效我们不支持退款!
8. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
9. 如果你也有好源码或者教程,可以到审核区发布,分享有金币奖励和额外收入!
10.如果您购买了某个产品,而我们还没来得及更新,请联系站长或留言催更,谢谢理解 !
GG资源网 » 几个非常有意思的javascript知识点总结【实践】(JavaScript地理位置信息API的详细内容)

发表回复

CAPTCHAis initialing...