admin管理员组文章数量:1314066
This is a simple problem for beginner but so far I haven't seen a solution that fits my need. Basically I got a simple menu with ul
and li
. There are 2 requirements:
Req1: When click on one, the li
will get new class .active
.
Req2: Menu items are dynamic, meaning I should be able to add or remove any menu item (by using some other button).
There are 2 ways to do this:
Method 1: Tranversing for each MenuView as MenuItem
I have a MenuView with something like this
el: $('li'),
events: {
"click" : "highlight"
},
highlight: function(e) {
thisParent = $(e.target).parent();
thisParent.siblings('.active').removeClass('active');
thisParent.addClass('active');
},
Pro: Easy. This is what I have now.
Con: Dependency on the html structure. What if it's changed to div
instead with many layers.
Method 2: One View for the MenuCollection
Create a MenuItemCollection and use MenuView for that collection instead. The el
for MenuView will be ul
(instead of li
). The HTML will look like this with separate id
:
<ul>
<li id="leftmenu-one">one</li>
<li id="leftmenu-two">two</li>
<li id="leftmenu-three">three</li>
</ul>
Then when a click event is detected, do 2 things:
2a. Remove all .active
class in ul li
2b. Add .active
class to the e.target
DOM
Pro: Decoupling html design
Con: Little more code.
QUESTION: I think most people would say Method 1 is bad. But is there method 3, 4, 5... that are better? How to handle the adding of new menu item?
This is a simple problem for beginner but so far I haven't seen a solution that fits my need. Basically I got a simple menu with ul
and li
. There are 2 requirements:
Req1: When click on one, the li
will get new class .active
.
Req2: Menu items are dynamic, meaning I should be able to add or remove any menu item (by using some other button).
There are 2 ways to do this:
Method 1: Tranversing for each MenuView as MenuItem
I have a MenuView with something like this
el: $('li'),
events: {
"click" : "highlight"
},
highlight: function(e) {
thisParent = $(e.target).parent();
thisParent.siblings('.active').removeClass('active');
thisParent.addClass('active');
},
Pro: Easy. This is what I have now.
Con: Dependency on the html structure. What if it's changed to div
instead with many layers.
Method 2: One View for the MenuCollection
Create a MenuItemCollection and use MenuView for that collection instead. The el
for MenuView will be ul
(instead of li
). The HTML will look like this with separate id
:
<ul>
<li id="leftmenu-one">one</li>
<li id="leftmenu-two">two</li>
<li id="leftmenu-three">three</li>
</ul>
Then when a click event is detected, do 2 things:
2a. Remove all .active
class in ul li
2b. Add .active
class to the e.target
DOM
Pro: Decoupling html design
Con: Little more code.
QUESTION: I think most people would say Method 1 is bad. But is there method 3, 4, 5... that are better? How to handle the adding of new menu item?
Share Improve this question edited Oct 16, 2012 at 6:10 HP. asked Oct 16, 2012 at 5:39 HP.HP. 19.9k56 gold badges159 silver badges258 bronze badges 6- Your code is a bit too reliant on the HTML structure. You shouldn't be doing that much DOM traversal in Backbone, because each View contains it's own HTML references. Instead of traversing the DOM, isolate the correct View and then have it handle the traversal on a smaller scale. A good way to do this is with Backbone Events. – Cory Danielson Commented Oct 16, 2012 at 9:27
- @CoryDanielson Right I knew it and was checking other creative alternatives. Some reason your answer below was deleted. Can you repost? I'd love to keep it to study later. – HP. Commented Oct 17, 2012 at 8:30
-
1
Alright, I undeleted it. The only difference between mine and the other answer is that I don't have
active
as part of the model, because I don't really see that as business data that would be saved in any database.. it'd be a wasted column.. it's more of a UI/view thing. Because of that, I don't have change:active, and threw a custom event instead. – Cory Danielson Commented Oct 17, 2012 at 17:20 -
1
I also have
Req2
in there (Menu items are dynamic, meaning I should be able to add or remove any menu item (by using some other button).) – Cory Danielson Commented Oct 17, 2012 at 17:21 - 1 The other way around. Every menuItemView is in the menuView. Memory isn't an issue, everything is by reference – Cory Danielson Commented Oct 18, 2012 at 4:52
3 Answers
Reset to default 8Create a menu item model
var MenuItem = Backbone.Model.extend({
title: 'Default Title',
isSelected: false
});
and items collection, that would listen to any model selection change event
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});
}
});
}
});
After that create a view each for menu item
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});
}
});
and menu itself like
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;
}
});
Full working js fiddle with ments at http://jsfiddle/Kf3SS/
JSFiddle Demo w/ code ments
There's a bit of DOM traversal in your views that you most-likely want to avoid... because this example is so small, it's not going to ruin the user experience, but it's a best practice and all that good stuff.
I think this should be a good starting point/example for you.
Variable Setup
var MenuView
, MenuItemView
, MenuItemModel
, menu //MenuView Instance
, menuItem //MenuItemView Instance
;
MenuItemModel
MenuItemModel = Backbone.Model.extend({
'defaults': {
'url': undefined
, 'text': undefined
}
});
MenuView (<ul>)
MenuView = Backbone.View.extend({
'tagName': 'ul'
, 'id': 'MenuView'
, 'menuItems': []
, 'activeButton': undefined
, 'initialize': function ( menuObjJSON, parent ) {
var menuItem;
for ( menuItemIndex in menuObjJSON ) {
this.addMenuItem( new MenuItemModel(menuObjJSON[menuItemIndex]) )
}
this.render(parent);
}
, 'render': function ( parent ) {
$(parent).append(this.$el);
}
, 'addMenuItem': function ( model ) {
var menuItem = new MenuItemView({
'model': model,
'parentElement': this.$el
});
menuItem.on('changeActive', this.setActiveButton, this);
this.menuItems.push( menuItem );
return menuItem;
}
, 'removeMenuItem': function ( identifier ) {
var i, menuItem, length = this.menuItems.length, menuItemsCopy;
for (i = 0; i < length; i++) {
if ( this.menuItems[i] ) {
menuItemView = this.menuItems[i];
if ( menuItemView.model.get('text').toLowerCase() === identifier.toLowerCase()
|| menuItemView.model.get('url').toLowerCase() === identifier.toLowerCase() )
{
menuItemView.destroy();
debugger;
menuItemsEnd = this.menuItems.slice(i+1, length);
this.menuItems = [].concat(this.menuItems.slice(0,i), menuItemsEnd);
return true;
}
}
}
return false; //if menu item not found
}
, 'setActiveButton': function ( activeMenuItem ) {
if ( this.activeButton ) {
this.activeButton.removeHighlight();
}
this.activeButton = activeMenuItem;
}
});
MenuItemView (<li>)
MenuItemView = Backbone.View.extend({
'tagName': 'li'
, 'className': 'menuItem'
, 'events': {
'click a': 'highlight'
}
, 'initialize': function ( options ) {
this.render(options.model, options.parentElement);
}
, 'render': function ( model, parentElement ) {
this.$el.append("<a href='" + model.get('url')+ "'>" + model.get('text') + "</a>");
parentElement.append(this.$el);
}
, 'highlight': function ( event ) {
if ( !this.$el.hasClass('active') ) {
this.trigger('changeActive', this);
this.$el.addClass('active');
}
}
, 'removeHighlight': function () {
this.$el.removeClass('active');
}
, 'destroy': function () {
this.unbind('click a');
this.remove(); //unbind from DOM, remove DOM events
}
});
Menu Instantiation
menu = new MenuView([
{'url': '#home', 'text': 'Home'}
, {'url': '#catpics', 'text': 'Cat Pics'}
, {'url': '#dogpics', 'text': 'Dog Pics'}
, {'url': '#about', 'text': 'About Us'}
], $('body'));
Adding New Menu Items. (not the most practical example, but a starting point)
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);
Removing Menu Items (not the most practical example, but a starting point)
setTimeout(function(){
menu.removeMenuItem('#contact');
}, 5000);
setTimeout(function(){
menu.removeMenuItem('about us');
}, 5000);
setTimeout(function(){
menu.removeMenuItem('#SOMETHING');
}, 5000);
I hope this may be helpful to you. use this jquery
$("ul li").click(function() {
$("ul li").removeClass("active");
$(this).addClass("active");
});
Demo Link
本文标签: javascriptClick menu item to highlight in Backbone ViewStack Overflow
版权声明:本文标题:javascript - Click menu item to highlight in Backbone View - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741959201a2407170.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论