码桶

发现社区成员的开源项目

main
content.js2.5 KB
/*!
 * 超链新标签页打开 (Open Links in New Tab)
 * 强制所有超链接在新标签页中打开。
 *
 * 作者:白逸 | 官网:https://imxcy.cn
 * Copyright (c) 白逸. All rights reserved.
 */
(function () {
  "use strict";

  var STORAGE_KEY = "enabled";
  var enabled = true;

  // 读取初始开关状态(默认开启)
  chrome.storage.local.get({ enabled: true }, function (item) {
    enabled = item.enabled;
  });

  // 开关状态变化时实时同步
  chrome.storage.onChanged.addListener(function (changes, area) {
    if (area === "local" && changes[STORAGE_KEY]) {
      enabled = changes[STORAGE_KEY].newValue;
    }
  });

  // 在事件路径中向上查找最近的 <a> 元素,兼容 Shadow DOM 与 <svg> 内的链接
  function findAnchor(event) {
    var path = typeof event.composedPath === "function" ? event.composedPath() : null;
    if (path) {
      for (var i = 0; i < path.length; i++) {
        var node = path[i];
        if (node && node.nodeType === 1) {
          var tag = node.tagName;
          if (tag === "A" || tag === "a") return node;
        }
      }
      return null;
    }
    // 回退:无 composedPath 时使用 closest
    var target = event.target;
    return target && target.closest ? target.closest("a") : null;
  }

  // 判断链接是否应被跳过(保持浏览器默认行为)
  function shouldSkip(link, href) {
    if (!href) return true;
    if (href.charAt(0) === "#") return true; // 页内锚点
    if (/^\s*(javascript|mailto|tel|sms|blob|data):/i.test(href)) return true;
    if (link.hasAttribute("download")) return true; // 下载链接
    if (link.target === "_blank") return true; // 已是新标签页
    return false;
  }

  document.addEventListener(
    "click",
    function (e) {
      if (!enabled) return;

      // 仅处理鼠标左键,且不干扰用户的组合键操作
      if (e.button !== 0) return;
      if (e.ctrlKey || e.metaKey || e.shiftKey || e.altKey) return;
      if (e.defaultPrevented) return; // 页面脚本已自行处理

      var link = findAnchor(e);
      if (!link) return;

      var href = link.getAttribute("href");
      if (shouldSkip(link, href)) return;

      var url = link.href; // 使用解析后的绝对地址
      if (!url) return;

      e.preventDefault();
      e.stopPropagation();

      // noopener/noreferrer:避免新页面通过 window.opener 反向操作原页面
      window.open(url, "_blank", "noopener,noreferrer");
    },
    true
  );
})();