当超链接行为由例如虚拟创建时,如何防止打开新选项卡或窗口? HTML 按钮的“点击”事件处理?

Posted

技术标签:

【中文标题】当超链接行为由例如虚拟创建时,如何防止打开新选项卡或窗口? HTML 按钮的“点击”事件处理?【英文标题】:How does one prevent the opening of a new tab or window when hyperlink behavior was created virtually by e.g. an HTML button's "click" event handling? 【发布时间】:2021-11-11 12:15:50 【问题描述】:

我可以通过删除target 属性来禁用在<a> 标签的情况下打开新选项卡或窗口,例如target='_blank'

在某些情况下,而不是 <a> 网站放置 <button> 并添加 onClick()submit() 等以编程方式生成链接并设置 target 属性。

<button id="653cde4c-1620-11ec-85b5-5ae26c154b46">
    <div>click here</div>
</button>

在一些webpack 生成的javascript 文件中会有类似的东西不容易找到和阅读缩小的代码。

var button = document.querySelctor('653cde4c-1620-11ec-85b5-5ae26c154b46')
// Add whatever you want to add to this button element, e.g. add onClick() or submit() or something else which I am not sure of

我们可以在https://i.stack.imgur.com/8oG5w.png看到图片

&lt;button&gt; 提供链接点击功能的情况下。我无法查看和编辑 onClick()submit() 等,因为它的 webpack 生成了 javascript。我只能在 devtools console 中加载该 webapge 后运行我的 javascript 代码。

在这种情况下如何禁用打开新标签页或浏览器新窗口?或者我应该运行什么 javascript 代码来覆盖 &lt;button&gt; 链接行为?

【问题讨论】:

return false 在事件处理程序中? 查看link。 我认为 OP 想要禁用打开新标签/窗口,而不是阻止点击 @DominikMatis 是否至少有任何模式(例如具有至少一个特定属性的特定元素)可以识别这样的触发器/这样的元素? @PeterSeliger:我不确定,我添加了截图和编辑问题 【参考方案1】:

根据基于 webpack 的代码如何管理资源的选项卡处理,一种可能的解决方案是将自己的代码包装在 window.open 周围,甚至完全用自己的实现替换它。

至于包装,根据截获的数据,可以决定是否完全抑制 url 处理,或者不打开新标签/窗口,而是转发到 location.href,甚至继续调用原window.open

// untouchable 3rd party code
//
function alienLocationHandler( currentTarget ) 
  let  url, target  = currentTarget.dataset;

  url = (url ?? '').trim();
  if (url) 

    target = (target ?? '').trim();
    if (target) 

      // due to SO's permission handling ...
      //
      // ... Blocked opening 'https://***.com/' in a
      // new window because the request was made in a sandboxed
      // frame whose 'allow-popups' permission is not set.
      //
      window.open(url, target);
     else 
      window.location.href = url;
    
  

document
  .querySelectorAll('[data-url]')
  .forEach(elmNode =>
    elmNode.addEventListener('click', alienLocationHandler)
  );


// one possible approach :: wrap around `window.open`
//
window.open = (function createAroundHandler(proceed, thisArg) 
  return function aroundWindowOpen(url, target, ...rest) 

    console.log('aroundWindowOpen ...', 
      url,
      target,
      rest,
    );

    // - of cause all the code is just for demonstration purpose.
    //
    // - the OP has to come up with own handler logic which does
    //   fit the OP's needs best.
    
    if (url !== 'https://***.com') 

      // invocation of the original `window.open`
      // will be blocked by SO's permission handling.

      proceed.call(thisArg, url, target, ...rest);

     else 
      // delayed fowarding to `location.href`.

      setTimeout(() =>  window.location.href = url , 5000);
    
  ;
(window.open, window));
body  margin: 0; 
.as-console-wrapper  min-height: 87%; top: auto; 
<button data-url="https://***.com">
  no interception
</button>

<button
  data-url="https://google.com"
  data-target='google'
  >
  intercepted &amp; open attempt  
</button>

<button
  id="_653cde4c-1620-11ec-85b5-5ae26c154b46"
  data-url="https://***.com"
  data-target='_blank'
  >
  intercepted and forwarded with 5sec delay
</button>

另一种方法是通过监听和处理click 事件来利用event delegation,例如document.body 级别。

