a 标签想在新窗口打开?只写 target="_blank" 还差点意思

发布于
2

写页面的时候,”点链接别把当前页顶掉”几乎是个固定需求。多数人的第一反应是给 a 标签补一个 target=”_blank”,刷新一看,确实新开了标签页,于是收工。我以前也这么干,直到有一次排查一个诡异的跳转问题,才发现这一行只做了一半的事。

完整的写法长这样

新窗口打开不是加一个属性就完事,标准姿势是三个属性一起上:

<a href="https://example.com"
   target="_blank"
   rel="noopener noreferrer">示例站点</a>

这三个属性各管一摊:href 决定去哪,target 决定在哪打开,rel 决定新旧两个页面之间还剩多少关系。少写 rel,前面两个照样生效,页面表现上看不出任何区别——问题恰恰就藏在这种”看不出区别”里。

上面是推荐写法,下面那种只写 target 的,是典型的反面例子

不写 rel,会留下一个后门

浏览器为了兼容老页面,凡是用 target=”_blank” 打开的页面,里面的 window.opener 都会保留一个指向原页面的引用。也就是说,新页面拿着这个句柄,能反过来改你的地址栏、读你的 DOM,甚至用死循环把你的主线程拖死。

这就是常说的反向标签劫持(tabnabbing):用户从你的站跳出去,落到某个看起来很正常的页面,那个页面悄悄把原标签换成仿冒的登录页。用户切回来,账号密码就交出去了。整个过程标签图标和标题都还在,普通用户根本察觉不到。

noopener 和 noreferrer 到底谁在干活

noopener:把新页面的 window.opener 置成 null,句柄直接断掉。解决上面的问题,靠的就是它。

noreferrer:跳转时不发送 Referer 请求头,目标站点看不到用户是从哪个页面来的,属于隐私保护;顺带一提,在一些不支持 noopener 的老浏览器里,它也能起到断开页面关联的作用。

两个一起写,理由很实在:noopener 是主防护,noreferrer 负责补位和隐私,彼此不冲突,代价也就是十几个字符。新版本的 Chrome、Firefox、Safari 虽然已经默认把 target=”_blank” 当成 noopener 处理,但你的代码终究要跑在别人那台未必更新的浏览器上,显式写上永远不亏。

JS 里动态拼链接,最容易把 rel 拼丢

用字符串拼 HTML 是丢属性的重灾区,尤其是图省事写成下面这样:

el.innerHTML += '<a href="https://example.com" target="_blank">站点</a>';

拼的时候少写一项没人提醒你,跑起来也不报错。动态创建链接老老实实用 DOM API,一个属性一次赋值:

const link = document.createElement('a');
link.href = 'https://example.com';
link.target = '_blank';
link.rel = 'noopener noreferrer';
link.textContent = '示例站点';
console.log(link.outerHTML);   // 上线前瞄一眼,三项都在
document.body.appendChild(link);

那行 console.log 不是凑数,outerHTML 打出来扫一眼,href、target、rel 齐不齐一目了然,比事后靠经验回忆靠谱得多。

整站外链要统一处理

站点里外链一多,逐个改不现实,批量刷一遍更省事:

document.querySelectorAll('a[href^="http"]:not([href*="yoursite.com"])')
  .forEach(a => {
    if (a.target === '_blank') {
      a.setAttribute('rel', 'noopener noreferrer');
    }
  });

这里有个小坑:setAttribute 会覆盖掉元素上原有的 rel。如果链接本来就带着 nofollow 之类,记得一并带上,写成 “noopener noreferrer nofollow”,别把 SEO 那边的设置给冲了。

这条捷径别走

在 head 里写一行 ,所有没显式写 target 的链接都改成新窗口打开,看着相当省事。可它只管得了 target,管不了 rel,安全风险一个没少;更要命的是它是全局生效的,导航、锚点、表单提交全都跟着变,想让某个链接走当前窗口,还得单独写 target=”_self” 往回扳。省了几行代码,换回一堆例外,不划算。

几种写法

写法

实际效果

结论

只写 target=”_blank”

新窗口打开,新页面仍持有 opener

不合格,必须补 rel

target + rel=”noopener”

新窗口打开,句柄断开

可用

target + rel=”noopener noreferrer”

新窗口打开,句柄断开且不传 Referer

推荐

全站链接新开,rel 无法逐个配置

不推荐

上线前的自查清单

  • 全站搜一遍 target=”_blank”,确认每个旁边都跟着 rel。

  • 用 JS 生成的链接,检查是不是还在拼字符串。

  • 后台编辑器、富文本里存进去的外链,也要过一遍这道流程。

  • 提交前用 outerHTML 或 DevTools 抽查两三个真实链接,别只看源码。

  • 顺手确认目标链接是否真的需要新开:同站内跳转留在本页,对用户体验更友好。

0 / 600
0 讨论
热门最新
总结
暂无总结
嗨,下午好!
所有的成功,都源自一个勇敢的开始

暂无福利券