Search code examples
javascriptjquerybackbone.jsbackbone-viewscustom-data-attribute

jQuery data $(...).data('foo') sometimes returning undefined in Backbone app


Running into a weird bug – so I have an attribute defined as 'data-tagtype' on my button elems in my HTML. When a user clicks on a button, the following method gets called:

onClickTag: function(e) {
  if (!this.playerLoaded) {
    return false;
  }
  var type = $(e.target).data('tagtype');
  var seconds = this.getCurrentTime();
  console.log(type);
  if (type) {
    this.model.addTag({
      type: type,
      seconds: seconds
    });
  }
},

This works most of the time, but for some reason sometimes type is undefined for (seemingly) random elements. The corresponding HTML is here:

<button id="tag-love" class="tagger disabled" data-tagtype="love"><i class="fa fa-heart fa-fw"></i></button>
<button id="tag-important" class="tagger disabled" data-tagtype="important"><i class="fa fa-exclamation fa-fw"></i> Important</button>
<button id="tag-more" class="tagger disabled" data-tagtype="more"><i class="fa fa-ellipsis-h fa-fw"></i> More</button>
<button id="tag-confused" class="tagger disabled" data-tagtype="confused"><i class="fa fa-question fa-fw"></i> Confused</button>

It's weird because there doesn't seem to be a pattern with respect to which ones return undefined when. Sometimes all of them work and sometimes one of them returns undefined for a couple of seconds, but then if I keep clicking it returns the proper value.

The View is definitely rendered/loaded into the DOM before any of these methods get called.

Any ideas? Does Backbone do something maybe?


Solution

  • The problem is that Backbone views use event delegation for their event handling. That means that e.target will be the the element that is clicked rather than the element that is responding to the event. If you click on the <i>, e.target will be that <i> but if you click on the text, e.target will be the <button>; the <i> doesn't have the data attribute you're looking for but the <button> does. That means that sometimes $(e.target).data('tagtype') will be undefined.

    You can see this behavior in a simple example:

    <div id="x">
        <button type="button" data-tagtype="love"><i>icon</i> text</button>
    </div>
    

    and a minimal view:

    var V = Backbone.View.extend({
        el: '#x',
        events: {
            'click button': 'clicked'
        },
        clicked: function(ev) {
            console.log(
                $(ev.target).data('tagtype'),
                $(ev.currentTarget).data('tagtype')
            );
        }
    });
    

    Demo: http://jsfiddle.net/ambiguous/pe77p/

    If you click on <i>icon</i>, you'll get undefined love in the console but if you click on the text, you'll get love love in the console.

    That little demo also contains the solution: use e.currentTarget instead of e.target.