最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Backbone.js hover event not triggering - Stack Overflow

programmeradmin1浏览0评论

I'm trying to use Backbone.js for the first time and I'm having some trouble. I don't know if my problem is that I'm not understanding how backbone is supposed to work or if it's just a code problem.

I'm trying to create a dynamic menu, and I have no problem creating the main menu bar with it's items, but I can't get the hover event to trigger whenever I hover one of the menu items.

Views

var MenuView = Backbone.View.extend({
    initialize: function(items) {
        this.menu = items;
        //Main navigation bar
        this.el = $("#main-nav");
        this.trigger('start');
        this.render();
    },
    render: function() {
        var me = this;
        _.each(this.menu, function(mi) {
            mi.render(me.el);
        });
        return this;
    },
    handleHover: function(e) {
        console.debug(e);
    }
});

var MenuItemView = Backbone.View.extend({
    tagName: 'li',
    className:'menu-item',
    events: { //none of these work
        'hover a':'handleHover',
        'mouseover a':'handleHover',
        'mouseover':'handleHover',
        'click': 'handleHover',
        'click a': 'handleHover'
    },
    initialize: function(mi) {
        this.menuItem = mi;
        this.el = $("<li class=\"menu-item\"></li>")
    }, 
    render: function(parent) {
        this.el.append('<a href="' + this.menuItem.get("link") + '">' + this.menuItem.get("text") + '</a>');
        parent.append(this.el);
        return this;
    },

    handleHover: function(ev) {
        console.debug("Hovering! " + ev + this.menuItem.get("cid"));
        console.debug(ev);
        return false;
    }
});

Model

var MenuItem = Backbone.Model.extend({
    defaults: {
        parent: null,
        children: [],
        link: "",
        text: ""
    }   
});

Startup code

$(document).ready(function() {
    var menu = new MenuView([
        new MenuItemView( new MenuItem({link: "/", text: "Home"})),
        new MenuItemView( new MenuItem({link: "/", text: "Users"})),
        new MenuItemView( new MenuItem({link: "/", text: "Configuration"}))
    ]);
});

Any help will be appreciated!

Thanks!

Update

Ok, after taking the definition of el outside of the initialize method on the MenuItemView view, it works, BUT that same element gets reused on all instances of the view, so I had to change the view to the following code in order to make it work the way I want it:

 var MenuItemView = Backbone.View.extend({

    events: { //none of these work
        'hover a':'handleHover',
        'mouseover a':'handleHover',
        'mouseover':'handleHover',
        'click': 'handleHover',
        'click a': 'handleHover'
    },
    el: $('<li class="menu-item"></li>'),
    initialize: function(mi) {
        this.menuItem = mi;
        this.el = $(this.el).clone(true);
    }, 
    render: function(parent) {
        this.el.append('<a href="' + this.menuItem.get("link") + '">' + this.menuItem.get("text") + '</a>');
        parent.append(this.el);
        return this;
    },

    handleHover: function(ev) {
        console.debug("Hovering! " + ev + this.menuItem.get("cid"));
        console.debug(ev);
        return false;
    }
});

Wny do I have to clone the element on a new instance?

I'm trying to use Backbone.js for the first time and I'm having some trouble. I don't know if my problem is that I'm not understanding how backbone is supposed to work or if it's just a code problem.

I'm trying to create a dynamic menu, and I have no problem creating the main menu bar with it's items, but I can't get the hover event to trigger whenever I hover one of the menu items.

Views

var MenuView = Backbone.View.extend({
    initialize: function(items) {
        this.menu = items;
        //Main navigation bar
        this.el = $("#main-nav");
        this.trigger('start');
        this.render();
    },
    render: function() {
        var me = this;
        _.each(this.menu, function(mi) {
            mi.render(me.el);
        });
        return this;
    },
    handleHover: function(e) {
        console.debug(e);
    }
});

var MenuItemView = Backbone.View.extend({
    tagName: 'li',
    className:'menu-item',
    events: { //none of these work
        'hover a':'handleHover',
        'mouseover a':'handleHover',
        'mouseover':'handleHover',
        'click': 'handleHover',
        'click a': 'handleHover'
    },
    initialize: function(mi) {
        this.menuItem = mi;
        this.el = $("<li class=\"menu-item\"></li>")
    }, 
    render: function(parent) {
        this.el.append('<a href="' + this.menuItem.get("link") + '">' + this.menuItem.get("text") + '</a>');
        parent.append(this.el);
        return this;
    },

    handleHover: function(ev) {
        console.debug("Hovering! " + ev + this.menuItem.get("cid"));
        console.debug(ev);
        return false;
    }
});

Model

