【问题标题】:Actually do default after preventing default防止默认后实际做默认
【发布时间】:2018-09-01 23:40:53
【问题描述】:

我想阻止链接的默认行为 (a) 然后默认行为,比如说在新窗口中打开一个链接。

这是一些 HTML 代码:

<a href="somewhere" target="_blank" id="mylink">

还有JS代码:

document.getElementById('mylink').addEventListener('click', function (e) {
    e.preventDefault();
    axios.post('options', new FormData(document.querySelector('#myform')))
         .then(function(){ 
             // Here I want to do what the link should have done!
         });
});

我知道我可以这样做:

window.open(e.target.href);

但这不是一个选项,因为浏览器认为这是一个弹出窗口。而且我不想在 JS 中重写一些东西,只是像往常一样考虑链接:这个链接必须执行其默认行为(这是被阻止的)。

有没有办法做到这一点?

【问题讨论】:

  • window.location = somewhere 呢?
  • 您可以通过设置和检查标志来重新触发链接的点击事件。
  • @Doug:不,这不是 HTML 应该做的:HTML(如果没有阻止默认设置)应该在新窗口中打开链接
  • 这似乎有点像 XY 问题?如果您不打算执行链接,除非 ajax 事件成功(数据可用),那么似乎链接可能不是触发请求开始的地方。您的 ajax 调用似乎没有使用链接中的任何内容,并且没有什么可以阻止在加载页面时进行 ajax 调用以启用或禁用链接开始。您似乎也在尝试解决用户偏好。正如你自己所说的windows.open 会起作用,但前提是它没有打开弹出窗口,..这是用户偏好。

标签: javascript html


【解决方案1】:

这里有一些想法:

var openingPopup = false;
document.getElementById('mylink').addEventListener('click', function (e) {
    if(!openingPopup){
        e.preventDefault();
        axios.post('options', new FormData(document.querySelector('#myform')))
            .then(function(){ 
                // make sure this would not run twice
                openingPopup = true;
                document.getElementById('mylink').click();
            });
    }else{
        // skipping this only for one time
        openingPopup = false; 
    }
});

这样,

  1. 您运行一次弹出式打开器单击处理程序,
  2. 然后阻止其他人,
  3. 再次手动触发点击事件,
  4. 这次什么都不做,但让其他人跑。

【讨论】:

    【解决方案2】:

    根据@Electrox-Qui-Mortem 的建议链接,完成预处理后,您可以移除事件侦听器并再次调用点击。

    (相关MDN链接:https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener

    const anchor = document.getElementById('anchor');
    
    anchor.addEventListener('click', test);
    
    function test(e){
      e.preventDefault();
      alert('test');
      if( true ){
        console.log( this );
        this.removeEventListener('click', test);
        this.click();
      }
    }
    &lt;a id="anchor" href="https://www.google.com"&gt;Test&lt;/a&gt;

    它在代码测试工具(如 JSfiddle 和 CodePen)中的性能很不稳定——您必须在实际应用程序中对其进行测试,以确保它是否适合您的用例。

    在将外部函数引用为“侦听器”函数时,我只使用 removeEventListener 获得了良好的结果。在这种情况下test()

    【讨论】:

    • 好的,谢谢您的回答。它不能作为例外工作:它被视为不需要的弹出窗口。所以这不是相同的行为:链接(没有 JS)在没有浏览器提示弹出的情况下工作,但是通过触发的点击,它被认为是不需要的弹出窗口。 (虽然不确定我是否完全清楚)
    • @rap-2-h 这必须被认为是不受欢迎的弹出窗口 - 如果不是,那么阴暗的广告商将使用相同的方法让他们的弹出窗口过去你的弹出窗口拦截器。
    【解决方案3】:

    以更“原生”的方式,您可以创建一个不可取消的新“点击”事件并针对同一元素触发它。

    var anchor = document.querySelector('#target');
    
    function triggerClick(target) {
      var newClick = new MouseEvent('click', {
        view: window,
        bubbles: true,
        cancelable: false
      });
      
      target.dispatchEvent(newClick);
    }
    
    anchor.addEventListener('click', function(e) {
      e.preventDefault();
      
      if(e.defaultPrevented) {
        alert('Prevented!');
        triggerClick(anchor);
      }
      else {
        alert('Not prevented');
      }
    });
    &lt;a href="https://google.com" target="_blank" id="target"&gt;Click me!&lt;/a&gt;

    这里的关键是在函数triggerClick(target)中创建的新事件中的cancelable: false,它绕过了e.preventDefault()

    在 * 上的嵌入式示例中它不起作用,但 here's a JSFiddle!

    【讨论】:

    • 谢谢,它可以工作,但是如果我的 triggerClick 函数在回调中(就像我的问题一样),它就不起作用。它显示一个弹出信息(弹出被阻止,blablabla)
    • @rap-2-h 你确定你没有做其他可能干扰回调的事情吗?我在Fiddle 中尝试了一个 HTTP 请求,它似乎对我有用。