// untouchable 3rd party code
//
function alienLocationHandler( currentTarget ) 
  let  url, target  = currentTarget.dataset;

  url = (url ?? '').trim();
  if (url) 

    target = (target ?? '').trim();
    if (target) 

      // due to SO's permission handling ...
      //
      // ... Blocked opening 'https://***.com/' in a
      // new window because the request was made in a sandboxed
      // frame whose 'allow-popups' permission is not set.
      //
      window.open(url, target);
     else 
      window.location.href = url;
    
  

document
  .querySelectorAll('[data-url]')
  .forEach(elmNode =>
    elmNode.addEventListener('click', alienLocationHandler)
  );


// another possible approach :: event delegation
//
// - [https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#event_delegation]
// - [https://davidwalsh.name/event-delegate]
//
// - [https://javascript.info/event-delegation]
// - [https://learn.jquery.com/events/event-delegation/]
//
function isButtonEvent(activeElement, targetNode) 
  return (activeElement.tagName.toLowerCase() === 'button') && (
    activeElement.isSameNode(targetNode) ||
    activeElement.contains(targetNode)
  );

function preventSpecificButtonClickBehavior(evt) 
  const elmButton = document.activeElement;

  if (isButtonEvent(elmButton, evt.target)) 
    const url = (elmButton.dataset.url ?? '').trim();

    const isUrlBasedPrevented = (url !== 'https://***.com');
    const isIdBasedPrevented = ((elmButton.id ?? '').trim() !== '');

    if (isUrlBasedPrevented || isIdBasedPrevented) 
      evt.stopImmediatePropagation();
      evt.stopPropagation();

      if (isUrlBasedPrevented) 
        console.log('prevented button click behavior ... URL based')
      
      if (isIdBasedPrevented) 
        console.log('prevented button click behavior ... ID based')
      
    
  

document
  .body
  .addEventListener(
    'click',
    preventSpecificButtonClickBehavior,
    // - [https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#parameters]
    // - The boolean `useCapture` parameter/flag does the trick if set as `true` value.
    true
  );
body  margin: 0; 
.as-console-wrapper  min-height: 87%; top: auto; 
<button data-url="https://***.com">
  click behavior not prevented
</button>

<button
  id="_653cde4c-1620-11ec-85b5-5ae26c154b46"
  data-url="https://***.com"
  >
  prevented behavior (id based)
</button>

<button
  data-url="https://google.com"
  data-target='google'
  >
  <span>
    <span>
      prevented behavior (url based)
    </span>
  </span>
</button>

【讨论】:

@AlokSinghMahor ...关于上述两种方法还有什么问题吗? 非常感谢您的回答。这个答案给了我线索来建立解决我的问题。我对你的答案投了赞成票。你的方法可以帮助我解决我在&lt;button&gt; 中没有data-url 的问题吗?请看截图 @AlokSinghMahor ...我想您谈论的是基于事件委托的方法。 data-* 属性仅用于演示目的,以模仿第三方按钮单击触发的超链接行为。因此,您需要自己弄清楚如何最好地识别事件源,然后必须通过进一步传播事件来停止该事件源。一个可行的识别可能已经是检查按钮的特殊模式id' 与这种按钮的结构相结合,就像总是有一个&lt;div/&gt; 和另一个id 属性嵌入在&lt;button/&gt; 中。 【参考方案2】:

onClick() 中的逻辑可能类似于window.open(url, [params])。您可以将其更改为设置window.location.href = url;,并且URL将在同一窗口中加载。

【讨论】:

我们不知道url的价值 @AlokSinghMahor url 是您点击时要访问的 URL 对于外部网站,我无权查看和编辑 onClick()。加载到 devtools 控制台后,我只能在该 webapge 上运行我的 javascript 代码。

以上是关于当超链接行为由例如虚拟创建时,如何防止打开新选项卡或窗口? HTML 按钮的“点击”事件处理?的主要内容,如果未能解决你的问题,请参考以下文章

单击时如何在新选项卡或(新窗口)中重定向超链接?

Selenium - 单击链接会打开一个新选项卡

关闭选项卡时如何防止调试器停止

如何使表格中的整行像ngtable(angularjs)中的超链接(在新选项卡等中打开)一样

如何防止 IntelliJ 自动关闭我的编辑器选项卡?

如何打开新选项卡并重定向到链接 EXT JS portlet 按钮