文章目录
《HTML并不简单:Web前端开发精进秘籍》张鑫旭,一些摘要:
HTML,这门语言的知识体系非常庞杂,涉及Web交互开发、SEO(搜索引擎优化)、安全、无障碍访问等各个领域,并不仅仅是单纯的标签结构。
大家之所以对HTML有错误认知,一是因为不关心,二是因为认为它可替代,三是因为它缺少好的科普著作。
过去,这是前端开发中的常识,而如今却是冷知识,知者甚少。一方面是因为前端知识图谱更广了,从业者学习的精力有限,无暇顾及这部分内容;另一方面是产品形态更加多样化,例如移动端产品、工具类产品是不需要SEO知识的;当然,还有一部分原因是,这些知识的匮乏对业务收益的影响是不可见的,或者可以通过其他策略规避因开发人员不懂rel="nofollow"给产品带来的损失。
a元素,想要跳转到某个页面,使用href属性即可。若想新窗口打开目标页面,则:
javascript
<a href="/" target="_blank">首页</a>
即可。
a元素支持rel属性。
HTML 属性:rel - HTML(超文本标记语言) | MDN (mozilla.org)
我们这里关注几个值:
nofollow
noopener
和opener
noreferrer
rel="nofollow"
给a元素设置rel="nofollow"是SEO中的常用策略,用来告诉搜索引擎不要追踪这个链接。一般情况下,有两种情况需要设置此属性:
- 目标页面是无效信息,或含有敏感信息
- 目标是外站,不希望共享权重
不希望共享权重的详细描述:
SEO中有个策略,如果一个权重很高的网站,直接外链一个权重不高的中小网站,同时没有设置rel="nofollow",那么这个中小网站的权重有一定的概率会被提高。
如果不想权重外泄,可以设置此属性。
相关链接:
如何用其他方法规避不懂rel="nofollow"
带来的损失?
方法:将所有的外链都换成本站地址进行中转
专门申请一个域名,A网站想要外联B网站,点击B网站先跳到中转站,再跳去B网站。这样可以避免权重外泄,也可以统计外链的点击量。
CSDN就是这样做的:
javascript
https://link.csdn.net/?from_id=xxxxxxxx&target=https%3A%2F%2Fzhuanlan.zhihu.com%2Fp%2F315000749
rel="noopener"与rel="opener"
省流版:如果是不信任 的链接地址,只要是新窗口打开的
target="blank"
,一定要设置rel="noopener"
。
有场景如下:
主页面有一个"登录"按钮,点击后会开启一个新窗口进行登录,现在希望用户登录成功后,主页面自动刷新,该如何实现?
或许可以用以下方法:
- visibilitychange事件
- postMessage实现跨页面通信
javascript
document.addEventListener('visibilitychange',function(){
if(document.visibilitychange==='visible'){
// 登陆成功
}
})
但是这两种方法,要么有运行冗余外加执行时机滞后的问题,要么学习成本高,不好。
本小节的rel='opener'
就可以精确又轻松地实现上述需求:
html
<a href="/" target="_blank" rel="opener">登录</a>
在登录成功后,执行:
javascript
window.opener.location.reload()
但是,这样会有安全隐患。window对象的权限太高了,不仅挂载在window对象上的属性和方法可以被执行,任何document对象的属性和方法也可以被执行,因为document对象可以通过window.document访问。
有可能出现被恶意修改主页面内容,或被人通过window.opener.document.cookie
获取用户的敏感信息并进行身份伪造等。
作者写了一个页面来演示这种情况:https://www.htmlapi.cn/2/1-1.html
在2021年以前,很多浏览器针对新窗口打开的链接,都是默认放开window.opener
的,即,如有链接如下:
html
<a href="https://www.aaa.com" target="_blank">登录</a>
那么,此网站www.aaa.com可以通过window.opener
对来源页面为所欲为,无论是跳转到恶意网站、注入恶意信息、伪造用户身份,都是有可能的。
因此,那时有一个技术常识,设置了target="blank"
的外站一定要设置rel="noopener"
,这样,外站页面的window.operer
就是null
,也就没有安全隐患了。
不过,从2020年开始,许多浏览器设置target="blank"的链接元素默认不再是operer,而是noopener。但是,我们还是要手动设置rel="noopener"
,以防万一。
对于上述场景的需求,让我们看看gpt怎么说:
- 在主页面中,创建一个链接,点击该链接会在新窗口中打开登录页面:
html
<a href="/login" target="_blank" rel="noopener noreferrer">登录</a>
- 在新窗口的登录成功后的处理逻辑中,添加一行代码,用于刷新主页面:
javascript
if (window.opener) {
window.opener.location.reload();
}
这段代码检查window.opener是否存在,如果存在(即当前窗口是由另一个窗口打开的),则刷新打开当前窗口的窗口。
请注意,出于安全原因,你应该总是在使用target="_blank"时添加rel="noopener noreferrer"属性。这可以防止新窗口通过window.opener修改打开它的窗口的location,并防止Referer Header泄露信息。
rel="noreferrer"
首先要了解document.referrer
这个API。
document.referrer
可以返回当前页面的来源地址(当前页面的上一个页面)。用户可以直接在浏览器的地址栏输入URL地址进行访问。若通过设置了rel='noreferrer'
的链接元素访问,那么document.referrer就是空字符串。
如edge首页:
如我从内容管理访问我自己的文章:
所有知名的网站分析工具,如百度统计和谷歌分析,都是通过document.referrer
来判断多少 PV(页面访问量) 来自搜索引擎,多少PV来自社交媒体,以及多少PV来自直接访问的。
我们可以借用document.referrer
来完成一些开发任务,与细节体验相关。有两个例子。
例一:
在移动端开发中,页面左上角往往会有一个"返回"按钮,但如果用户是通过点击某个分享链接进入的,那么这个返回的逻辑就不对,因为并没有上一页,此时这个"返回"按钮显示为"主页"按钮更合适。此时,我们就可以使用document.referrer来优化此细节,如果document.referrer是空字符串,则点击左上角的按钮会回到首页;如果不是,则会返回上一页。
例二:
在某列表页面,点击任意列表会进入详情页,然后希望再次返回(通过页面内链接,而非浏览器的"后退"按钮)到列表页的时候,页面依然定位在之前的滚动位置,但如果是从其他页面进入的,则滚动置顶。
可以通过在路由里加参数实现,若从列表项返回则:
html
<a href='/list.html?from=detail'>返回列表<a>
若从其他地方返回,则没有此参数:
html
<a href='/list.html?from=detail'>返回列表<a>
但是这样不够优雅,产生了副作用。可以使用浏览器自带的API:
javascript
if(/detail\.html/.test(document.referrer)){
// 为true说明来源页面,即上一个页面是detail.html,即从详情页来的,执行滚动还原
}
例如,如果用户从"http://example.com/detail.html"页面导航到当前页面,那么这个`if`语句将为`true`,并执行其内部的代码。如果用户从"http://example.com/otherpage.html"或直接在浏览器中输入URL来访问当前页面,那么这个if语句将为false,并跳过其内部的代码。
作者提供的例子:https://www.htmlapi.cn/2/1-2.html
那么,我们要在什么时候给链接设置rel="noreferrer"
?
- 自己产品的链接都不要设置:设置了会影响用户访问路径的追踪,进而影响页面的流失率等数据的统计
- 外站链接,作者建议都设置:URL中含有大量信息,甚至有隐私内容,如搜索结果落地页的URL会包含搜索关键词,最好不要泄露
- 若产品是社交媒体,可以不设置:如微博、知乎等。这种产品以社交为主,更看重信息传播。暴露referrer信息有助于第三方网站溯源,间接增加访问量和热度。
(如上述截图,CSDN也没有设置)
注意,我们会经常看见如下代码,即noopener和noreferrer
同时设置:
javascript
<a href='xxxx' target="_blank" rel="noopener noreferrer">外部网站<a>
但是,社交媒体就不担心隐私泄露了吗?实际上,rel="noreferrer"
的确存在不足,其设置与否的效果就像是0和1,并没有折中的说法,也就是说,无法兼顾信息传播和隐私保护。
于是,2016年,各大浏览器开发商陆续支持一个新的HTML属性,referrerpolicy
,可以兼顾信息传播和隐私保护,之后会讲。
还有一种少见的场景:
链接地址直接是外部图片,而这个图片设置了防外链,此时,给链接元素设置rel="noreferrer"有可能让图片可以访问。如:https://www.htmlapi.cn/2/1-3.html
原因:
防止热链的策略通常是通过检查HTTP的Referer头来实现的。当你从一个网页访问另一个网页时,你的浏览器会发送一个Referer头,告诉服务器你是从哪个页面跳转过来的。如果服务器发现Referer头不是它期望的值(例如,不是同一域名下的页面),那么它可能会拒绝提供服务,例如不返回图片内容。
rel="noreferrer"属性会告诉浏览器在请求资源时不要发送Referer头。 这样,即使图片的服务器设置了防止热链的策略,也无法检查Referer头,因此可能会正常提供服务。
对于作者例子https://www.htmlapi.cn/2/1-3.html,若图片被浏览器缓存了,点第一个链接也可以访问到图片(点完第二个再点第一个就可以访问了,因为缓存了)。
relList对象
对于rel="noopener noreferrer"
,我们发现rel属性支持使用空格分隔多个值。相似地,class也可以。
class可以用classList.remove移除某个类。rel也可以:使用relList.remove移除某个rel。
它们有专门的接口interface:DOMTokenList 。
DOMTokenList - Web API | MDN (mozilla.org)
rel属性要比很多人预想的要强大得多,具有其他HTML属性所没有的、独立的API。