Javascript 单击菜单项以在主干视图中高亮显示

Javascript 单击菜单项以在主干视图中高亮显示,javascript,jquery,html,css,backbone.js,Javascript,Jquery,Html,Css,Backbone.js,这对于初学者来说是一个简单的问题,但到目前为止,我还没有看到一个适合我需要的解决方案。基本上我得到了一个带有ul和li的简单菜单。有两个要求: Req1:当单击一个时,li将获得新类.active Req2:菜单项是动态的,这意味着我应该能够添加或删除任何菜单项(通过使用其他按钮) 有两种方法可以做到这一点: 方法1:将每个MenuView转换为MenuItem 我有一张这样的菜单 el: $('li'), events: { "click" : "highlight" }, high

这对于初学者来说是一个简单的问题,但到目前为止,我还没有看到一个适合我需要的解决方案。基本上我得到了一个带有
ul
li
的简单菜单。有两个要求:

Req1:当单击一个时,
li
将获得新类
.active

Req2:菜单项是动态的,这意味着我应该能够添加或删除任何菜单项(通过使用其他按钮)

有两种方法可以做到这一点:

方法1:将每个MenuView转换为MenuItem

我有一张这样的菜单

el:  $('li'),

events: {
  "click" : "highlight"
},

highlight: function(e) {
  thisParent = $(e.target).parent();
  thisParent.siblings('.active').removeClass('active');
  thisParent.addClass('active');
},
Pro:简单。这就是我现在拥有的

Con:依赖于html结构。如果它改为具有多个层的
div
,该怎么办

方法2:MenuCollection的一个视图

创建MenuItemCollection并改为使用该集合的MenuView。MenuView的
el
将是
ul
(而不是
li
)。HTML将如下所示,带有单独的
id

<ul>
    <li id="leftmenu-one">one</li>
    <li id="leftmenu-two">two</li>
    <li id="leftmenu-three">three</li>
</ul>
  • 一个
  • 两个 三个
然后,当检测到单击事件时,执行两项操作:

2a.删除
ul li中的所有
活动

2b.
.active
类添加到
e.target
DOM

Pro:解耦html设计

Con:再多一点代码


问题:我想大多数人会说方法1不好。但是有没有方法3,4,5。。。那更好吗?如何处理添加新菜单项?

我希望这对您有所帮助。使用这个jquery

$("ul li").click(function() {
$("ul li").removeClass("active");
$(this).addClass("active");
});

在您的视图中有一些您最想避免的DOM遍历。。。因为这个例子太小了,它不会破坏用户体验,但它是一个最佳实践和所有好东西

我认为这应该是一个很好的起点/例子

