Skip to content

dom events "leaking" from child view to parent view #2967

Open
@paulovieira

Description

@paulovieira

When using nested views that have dom events in both the parent and child, it might happen that a dom event triggered in the child view will call also call the event handler in the parent (besides calling the handler in the child, as it should).

This happens if the selectors used in the events hash are the same in the parent and in child (or if they are not specific enough). Below is a simple example reproducing the situation (jsfiddle here: https://jsfiddle.net/paulovieira/kmt8kdmy/).

If you click the save button in the child view, you see that the handler from the parent is called as well, contrary to my expectation (the child view shouldn't be aware that there is a parent)

var Child = Mn.ItemView.extend({

    attributes: {
        style: "border: 1px solid red; margin: 5px;"
    },

    template: _.template(`
        <p>child view</p>
        <button class="btn-save">save</button>
    `),

    ui: {
        saveButtonChild: "button.btn-save"
    },

    events: {
        "click @ui.saveButtonChild": function(e){
            alert("this is the child view handler!");
        }
    },

});

var Parent = Mn.LayoutView.extend({

    attributes: {
        style: "border: 1px solid blue; padding: 5px;"
    },

    template: _.template(`
        <p>parent view</p>
        <button class="btn-save">save</button>

        <div class="some-region"></div>
    `),

    ui: {
        saveButtonParent: " button.btn-save",
        someRegion: "div.some-region"
    },

    events: {
        "click @ui.saveButtonParent": function(e){
            alert("this is the parent view handler!");
        }
    },

    onBeforeAttach: function(){
        this.getRegion("someRegion").show(new Child);
    },

    regions: {
        someRegion: '@ui.someRegion'
    }
});

var mainRegion = new Mn.Region({ el: "div#root-region"});
mainRegion.show(new Parent);

This is not a bug. It's simply a consequence of the way backbone uses event delegation in jquery, and the fact that the selectors are the same. There's an informative blog post in the marionette blog about this subject: http://blog.marionettejs.com/2015/02/12/understanding-the-event-hash/index.html

But I was wondering how have been people dealing with this issue. In a situation of deep nested views this might turn out to be problematic. Child views should not care about what is happening above them. If I have to return to some marionette code 1 year later to add a new child view, I shouldn't have to review the details in the parent to make sure the events are not clashing. Do you agree?

Having to add specific dummy classes or ids everywhere ("js-child-view-person", etc) just to avoid these clashes doesn't feel right.

Another option would be to add a top-level container element in the template, so that the selectors in the events hash can start from there. Something like this:

var Child = Mn.ItemView.extend({
     template: _.template(`
        <div class="child-container">
            <p>child view</p>
            <button class="btn-save">save</button>
        </div>
    `),

    ui: {
        saveButtonChild: "div.child-container > button.btn-save"
    },

    events: {
        "click @ui.saveButtonChild": function(e){
            alert("this is the child view handler!");
        }
    }
});

But this also doesn't feel right.

What have been people doing to solve this?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions