Search code examples
backbone.jsbackbone-viewsbackbone-eventsjquery-droppablejquery-draggable

jQuery & Backbone: How to append a view on drop event, under certain conditions


I have the following View of my CanvasModel, on which the user can drag & drop various widgets from a toolbar. Whenever something is dropped into the CanvasView a new WidgetView instance will be appended into the DOM.

The recently appended Widget is also draggable, in order for the user to position it wherever he wants within the Canvas. The problem is that as soon as the recently appended Widget is dropped, another WidgetView instance gets appended

var CanvasView = Backbone.View.extend ({
    tagName: 'section',
    className: 'canvas', 

    events: {
    'drop': 'instantiateWidget'
    },

    initialize: function(){
        this.$el.droppable({});
    },

    render: function(){
        return this;
    },

    instantiateWidget: function() {
        this.$el.on("drop", function(event, ui){}).append(new WidgetView({model: myWidget}).render().el);   
    }
});     

I only want to instantiate a Widget that is dragged from the toolbar to the canvas, (they have a class of "dragged"). Is there a way to "filter" the drop listener, so the CanvasView will instantiate a Widget only when the dropped item has either a class of "dragged" or it has been dragged from the toolbar?


Solution

  • I'm not familiar with that specific drop event, but to answer your question of

    Is there a way to "filter" the drop listener

    The answer is, yes indeed. All event handlers get passed an event object, and you can check out the details of it (in particular its target property) to determine which DOM element triggered the event.

    However, I noticed a more important issue in your code: you seem to be hooking up the event handler improperly. You're doing:

    this.$el.on("drop", function(event, ui){}).append(new WidgetView({model: myWidget}).render().el);
    

    which will hook up an event handler that does nothing, then immediately append a new widget. What I think you want to do is:

    this.$el.on("drop", function(event, ui){
        $(event.target).append(new WidgetView({model: myWidget}).render().el)
    });
    

    Except even that seems wrong, because you're binding an event handler in response to another event. What I think you want is:

    instantiateWidget: function() {
       this.$el.append(new WidgetView({model: myWidget}).render().el);   
    }
    

    So, to circle back to your original question, if you want to check the element that caused the event in that new code, it would look something like:

    instantiateWidget: function(e) {
       if ($(e.target).is(':not(canvas)') return;
       this.$el.append(new WidgetView({model: myWidget}).render().el);   
    }
    

    Alternatively, you might be able to avoid that entirely and just set your selectors on your event hookups to only target events spawned by the elements you want (remember, the event hookups just use normal jQuery selectors).