变量设置 MenuItemModel 菜单视图(
    ) 添加新的菜单项。(不是最实际的例子,而是一个起点) 删除菜单项(不是最实用的示例,而是一个起点)
    创建菜单项模型

        var MenuItem = Backbone.Model.extend({
            title: 'Default Title',
            isSelected: false
        });
    
    和项集合,该集合将侦听任何模型选择更改事件

       var MenuItemCollection = Backbone.Collection.extend({
            model: MenuItem,
    
            initialize: function() {
                this.on('change:isSelected', this.onSelectedChanged, this);
            },
    
            onSelectedChanged: function(model) {
                this.each(function(model) {
                    if (model.get('isSelected') === true && !model.hasChanged('isSelected')) {
                        model.set({isSelected: false});
                    }
                });
            }
        });
    
    然后为每个菜单项创建一个视图

       var MenuItemView = Backbone.View.extend({
            tagName:  'li',
            events: {
              'click' : 'highlight'
            },
    
            initialize: function() {
                _.bindAll(this);
                this.model.on('change:isSelected', this.onSelectedChanged);
            },
    
            render: function() {
                this.$el.text(this.model.get('title'));
                return this;
            },
    
            onSelectedChanged: function() {
                if (this.model.get('isSelected') === true) {
                    this.$el.addClass('active');
                }
                else {
                    this.$el.removeClass('active');
                }
            },
    
            highlight: function() {
                this.model.set({isSelected: true});
            }
        });
    
    菜单本身就像

       var MenuView = Backbone.View.extend({
            tagName:  'ul',
    
            initialize: function() {
                _.bindAll(this);
            },
    
            render: function() {
                this.collection.each(function(model) {
                    var item = new MenuItemView({model: model});
                    this.$el.append(item.render().el);
                }, this);
    
                return this;
            }
        });
    

    完整工作的js在

    处摆弄注释对,我可以单独使用jQuery,但整个想法是将菜单抽象到视图对象中。简言之,@HP。希望活动项基于活动视图,而不是基于单击事件。您的代码过于依赖HTML结构。您不应该在主干中进行太多DOM遍历,因为每个视图都包含自己的HTML引用。不要遍历DOM,而是隔离正确的视图,然后让它以较小的规模处理遍历。这样做的一个好方法是使用主干活动。@CoryDanielson对,我知道这一点,并且正在检查其他创造性的替代方案。以下答案被删除的原因。你能转寄吗?我想把它留着以后学习。好的,我取消了它。我的答案和另一个答案之间的唯一区别是,我没有将
    活动
    作为模型的一部分,因为我并不真正将其视为可以保存在任何数据库中的业务数据。。那将是一个浪费的专栏。。它更像是一个UI/视图的东西。因此,我没有change:active,而是抛出了一个自定义事件。我还有
    Req2
    (菜单项是动态的,这意味着我应该能够添加或删除任何菜单项(通过使用其他按钮)。)相反。每个menuItemView都在menuView中。记忆不是问题,一切都是参照物。这很聪明。因此,让我确保我理解了您的逻辑:看起来我们的想法是利用模型的
    change
    事件,对吗?
    单击
    事件将模型设置为
    活动
    ,触发了
    更改
    事件,以设置
    isSelected:false
    ,使用
    hasChanged()
    作为逻辑Yes,在这种情况下,中继选择逻辑(实际上是菜单项的业务逻辑的一部分)是合理的从视图到模型/集合,使视图有机会根据模型状态重新绘制自身。另外一个优点是,您将保留与items count或任何其他附加的“onclick”项逻辑无关的行为。虽然在这种情况下,主干网的实现在简单性上已经失去了纯jquery的功能。太棒了!我喜欢这种方法。使用木偶收集视图和收集事件会更好。很抱歉,我很难理解这里的
    单击
    事件逻辑。因此,当发生
    单击
    时,将调用
    突出显示
    ,以添加
    活动
    类。但是,
    changeActive
    事件如何触发旧的活动菜单项的
    removeholding
    ?我想我不明白
    setActiveButton
    中的部分。谢谢。MenuView保存每个MenuItemView,并侦听每个活动事件。当它们触发changeActive事件时,它们会将此
    (视图本身)作为参数传递。当菜单视图看到此事件时,它会告诉
    this.activeButton
    删除突出显示,然后将新视图(触发事件的视图)保存到this.activeButton菜单项:
    this.trigger('changeActive',this)。。。。。。。。。。菜单:
    menuItem.on('changeActive',this.setActiveButton,this)
    'setActiveButton':函数(activeMenuItem){}
    从菜单项作为activeMenuItem传递到setActiveButton。菜单将删除旧菜单上的突出显示,然后覆盖<代码中的新菜单
    menu = new MenuView([
        {'url': '#home',    'text': 'Home'}
    ,   {'url': '#catpics', 'text': 'Cat Pics'}
    ,   {'url': '#dogpics', 'text': 'Dog Pics'}
    ,   {'url': '#about',   'text': 'About Us'}
    ], $('body'));
    
    setTimeout(function(){
        menu.addMenuItem({'url': '#contact', 'text': 'Contact Us'});
    }, 1000);
    
    setTimeout(function(){
        menu.addMenuItem({'url': '#Login', 'text': 'Log In'});
    }, 2000);
    
    setTimeout(function(){
        menu.addMenuItem({'url': '#W3bm4573r', 'text': 'W3bm4573r'});
    }, 3000);
    
    setTimeout(function(){
        menu.addMenuItem({'url': '#something', 'text': 'Something'});
    }, 4000);
    
    setTimeout(function(){
        menu.removeMenuItem('#contact');
    }, 5000);
    
    setTimeout(function(){
        menu.removeMenuItem('about us');
    }, 5000);
    
    setTimeout(function(){
        menu.removeMenuItem('#SOMETHING');
    }, 5000);
    
        var MenuItem = Backbone.Model.extend({
            title: 'Default Title',
            isSelected: false
        });
    
       var MenuItemCollection = Backbone.Collection.extend({
            model: MenuItem,
    
            initialize: function() {
                this.on('change:isSelected', this.onSelectedChanged, this);
            },
    
            onSelectedChanged: function(model) {
                this.each(function(model) {
                    if (model.get('isSelected') === true && !model.hasChanged('isSelected')) {
                        model.set({isSelected: false});
                    }
                });
            }
        });
    
       var MenuItemView = Backbone.View.extend({
            tagName:  'li',
            events: {
              'click' : 'highlight'
            },
    
            initialize: function() {
                _.bindAll(this);
                this.model.on('change:isSelected', this.onSelectedChanged);
            },
    
            render: function() {
                this.$el.text(this.model.get('title'));
                return this;
            },
    
            onSelectedChanged: function() {
                if (this.model.get('isSelected') === true) {
                    this.$el.addClass('active');
                }
                else {
                    this.$el.removeClass('active');
                }
            },
    
            highlight: function() {
                this.model.set({isSelected: true});
            }
        });
    
       var MenuView = Backbone.View.extend({
            tagName:  'ul',
    
            initialize: function() {
                _.bindAll(this);
            },
    
            render: function() {
                this.collection.each(function(model) {
                    var item = new MenuItemView({model: model});
                    this.$el.append(item.render().el);
                }, this);
    
                return this;
            }
        });