你真的了解 <a> 标签吗?

1,986 阅读12分钟

前言

创作本篇文章的起因如下:在某次开发中,一个表格的操作部分需要在新窗口打开查看数据详情的页面,需求很简单也很容易实现,一行代码 `<a target="_blank">查看</a>` 就可以了,但是因为粗心的缘故我把代码写成了 `<a target="__blank">查看</a>`,不知道你有没有看出端倪,没错,就是多写了一个下划线,其实也很难看出来,并且正常的通过了测试,为啥能通过测试呢?看看下面的效果:

2021-08-29 18.04.12.gif

从上图可以看出来,虽然代码写错了,但是依然可以从新窗口打开链接,所以通过了自测和测试环节。但是这段代码真的没有任何问题吗?大家一起再仔细看看下面的截图:

2021-08-29 18.18.37.gif

相信小伙伴们已经发现端倪,这段代码造成的效果是:第一次是在新窗口打开链接,之后每一次都是在第一次打开的窗口内更新页面,并没有继续的在一个新窗口打开。

此时此刻笔者内心的想法就是 有意思 的同时又不得不感叹前端知识体系的 "杂",一个小小的 <a> 标签居然给我带来了这么多疑惑和探寻的欲望,并且仔细思考回顾自身的知识体系会发现其实很多知识点其实只知道基本的使用方法并没有很深入的去研究学习,因此才有了本篇文章。

本文以这个小 Bug 为契机,希望大家跟着笔者一起重新学习一下这个在日常开发中已经用的滚瓜烂熟熟悉的不能再熟悉的 <a> 标签的更多用法,也许会令你大开眼界哦~

熟悉的 <a> 标签

深入介绍 a 标签之前,笔者来带大家重新回顾一下大家印象里的 <a> 标签应该是做什么工作的。

1 - 打开链接

小伙伴第一印象 <a> 标签做的不就是打开链接的操作嘛,日常开发中开发者能使用到的 90% 就是这个功能。

<!-- 下面这段代码,将会在当前窗口打开掘金 -->
<a href="https://juejin.cn">打开掘金</a>

2 - 新窗口打开链接

对于熟练使用 <a> 标签的同学来说,新窗口打开链接那不就是伸手就来。

<!-- 下面这段代码,将会在新窗口打开掘金 -->
<a href="https://juejin.cn" target="_blank">打开掘金</a>

3 - 链接到本页的某个部分

<!-- 下面这段代码打开超链接的同时链接到页面某个位置 -->
<a href="https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/a#%E9%93%BE%E6%8E%A5%E5%88%B0%E6%9C%AC%E9%A1%B5%E7%9A%84%E6%9F%90%E4%B8%AA%E9%83%A8%E5%88%86">连接到指定位置</a>

效果如下图所示:

2021-08-30 20.38.43.gif

打开链接页面的同时,会自动定位到链接后面拼接的 #location 对应位置。

不熟悉的 <a> 标签

上面介绍了平时大家开发过程中较为熟悉的 <a> 标签几点用法,接下来一一对照来介绍一下你可能不怎么熟悉的 <a> 标签相关内容。

1 - 只能打开链接?

前面提到了日常开发中 <a> 标签可能就是用来打开网络连接了,那么除了网络连接之外其实 <a> 标签还能帮我们做很多事情。

  • 发邮件
<!-- 下面这段代码,将会打开本机邮件服务 -->
<a href="mailto:zhangsan@126.com">给张三发邮件</a>

2021-08-29 19.16.30.gif

  • 打电话
<!-- 下面这段代码,在 H5 里将会调用打电话的功能 -->
<a href="tel:+13000001111">给李四打电话</a>

2021-08-29 19.20.54.gif

如果是在手机上点击这个链接,就会直接调用你的本机拨号功能,感兴趣的可以去尝试一下。

  • 下载网络资源

<a> 标签除了上述功能外,还有一个非常重要的功能,那就是下载网络资源,这个功能是 HTML5 最新提出的,并且目前来看可应用场景还是非常广泛且好用的。

<!-- 下面这段代码,会直接通过浏览器下载一个网络图片 -->
<a href='imgurl' download>下载图片</a>

2021-08-29 19.28.47.gif

官方还有一个 Demo,使用 a 标签下载页面上 canvas 画布的内容,链接在此

关于 <a> 标签下载更多内容,笔者之前写过一篇文章,所以更详细的可以去那篇文章去看 这应该是你见过的最全前端下载总结

2 - 只能在新窗口打开链接?

