### [a 标签想在新窗口打开?只写 target=”_blank” 还差点意思](https://www.huociguo.com/article/1440) **Published:** 2026-09-26T09:23:41 **Author:** 米了 **Excerpt:** 写页面的时候,"点链接别把当前页顶掉"几乎是个固定需求。多数人的第一反应是给 a 标签补一个 target="… 写页面的时候,”点链接别把当前页顶掉”几乎是个固定需求。多数人的第一反应是给 a 标签补一个 target=”\_blank”,刷新一看,确实新开了标签页,于是收工。我以前也这么干,直到有一次排查一个诡异的跳转问题,才发现这一行只做了一半的事。 ## **完整的写法长这样** 新窗口打开不是加一个属性就完事,标准姿势是三个属性一起上: ``` 示例站点 ``` 这三个属性各管一摊:href 决定去哪,target 决定在哪打开,rel 决定新旧两个页面之间还剩多少关系。少写 rel,前面两个照样生效,页面表现上看不出任何区别——问题恰恰就藏在这种”看不出区别”里。 ![](https://api.huociguo.com/wp-content/uploads/2026/09/%E4%B8%8B%E8%BD%BD-2609177db2-1.png "下载-2609177db2-1") 上面是推荐写法,下面那种只写 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 += '站点'; ``` 拼的时候少写一项没人提醒你,跑起来也不报错。动态创建链接老老实实用 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 抽查两三个真实链接,别只看源码。 - 顺手确认目标链接是否真的需要新开:同站内跳转留在本页,对用户体验更友好。 **Categories:** HTML5 ---