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;
}
});