$(document).ready(function(){
$('.tagLines').on('mouseover', function(){
$(this).css("background-color","#ffffff").css("box-shadow", "0 0 4px 4px #C9C9C9");
}).on('mouseleave', function(){
$(this).css('background-color', '#F7F7F7').css("box-shadow", "0 0 2px 2px #C9C9C9");
}).on('click', function(){
window.location.hash = "manageMarks";
});
//this section is for all the views used in the application
var PGHome = Backbone.View.extend({
tagName: 'div',
initialize: function(){
},
render: function(){
var template = _.template($("#home").html(), {} );
this.$el.html( template );
}
});
var pgHome = new PGHome({el: $("#bodyContainer") });
//***********************************************************
//this is a router for the application
var NavRouter = Backbone.Router.extend({
routes:{
"manageMarks": "manage",
"": "home"
}
});
var navRouter = new NavRouter();
navRouter.on('route:manage', function(){
console.log("Manage Modules");
}).on('route:home', function (){
pgHome.render();
});
Backbone.history.start();
//******************************************
});
The above is a js snippet and its working as far as rendering a view with backbone is concerned. The problem is after the view is rendered, the elements rendered's events (click, mouseover, mouseleave) are not firing. The events where working prior to adding them with backbone. Can anyone help?
You need use event delegation, because you add events, before element has been appended to page
$("#bodyContainer").on('mouseover', '.tagLines', function(){
$(this).css("background-color","#ffffff").css("box-shadow", "0 0 4px 4px #C9C9C9");
}).on('mouseleave', '.tagLines', function(){
$(this).css('background-color', '#F7F7F7').css("box-shadow", "0 0 2px 2px #C9C9C9");
}).on('click', '.tagLines', function(){
window.location.hash = "manageMarks";
});
There isn't .tagLine element in DOM when bind events to it.
The code like this,
var PGHome = Backbone.View.extend({
tagName: 'div',
events:{
'mouseover .tagLines': function(){
// ...
},
'mouseleave .tagLines': function(){
// ...
},
'click .tagLines': function(){
// ...
}
},
initialize: function(){},
render: function(){
var template = _.template($("#home").html(), {} );
this.$el.html( template );
}
});
Related
I've got a problem with my Backbone.js app (Fiddle: http://jsfiddle.net/the_archer/bew7x010/3/). The app is list (ol element) with nested lists within. Here's my starting HTML:
<body>
<ol id="flowList"></ol>
</body>
<script type="text/template" id="item-template">
<%= content %>
</script>
When the list is empty. I add a new <li> element within it using Backbone.js and focus on the li.
When within the li, if I press the enter key, I want to insert a new li just after the li I pressed enter within.
If I press tab, I want to add a new sub ol element within the list element. I handle the keypress within the li elements like so:
handleKeyboardShortcuts: function(e){
if (e.keyCode == 13 && !e.shiftKey){
e.preventDefault();
this.el = $(e.target).parent();
Items.create({contnet: "New Item!"});
}
}
I have a listenTo on my collection, which on add, appends a li to the #flowList ol element using the addOne function:
addOne: function(todo) {
var view = new ItemView({model: todo});
$(this.el).append(view.render().el);
}
My problem is:
How do I pass the target element to add to, to the addOne function?
How do I pass an option to the addOne function, which instead of doing $(this.el).append does $(this.el).after ?
For some reason, I can't get my head around how to pass around those details. Here's the full Backbone.js code:
$(function() {
var Item = Backbone.Model.extend({
defaults: function() {
return {
content: "empty item..."
};
}
});
var ItemList = Backbone.Collection.extend({
model: Item,
localStorage: new Backbone.LocalStorage("todos-backbone"),
});
var Items = new ItemList;
var ItemView = Backbone.View.extend({
tagName: "li",
template: _.template($('#item-template').html()),
events: {
"click": "enableEdit",
"blur": "disableEdit",
},
initialize: function() {
this.listenTo(this.model, 'change', this.render);
this.listenTo(this.model, 'destroy', this.remove);
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
},
enableEdit: function(){
this.$el.attr("contenteditable","true").focus();
},
disableEdit: function(){
this.$el.attr("contenteditable","false");
}
});
var AppView = Backbone.View.extend({
el: $("#flowList"),
events: {
"keydown li": "handleKeyboardShortcuts"
},
initialize: function() {
this.listenTo(Items, 'add', this.addOne);
this.listenTo(Items, 'reset', this.addAll);
this.listenTo(Items, 'all', this.render);
Items.fetch();
if (Items.length === 0){
Items.create({content: "Sample Item!"});
}
},
render: function(e) {
console.log(e);
},
addOne: function(todo) {
var view = new ItemView({model: todo});
$(this.el).append(view.render().el);
},
addAll: function() {
Items.each(this.addOne, this);
},
handleKeyboardShortcuts: function(e){
if (e.keyCode == 13 && !e.shiftKey){
e.preventDefault();
this.el = $(e.target).parent();
Items.create({contnet: "New Item!"});
}
}
});
var App = new AppView;
});
Here's a link to the fiddle: http://jsfiddle.net/the_archer/bew7x010/3/
You could keep track of the selected item, enabling you to insert the new one after or within it.
In your existing enableEdit handler, trigger an 'item:select' event:
var ItemView = Backbone.View.extend({
enableEdit: function(){
this.trigger('item:select', this);
this.$el.attr("contenteditable","true").focus();
}
});
Then in your AppView, when you add a new item, attach a listener for the new item that updates the value of a 'selectedItem' property.
Then you can insert the new item into the dom based on the state of that property:
var AppView = Backbone.View.extend({
addOne: function(todo) {
var view = new ItemView({model: todo});
this.listenTo( view, 'item:select', this.handleItemSelect );
if(this.selectedItem) {
$(this.selectedItem.el).after(view.render().el);
}
else {
$(this.el).append(view.render().el);
}
},
handleItemSelect: function(item) {
console.log('item selected:', item);
this.selectedItem = item;
}
});
You should be able to do something similar for the tab key behaviour, by setting a flag when the tab key is pressed, before calling Items.create.
In the events hash, i want to use resize event, but they
do not work neither. However, click event works well. why resize does not work?
var SubheaderView = Backbone.View.extend({
id: 'gallery',
tagName: 'div',
events: {
'click #minor': 'getPadding',
'resize #minor': 'getPadding' //not work
},
initialize: function() {
this.render();
},
render: function() {
this.$el.append(subheaderTemplate);
},
getPadding: function() {
var pad_top = Math.floor($('#minor').height() * 0.4);
var pad_left = Math.floor($('#minor').width() * 0.07);
var pad = pad_top + 'px 0px 0px ' + pad_left + 'px';
$('#cover h1').css({'padding': pad});
}
});
It does not related to Backbone at all. According to MDN, only window has a resize event.
You can do something like this:
Add this code to backbone.js file you are using
Backbone.View.prototype.eventAggregator = _.extend({}, Backbone.Events);
$(window).resize(function () {
Backbone.View.prototype.eventAggregator.trigger('window:resize');
});
Then bind the event aggregator to any view to want to use the resize event
this.eventAggregator.bind('window:resize',this.resize)
where this.resize is the function to be called for the resize event
How to call the updateh1Css when the page first load? For now, it works when the window is resized, but I also want to call it when the page first load.
var SubheaderView = Backbone.View.extend({
id: 'gallery',
tagName: 'div',
initialize: function() {
$(window).on('resize', this.updateh1Css);
this.render();
},
render: function() {
this.$el.append(subheaderTemplate);
},
updateh1Css: function() {
var pad_top = Math.floor($('#minor').height() * 0.4);
var pad_left = Math.floor($('#minor').width() * 0.07);
var pad = pad_top + 'px 0px 0px ' + pad_left + 'px';
$('#cover h1').css({'padding': pad});
}
});
Since you're using jQuery you can use $(document).ready
initialize: function() {
$(window).on('resize', this.updateh1Css);
$(document).ready(this.updateh1Css);
this.render();
}
This will make it run when the document first finishes loaded.
I have base class which extends Backbone.View class.
I pass a Jquery DOM element through the constructor, is this object a candidate for garbage collection?
var MainView = BaseView.extend({
initialize: function(o) {
this.container = o.myJqueryDomElement;
this.render();
},
render: function() {
this.container.append(this.$el);
},
dispose: function() {
this.remove();
}
});
in some point in my code I do this:
var mainView = new MainView({
myJqueryDomElement = $('#content')
});
first of all you should pass the jQuery element in as el, so
var MainView = BaseView.extend({
initialize: function(o) {
this.render();
},
render: function() {
this.html(someTemplate)
},
dispose: function() {
this.remove();
}
});
var mainView = new MainView({
el: $('#content')
});
Then this will be available throughout the object as a jQuery object via this.$el and the view shouldn't know about its container just the element it lives in. So if you want this to be the main view change your selector to be $('#content .main') or just $('#main')
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('' + this.menuItem.get("text") + '');
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('' + this.menuItem.get("text") + '');
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?
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>');