上面熟悉的第二点是新窗口打开链接,那么 <a> 标签只能在新窗口打开链接吗?所谓的新窗口到底是什么?上面出现的 Bug 又是怎么回事呢?所有详细内容都会在这里进行介绍。

image.png

先来看看上面的图片,在 VSCode 工程里写一个 <a> 标签,然后写 target 属性的时候,编辑器智能给出了四个提示,说明 target 属性是有很多取值的,并不是只有一个大家日常使用的 _blank,那么就来说说这四个属性分别对应什么意思。

  • _slef

这个其实就是在当前窗口打开超链接,并且这个是 target 属性的默认值,所以如下两段代码是等价的。

<!-- 下面两段代码等价 -->
<a href="https://juejin.cn">打开掘金</a>
<a href="https://juejin.cn" target="_self">打开掘金</a>
  • _blank

在一个新的且未命名的窗口打开超链接,这里有点意思,主要强调的不仅仅是新窗口,还强调了未命名,其实每一个浏览器窗口都是一个对象,每个窗口都可以拥有自己的名字。

<!-- 下面这段代码在一个未命名的新窗口打开超链接 -->
<a href="https://juejin.cn" target="_blank">打开掘金</a>

既然有未命名,那么就有指定的名字,别着急,下面会介绍到。

  • _parent

加载响应到当前框架的 HTML4 父框架或当前的 HTML5 浏览上下文的父浏览上下文。如果没有 parent 框架或者浏览上下文,此选项的行为方式与 _self 相同。

上面的官方解释不是很容易理解,简单来说一下,什么是父窗口,其实这里是针对 iframe 而言,来看一个例子:

2021-08-30 20.22.39.gif

从上图可以看得非常清晰,页面里嵌套了一个 iframe,然后 iframe 里有下面这段代码:

<!-- 下面这段代码在父窗口窗口打开超链接 -->
<a href='/user/list' target='_parent'>在父窗口打开链接</a>

点击后,当前包含 iframe 的窗口(也就是父窗口)就进行了跳转,这也就是 iframe 操作父窗口的一种方式。

  • _top

这个属性其实还是和 ifarme 相关,可以理解为最外层没有 parent 窗口的那个窗口就是 _top 窗口,如果页面没有 iframe,此选项的行为方式与 _self 相同。

  • targetOpenerName —— 笔者自定义的

这个在 MDN 文档里没有,纯属个人总结,但是却又非常简单的可以理解,什么意思呢?上面提到过,_blank 是在一个未命名的新窗口打开超链接,那么 targetOpenerName 的意思就是:

1 - 如果没有一个名称为 targetOpenerName 的窗口,那么在新窗口打开超链接,并且把该窗口命名为 targetOpenerName。

2 - 如果有名称为 targetOpenerName 的窗口,那么在此窗口打开超链接。

经过上面的解释,文章开头的那个 Bug,是不是一下就清晰了,出现问题的代码是 target="__blank",虽然和 _blank 很像,但是并不是,因此新开的窗口命名为 __blank,所以之后所有超链接都是在名称为 __blank 的窗口打开超链接。

这里小感慨一下,如果不深入探寻,那么可能自身只停留在解决了一个 Bug 的层面,为什么会出现这个 Bug 可能与纳音也会被轻易归咎为粗心大意写错了,其实里面大有文章,希望大家平时也能认认真真对待每一个小问题,小 Bug,因为它有可能会让你知识体系更牢固。

3 - 链接到本页的某部分应用场景

上面介绍了,<a> 标签可以实现链接到本页某个位置的能力,那么其实利用这个能力,日常开发中就能做一些操作,比如浏览文章浏览到一半退出了,希望第二次进入还能在原来附近的位置进行阅读,其中一种办法就可以是:

  • 浏览到对应标题链接的时候,把链接存入 localStorage

  • 每次重新进入页面读取缓存值,如果存在,那么连接到对应位置。

<a> 其他属性

除了上面介绍的一些基本知识和对应的扩展知识外,<a> 标签还有很多其他属性,既然是深入探寻 <a> 标签系列,那么也简单介绍一下。

rel

这个属性算是除了上面介绍的常用的属性之外,最常见到以及可能会用到的一个属性了,该属性指定了目标对象到链接对象的关系,这个属性在当前窗口与目标窗口之间建立了联系。关于 rel 的取值有很多种,在这里主要着重介绍两个:noopenernoreferrer

  • noopener

指示浏览器打开链接而不授予新的浏览上下文对打开它的文档的访问权限-通过在打开的窗口中不设置Window.opener属性(返回null)。

上面是官方解释,大家不理解也没问题,用一个例子来帮助大家理解一下:

