Marionette ItemView 事件处理与 Bootstrap 下拉菜单冲突

Marionette ItemView event handling conflict with Bootstrap dropdown

我有一个创建 table 的复合视图,其中 childView 集合显示每一行。每行的末尾是一个基于 Bootstrap 的按钮下拉列表。 单击该行将打开一个模态(工作),但单击按钮下拉菜单时仍会触发该事件,因此模态显示并且下拉菜单显示在下方。

如何防止按钮上的点击向下传播到行点击事件,同时又不阻止 Bootstrap 挂钩也被切断?

处理这些类型的特殊点击事件的标准方法(在我的研究和书籍阅读中),在返回的 jquery 事件上使用 e.stopPropagation(),以防止事件进一步发生,但这会阻止 Bootstrap 下拉菜单打开事件的触发。

如果我在单击按钮组时仍然调用 stopPropagation(),然后使用事件对象的 currentTarget 对象并手动切换 class,我可以在没有冲突的情况下打开下拉菜单,但是由于关闭按钮触发器向下 4 层,我会使用蛮力并调用 .parent() 4 次来切换 class,这听起来不太可持续或 portable.

如何使用 Bootstrap 的内置切换按钮进行行单击操作并打开和关闭下拉菜单?

ItemView

Show.Result = Marionette.ItemView.extend({
    tagName:"tr",
    template: "#row-template",
    events:{
        "click .js-delete-result": "editRow",
        "click .js-edit-result": "deleteRow",
        "click div.btn-group button": "openSettings"
        "click": "rowClicked",
    },
    editRow: function(e){e.stopPropagation();alert("you trying to delete bro?!")},
    deleteRow: function(e){e.stopPropagation();alert("you trying to edit bro?!")},
    openSettings: function(e){
            //e.stopPropagation()
            //$(e.currentTarget).parent().toggleClass('open')
            //e.currentTarget.button('toggle');
        },
    rowClicked:function(e){
        e.preventDefault()
        this.trigger("show:result",this)
    },
}

模板

<script id="row-template">
    <td class="vert-align"><%= value %></td>
    <td class="vert-align"><%= notes %></td>
    <td class="vert-align">
      <div class="btn-group">
        <button class="btn btn-xs btn-default dropdown-toggle" aria-haspopup="true"  data-toggle="dropdown" aria-expanded="false">
            <span class="glyphicon glyphicon-edit"/>
        </button>
        <ul class="dropdown-menu" role="menu">
            <li><a href="#" class="js-edit-result">Edit</a></li>
            <li><a href="#" class="js-delete-result"> Delete <i class= "glyphicon glyphicon-trash"/></a></li>
        </ul>
      </div>
    </td>
</script>

随机想法: 也许我以错误的方式思考问题,应该找到一个更好的 CSS / jQuery 选择器覆盖除按钮组以外的所有内容;是否有 'not' 选择器 CSS?

编辑: 我尝试更改 "click": "rowClicked", 通用点击处理程序以使用 'not' css 选择器; "click :not(div.btn-group *)": "rowClicked",但没有任何作用;单击按钮时它仍会打开模型。经过更多研究,我意识到我的非选择器没有正确格式化/使用,但这可能很大,因为我试图以一种不适合的方式使用它。

编辑 2: 我最终能够(某种程度上)阻止传播,但在此过程中发现生成了两次单独的点击。 我不知道为什么。这是因为事件传播从未停止,并向下冒泡到根 HTML 元素,在本例中为 TR。

更改通用点击功能以检查点击的目标是什么,以及它的祖先是否包含 .btn-group class,每次点击,但仅限于按钮本身;如果您只点击该行,那么它只会触发一次。

如何防止这种传播/在正确的项目上设置事件侦听器?

    rowClicked:function(e){
        e.preventDefault()
        console.log("I get called twice?!")
        if($(e.target).parents('.btn-group').length > 0){
            //do nothing
        }
        else{
            e.stopPropagation()
            this.trigger("show:result",this)
        }
    }, 

只是一个想法,没有测试过。您在按钮的父级停止传播的是什么?例如click div.btn-group ?它不应干扰默认的 bootstrap 行为,但它会阻止它冒泡到行本身。

试试这个;

  rowClicked:function(e){
    if($(e.currentTarget).is('td')){ //or e.target will also do
        this.trigger("show:result",this);
    }
}, 

经过大量谷歌搜索和测试我的代码后,我认为这可能是目前最好的方法。它并没有真正解决我的问题的核心,但由于通用点击事件处理程序在 bootstrap 可以到达它之前插入自身,将点击的焦点更改为更具体的东西似乎已经成功了。

感谢 @Pawan@squall3d 在这方面的一些指示,但我仍在寻找有关如何更好地传播/设置/控制事件侦听器以防止与其他库发生冲突的答案将来。

但是,为了让它现在正常工作,我将通用点击更改为 "click td": "rowClicked"td 几乎是基础层,但足够具体,不会干扰按钮事件。我不再需要按钮单击处理程序(我们希望 bootstrap 这样做),因此已将其删除:"click div.btn-group button": "openSettings"

Show.Result = Marionette.ItemView.extend({
    tagName:"tr",
    template: "#row-template",
    events:{
        "click .js-delete-result": "editRow",
        "click .js-edit-result": "deleteRow",
        "click td": "rowClicked",
    },
    editRow: function(e){e.stopPropagation();alert("you trying to delete bro?!")},
    deleteRow: function(e){e.stopPropagation();alert("you trying to edit bro?!")},
    rowClicked:function(e){
        e.preventDefault()
        this.trigger("show:result",this)
    },
}

值得注意的是,我 确实 向 rowClicked 函数添加了额外的代码,以防止在显示按钮下拉列表并且您不小心单击了一行时打开模式在下拉菜单外单击以将其关闭。我发现这是更好的用户体验。

    rowClicked:function(e){
        if($("#edit-btn").hasClass("open")){
            //also do nothing - the dropdown is open
        }
        else{
            e.stopPropagation()
            e.preventDefault()
            this.trigger("show:result",this)
        }
    }

并向 btn 组添加了一个 id 以供 jquery 选择

<div class="btn-group" id="edit-btn">

这是可行的,因为 Bootstrap 添加了一个 .open css class 来触发显示下拉列表 html。