返回

Angular 后台打开新标签页的两种方法详解

javascript

Angular JavaScript 后台打开新标签页

用户期望通过链接或按钮触发新标签页打开,而当前激活的页面不失焦。 这意味着,新的标签页在后台打开,不切换用户的焦点。 常见的 window.open 方法在多数浏览器下会将焦点直接转移到新标签页。 Angular 应用中处理此类需求时,需要特定的技巧。

问题根源

浏览器安全策略往往阻止脚本完全控制标签页的行为。直接使用 window.open 通常无法直接实现后台打开标签页。 这是浏览器安全模型为保护用户避免恶意脚本干扰浏览体验所做的限制。 window.blur()iframe 的方法尝试调整焦点,但其行为一致性较差。不同浏览器、不同版本可能导致这些方法无效,这加大了解决此问题的难度。

解决方案一: target="_blank"rel="noopener noreferrer"

最简单、可靠的后台打开新标签页的方式是使用标准的 HTML 锚标签(<a>)。 此方案不需要 Angular 代码进行过多处理。 核心在于 target="_blank"rel="noopener noreferrer" 属性的配合使用。 target="_blank" 指示浏览器在新标签页打开链接; rel="noopener noreferrer" 可以提升安全性,防止新打开的标签页访问原页面的 window 对象,有效防止一些安全漏洞。

实现步骤:

  1. 将 HTML 中的 <strong> 元素替换成 <a> 标签。
  2. <a> 标签中设置 href 属性和 targetrel 属性。
  3. 利用 Angular 的属性绑定或事件绑定来动态生成 href 的 URL。

代码示例:

<td style="width: 27%; padding-left: 20px" nowrap *ngIf="ls.selectedLanguages == 'id'">
  <a
    [href]="getProductUrl(item.id)"
    target="_blank"
    rel="noopener noreferrer"
    class="text-blue"
  >
    {{ item.name }}
  </a>
</td>
import { Component } from '@angular/core';
import { Router } from '@angular/router';

@Component({...})
export class MyComponent {
    constructor(private router: Router){}
    getProductUrl(productId: number) {
        return this.router.serializeUrl(this.router.createUrlTree(['/product', 'detail', productId]));
      }
}

解析:

  • getProductUrl(productId: number)函数动态创建跳转的路由URL
  • target="_blank" 确保在新的标签页中打开链接,而不会覆盖当前的页面。
  • rel="noopener noreferrer" 提供重要的安全保护,建议在新标签页打开的链接上总是使用。

优势:

  • 原生 HTML 支持,不需要 JavaScript 进行过多的操作,降低了兼容性问题。
  • 实现方式简单,开发速度快。

缺陷:

  • 有可能会遇到安全拦截, 需浏览器配合解除相关限制。

解决方案二:window.open() 和短暂的失焦延迟

如果需要更多程序化控制,可以直接使用 JavaScript 的 window.open() 方法, 但需要配合一个非常短的失焦延迟来达到目的,此方法相较方法一稳定性和兼容性更差,使用需谨慎。此方案并非100%可靠,但在某些浏览器环境下能较好的实现目的。

实现步骤:

  1. 调用 window.open() 打开新标签页。
  2. 添加一个延迟,使用 setTimeout() 在较短时间内重新聚焦回原来的窗口。
  3. 注意添加一些兼容性处理,判断newWindow 是否为null, 从而防止后续聚焦操作报错。

代码示例:

import { Component } from '@angular/core';
import { Router } from '@angular/router';

@Component({...})
export class MyComponent {
  constructor(private router: Router) {}
  openProductInNewTab(productId: number) {
    const url = this.router.serializeUrl(
      this.router.createUrlTree(['/product', 'detail', productId])
    );

    const newWindow = window.open(url, '_blank', 'noopener,noreferrer');

      if(newWindow){
         setTimeout(() => {
           window.focus();
        }, 100);
      }
    
  }
}

<td style="width: 27%; padding-left: 20px" nowrap *ngIf="ls.selectedLanguages == 'id'">
<strong (click)="openProductInNewTab(item.id)" class="text-blue">
              {{ item.name }}
            </strong>
          </td>

解析:

  • window.open(url, '_blank', 'noopener,noreferrer'):与原始示例相同,但新增了一个判断以提升兼容性
  • setTimeout(() => { window.focus(); }, 100): 使用延迟来把焦点放回当前标签页。延迟时间可以调整,100 毫秒在实践中效果较好,避免太快切换而产生瞬间闪烁的感觉,但也可能需要根据浏览器环境或硬件调整。

优势:

  • 更直接地控制标签页行为,理论上允许更多自定义操作。

缺陷:

  • 实现依赖 setTimeout() 和焦点切换,无法保证100%成功,各浏览器行为可能不一致,存在兼容性问题。

安全提示

  • 避免信任外部链接: 在 Angular 应用中处理外部 URL 时,使用 rel="noopener noreferrer"至关重要, 这可以避免新的标签页通过 window.opener 来操纵或访问你的应用。

  • 用户反馈: 如果页面需要打开新标签页进行进一步操作,给用户提供明确的反馈和指导,例如弹出提示。这样可以提高用户体验,降低用户的困惑。

综合来说,推荐方案一:使用标准的HTML锚标签及其属性进行实现。此方案是可靠,兼容性最好的实现。方案二,实现相对不稳定,使用时需要仔细测试验证其在各浏览器下的表现。 在实践过程中,应当依据应用的实际情况,选择适合自身需求的解决方案。