77

当用户点击某个链接时,我想向他们展示一个确认对话框。如果他们点击“是”,我想继续原来的导航。一个问题:我的确认对话框是通过返回一个 jQuery.Deferred 对象来实现的,该对象仅在/如果用户单击“是”按钮时才被解析。所以基本上确认对话框是异步的。

所以基本上我想要这样的东西:

$('a.my-link').click(function(e) {
  e.preventDefault(); e.stopPropogation();
  MyApp.confirm("Are you sure you want to navigate away?")
    .done(function() {
      //continue propogation of e
    })
})

当然,我可以设置一个标志并重新触发点击,但这太麻烦了。有什么自然的方法吗?

4

8 回答 8

14

令我惊讶的是,以下是在 Chrome 13 中实际运行的代码中的部分内容。

function handler (evt ) {
    var t = evt.target;
    ...
    setTimeout( function() {
        t.dispatchEvent( evt )
    }, 1000);
    return false;
}

这不是非常跨浏览器,也许将来会修复,因为感觉像是安全风险,恕我直言。

如果您取消事件传播,我不知道会发生什么。

于 2011-10-18T20:19:09.177 回答
4

它可能有风险,但至少在撰写本文时似乎有效,我们正在生产中使用它。

这是 ES6 和 React,我已经测试并发现它适用于以下浏览器。一个好处是如果有一个例外(在制作这个过程中有几个),它会像普通<a>链接一样进入链接,但它不会是 SPA 然后是 ofc。

桌面:

  • 铬 v.76.0.3809.132
  • Safari v.12.1.2
  • 火狐量子 v.69.0.1
  • 边缘 18
  • 边缘 17
  • IE11

手机/平板电脑:

  • Android v.8 三星互联网
  • 安卓 v.8 铬
  • Android v.9 铬
  • iOS11.4 Safari
  • iOS12.1 Safari

.

import 'mdn-polyfills/MouseEvent'; // for IE11
import React, { Component } from 'react';
import { Link } from 'react-router-dom';

class ProductListLink extends Component {
  constructor(props) {
    super(props);
    this.realClick = true;

    this.onProductClick = this.onProductClick.bind(this);
  }

  onProductClick = (e) => {
    const { target, nativeEvent } = e;
    const clonedNativeEvent = new MouseEvent('click', nativeEvent);

    if (!this.realClick) {
      this.realClick = true;
      return;
    }

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

    // @todo what you want before the link is acted on here

    this.realClick = false;
    target.dispatchEvent(clonedNativeEvent);
  };

  render() {
    <Link
      onClick={(e => this.onProductClick(e))}
    >
      Lorem
    </Link>  
  }
}
于 2019-09-20T10:29:12.127 回答
3

我在我的一个项目中通过这种方式解决了问题。此示例适用于一些基本事件处理,如点击等。确认处理程序必须是第一个处理程序绑定。

    // This example assumes clickFunction is first event handled.
    //
    // you have to preserve called function handler to ignore it 
    // when you continue calling.
    //
    // store it in object to preserve function reference     
    var ignoredHandler = {
        fn: false
    };

    // function which will continues processing        
    var go = function(e, el){
        // process href
        var href = $(el).attr('href');
        if (href) {
             window.location = href;
        }

        // process events
        var events = $(el).data('events');

        for (prop in events) {
            if (events.hasOwnProperty(prop)) {
                var event = events[prop];
                $.each(event, function(idx, handler){
                    // do not run for clickFunction
                    if (ignoredHandler.fn != handler.handler) {
                        handler.handler.call(el, e);
                    }
                });
            }
        }
    }

    // click handler
    var clickFunction = function(e){
        e.preventDefault();
        e.stopImmediatePropagation();
        MyApp.confirm("Are you sure you want to navigate away?")
           .done(go.apply(this, e));
    };

    // preserve ignored handler
    ignoredHandler.fn = clickFunction;
    $('.confirmable').click(clickFunction);

    // a little bit longer but it works :)
于 2012-05-17T08:37:38.847 回答
2

如果我正确理解了问题,我认为您可以将事件更新为您在那里的闭包中的原始事件。因此,只需在 .done 函数中设置 e = e.originalEvent 即可。

https://jsfiddle.net/oyetxu54/

MyApp.confirm("confirmation?")
.done(function(){ e = e.originalEvent;})

这是一个不同示例的小提琴(保持控制台打开,以便您可以看到消息):这在 chrome 和 firefox 中对我有用

于 2015-05-11T22:27:00.023 回答
0

我通过以下方式解决了这个问题:

  1. 在父元素上放置事件监听器
  2. 仅当用户确认时才从链接中删除课程
  3. 删除课程后重新单击链接。

function async() {
  var dfd = $.Deferred();
  
  // simulate async
  setTimeout(function () {
    if (confirm('Stackoverflow FTW')) {
      dfd.resolve();
    } else {
      dfd.reject();
    }
  }, 0);
  
  return dfd.promise();
};

$('.container').on('click', '.another-page', function (e) {
  e.stopPropagation();
  e.preventDefault();
  async().done(function () {
    $(e.currentTarget).removeClass('another-page').click();
  });
});

$('body').on('click', function (e) {
  alert('navigating somewhere else woot!')
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div class="container">
  <a href="#" class="another-page">Somewhere else</a>
</div>

我将事件侦听器添加到父级而不是链接本身的原因是因为 jQuery 的on事件将绑定到元素,除非另有说明。因此,即使元素没有类another-page,它仍然附加了事件侦听器,因此您必须利用event delegation它来解决这个问题。

GOTCHAS这是非常基于状态的。即,如果您需要在用户每次点击链接时询问他们,您必须添加第二个侦听器才能将another-page课程重新添加到链接中。IE:

$('body').on('click', function (e) {
  $(e.currentTarget).addClass('another-page');
});

旁注如果用户接受,您也可以删除事件侦听器container,如果这样做,请确保使用namespace事件,因为容器上可能有其他侦听器您可能会无意中删除。有关详细信息,请参阅https://api.jquery.com/event.namespace/ 。

于 2015-05-20T00:27:29.537 回答
0

我们的项目中有类似的要求,这对我有用。在 chrome 和 IE11 中测试。

$('a.my-link').click(function(e) {
  e.preventDefault(); 
  if (do_something === true) {
    e.stopPropogation();
    MyApp.confirm("Are you sure you want to navigate away?")
    .done(function() {
      do_something = false;
      // this allows user to navigate 
      $(e.target).click();
    })
  }

})
于 2017-08-09T10:48:21.637 回答
0

我编辑了你的代码。我添加的新功能:

  1. 为事件添加了命名空间;
  2. 单击元素后,事件将被命名空间删除;
  3. 最后,在“MyApp”部分完成所需操作后,通过触发其他元素“click”事件继续传播。

代码:

$('a.my-link').on("click.myEvent", function(e) {
  var $that = $(this);
  $that.off("click.myEvent");
  e.preventDefault();
  e.stopImmediatePropagation();
  MyApp.confirm("Are you sure you want to navigate away?")
    .done(function() {
        //continue propogation of e
        $that.trigger("click");
    });
});
于 2018-12-14T06:43:15.447 回答
-2

这未经测试,但可以作为您的解决方法

$('a.my-link').click(function(e) {
  e.preventDefault(); e.stopPropogation();
  MyApp.confirm("Are you sure you want to navigate away?")
    .done(function() {
      //continue propogation of e
      $(this).unbind('click').click()
  })
})
于 2011-10-18T18:46:48.190 回答