var MenuItem = Backbone.Model.extend({
    defaults: {
        parent: null,
        children: [],
        link: "",
        text: ""
    }   
});

Startup code

$(document).ready(function() {
    var menu = new MenuView([
        new MenuItemView( new MenuItem({link: "/", text: "Home"})),
        new MenuItemView( new MenuItem({link: "/", text: "Users"})),
        new MenuItemView( new MenuItem({link: "/", text: "Configuration"}))
    ]);
});

Any help will be appreciated!

Thanks!

Update

Ok, after taking the definition of el outside of the initialize method on the MenuItemView view, it works, BUT that same element gets reused on all instances of the view, so I had to change the view to the following code in order to make it work the way I want it:

 var MenuItemView = Backbone.View.extend({

    events: { //none of these work
        'hover a':'handleHover',
        'mouseover a':'handleHover',
        'mouseover':'handleHover',
        'click': 'handleHover',
        'click a': 'handleHover'
    },
    el: $('<li class="menu-item"></li>'),
    initialize: function(mi) {
        this.menuItem = mi;
        this.el = $(this.el).clone(true);
    }, 
    render: function(parent) {
        this.el.append('<a href="' + this.menuItem.get("link") + '">' + this.menuItem.get("text") + '</a>');
        parent.append(this.el);
        return this;
    },

    handleHover: function(ev) {
        console.debug("Hovering! " + ev + this.menuItem.get("cid"));
        console.debug(ev);
        return false;
    }
});

Wny do I have to clone the element on a new instance?

Share Improve this question edited Jan 19, 2012 at 19:40 mu is too short 435k71 gold badges858 silver badges818 bronze badges asked Jan 19, 2012 at 13:32 DeletemanDeleteman 8,6907 gold badges27 silver badges40 bronze badges 2
  • Why do you have to clone your el? Consider when $('<li class="menu-item"></li>') is evaluated and you'll have your answer. – mu is too short Commented Jan 19, 2012 at 19:04
  • if I create the element for el on the initialize method, then the events are not triggered, how can I solve that? – Deleteman Commented Jan 19, 2012 at 19:06
Add a comment  | 

3 Answers 3

Reset to default 10

hover is not a normal event, but a 'convenience' event provided by jquery. It is a combination of mouseenter and mouseleave.

Binding to mouseenter and mouseleave instead of hover will do what you need.

Re: "Why do I have to clone the element on a new instance?"

The underlying problem is right here:

var MenuItemView = Backbone.View.extend({
    // ...
    el: $('<li class="menu-item"></li>'),

The $('<li class="menu-item"></li>') call is executed when MenuItemView is being defined so you end up with only one $('<li>') being shared across all instances of MenuItemView.

If you create the el inside initialize or render then you'll have to bind the events by hand using delegateEvents:

By default, delegateEvents is called within the View's constructor for you [...]

So if you create this.el yourself then you'll have to call this.delegateEvents() yourself. For example:

var MenuItemView = Backbone.View.extend({
    // ...
    render: function() {
        this.el = $('<li class="menu-item"><a>' + this.cid + '</a></li>');
        this.delegateEvents();
        return this;
    },
    //...
});

Demo: http://jsfiddle.net/ambiguous/RPqMh/2/

However, if you clone your this.el with the withDataAndEvents flag on, then you should be fine:

var MenuItemView = Backbone.View.extend({
    el: $('<li class="menu-item"></li>'),
    // ...
    initialize: function() {
        this.el = this.el.clone(true);
        this.el.append('<a>' + this.cid + '</a>');
    }, 
    //...
});

Demo: http://jsfiddle.net/ambiguous/hCW3F/1/

But if you just this.el.clone(), it won't work because the delegate won't be bound to the clone:

var MenuItemView = Backbone.View.extend({
    el: $('<li class="menu-item"></li>'),
    // ...
    initialize: function() {
        this.el = this.el.clone();
        this.el.append('<a>' + this.cid + '</a>');
    }, 
    // ...
});

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

But if you add your own delegateEvents call, you'll be okay:

var MenuItemView = Backbone.View.extend({
    el: $('<li class="menu-item"></li>'),
    // ...
    initialize: function() {
        this.el = this.el.clone();
        this.el.append('<a>' + this.cid + '</a>');
    },
    render: function() {
        this.delegateEvents();
        return this;
    },
    // ...
});

Demo: http://jsfiddle.net/ambiguous/KZNPA/1/

It seems to me that you don't need this properties:

tagName: 'li',
className:'menu-item'

in MenuItemView if you specify this.el = $('<li class="menu-item"></li>');

发布评论

评论列表(0)

  1. 暂无评论