2021-08-30 22.53.37.gif

2021-08-30 22.54.16.gif

从上面的两张截图大家发没发现细节,新开的窗口可以通过 window.opener 拿到一个 window 对象,而调用这个 window 对象的链接之后发现,居然能在新窗口改变原来窗口的链接,那么既然是拿到了一个完整的 window 对象,所以除了跳转可能还能干很多事情,因此这个属性就显得不是那么的安全,容易被恶意开发者利用制造漏洞,所以通常来说避免这种情况,在使用 <a> 标签的时候,如果打开新窗口,非必要的情况下都建议大家使用如下代码:

<!-- 下面这段代码是一个安全的打开新窗口的代码 -->
<a href='/user/list' target='_blank' rel="noopener">在新窗口安全打开链接</a>
  • noreferrer

阻止浏览器导航到另一个页面时,通过 Referer:HTTP header 将该页面地址或任何其他值作为 Referrer 发送。

那么这个属性具体有啥作用呢?顾名思义,它通过从 HTTP 头中删除引用信息来防止将引用者信息传递到目标网站。那么比如,你的网站通过 <a> 标签链接到了掘金,那么在谷歌分析中,掘金就会有一次你的网站的引用,也就是导流,当然前提是进行了导流之类的合作。如果你希望打开的链接就是一次直接流量而不是引用流量,那么就可以使用 rel="noreferrer"

没有使用 rel="noreferrer" 的 Request Header:

image.png

使用了 rel="noreferrer" 的 Request Header:

image.png

还有一点很重要,如果你做的是内网需求,或者内部需求,并且使用了 GA 作为分析工具,那么互相跳转尽量别用这个属性,因为它有可能会打乱你的分析报告。

关于 <a> 标签的相关开发规范,eslint 也有相应的配置,具体地址参考 jsx-no-target-blank

ping

这个功能就高级了,也类似于引流日志打点记录之类的功能,官方描述是这样的。

包含一个以空格分隔的url列表,当跟随超链接时,将由浏览器(在后台)发送带有正文 PING 的 POST 请求。通常用于跟踪。

什么意思,还是不太理解啊,没事再来一个例子看看:

<a href='/user/list' target='_blank' ping='http://localhost:3006/api/ping'>新窗口打开页面</a>

这段代码的意思就是,当点击链接的时候,会同步发送一个 POST 请求到 ping 属性对应的链接上,请求体内容为:PING,实际操作一下看看截图就知道了。

2021-08-30 23.29.06.gif

因为我没有设置对应的接收数据的接口,所以是 404,但是!!!请求确确实实的发出来了,因此这个属性也提供了一种思路,那就是链接类需求,我们其实可以通过 <a> 标签的 ping 属性来进行跳转打点记录。

hreflang

这个属性有点意思,官方介绍:该属性用于指定链接文档的人类语言。其仅提供建议,并没有内置的功能。hreflang 允许的值取决于HTML5 BCP47

这个人类语言就精髓了,大体上就是指定超链接的文档语言是什么,并且官方也说了,是一个建议,不具备法律效应,啥意思呢?就是说你指定了跳转过去的时候是英文,但是打开之后发现是俄语,人家也不负责,渣男!!!

type

该属性指定在一个 MIME type 链接目标的形式的媒体类型。其仅提供建议,并没有内置的功能。

同样,官方说了,只提供建议,不负责,因此也不过多介绍

其他属性

关于 <a> 标签其实还有一些其他属性,但是要不就是已废弃,要不就是在试验中,可用性场景真的非常少,所以就不过多介绍了。可以去官方文档里学习一下。

总结

本篇文章从一次实际案例(Bug)为切入点,对日常开发中最常用的 <a> 标签学习之后进行思考总结,感觉有了一些重学前端的意味,仔细想一下确实很多基础知识点(无论是 HTML/CSS/JavaScript)在日常开发过程中都经常使用,但是如果仔细去读 MDN 文档你会发现不论多么小的一个知识点都有那么多内容或者是属性方法是我们不知道的,因为平时开发中可能只用到了这个知识点最常用的 10%~20% 的基本功能,所以任重而道远啊。

希望大家在读本篇文章的时候能跟着我的视角一起去重新学习一下 <a> 标签,后面如果有机会依然会写类似的文章,当然如果小伙伴们感兴趣也可以私信/评论你日常开发过程中遇到的类似的问题或者直接投稿,个人觉得借此机会正好是一个深入回顾前端基础知识的契机。

我是前端周同学,一个非专业前端脱口秀爱好者,感兴趣的可以加我的微信/公众号/视频号,交流合作~