I'm having trouble accessing some elements in an object.
Here in my view file (aka HTML page), I'm initializing the c App.
//Yh script tags are wrapped around this
$(function() {
App.initialize();
});
Then in my JS file (this is a simple form of what I'm actually working on):
window.App = {
el: {
slider: $("#form-slider"),
allSlides: $(".slide")
},
initialize: function() {
this.ProductNewEvents();
//bla bla
// and so on
},
slideTiming: 800,
slideWidth: 790,
delegat etc
ProductNewEvents: function() {
//A whole bunch of events
this.el.slider.click(function() {
alert("Why you no work");
});
$("#form-slider").click(function() {
alert("Why you work");
});
},
some other objs
};
My problem here is I cannot call this.el.allSlides.some jQuery events or this.el.slider. Let's say animate or click in any of the objects. I have to fetch it from the DOM to bind any event to an element e.g. $(".slide").animate.
It depends what you are doing, since I can't see all of your code. When you access your App.el.slider property it holds a reference to $('#slider') at the time it was created. To overcome this your code may have to look something like:
window.App = {
el: {
slider: function(){
$("#form-slider");
},
allSlides: function(){
$(".slide");
}
},
initialize: function() {
this.ProductNewEvents();
//bla bla
// and so on
},
slideTiming: 800,
slideWidth: 790,
//delegat etc
ProductNewEvents: function() {
//A whole bunch of events
},
//some other objs
}
App.el.slider();
Related
currently I have the JS below. I'm trying to run the slideout.close(); event with smoothstate onStart. My code is below and i'm currently getting an error in the console. Could someone please help me out.
Thanks.
$(document).ready(function() {
slideout();
});
function slideout() {
var slideout = new Slideout({
'panel': document.getElementById('slideout-content'),
'menu': document.getElementById('slideout-nav'),
'padding': 256,
'tolerance': 70,
'side': 'right'
});
$('.mobile-nav__icon').on('click', function() {
slideout.toggle();
});
}
$(function(){
'use strict';
var options = {
prefetch: true,
cacheLength: 2,
onStart: {
duration: 860,
render: function ($container) {
$container.addClass('is-exiting');
smoothState.restartCSSAnimations();
slideout.close();
}
},
onReady: {
duration: 0,
render: function ($container, $newContent) {
$container.removeClass('is-exiting');
$container.html($newContent);
}
},
onAfter: function() {
slideout();
}
},
smoothState = $('#animate-wrapper').smoothState(options).data('smoothState');
});
You've created a global function called slideout, within which you have a local variable called slideout that is the one that refers to a Slideout object - you can't access that local variable from other functions. When you try to use slideout.close() that is looking for a .close() method on the function.
One fix would be to change the name of the variable or function and make the variable global too, so that you can access it anywhere. But adding more globals is messy.
I think it should be fine to combine all of your code into a single document ready handler, so that everything is in the same scope without needing any globals (you would still need to use a different name for the variable).
I can't test the following because I don't have whatever Slideout is, but:
$(document).ready(function() {
'use strict';
var slideout; // variable that is local to the doc ready handler function
// and accessible to all code within that handler
function initSlideout() { // function renamed
slideout = new Slideout({ // assign to variable declared above
'panel': document.getElementById('slideout-content'),
'menu': document.getElementById('slideout-nav'),
'padding': 256,
'tolerance': 70,
'side': 'right'
});
}
initSlideout();
$('.mobile-nav__icon').on('click', function() {
slideout.toggle();
});
var options = {
prefetch: true,
cacheLength: 2,
onStart: {
duration: 860,
render: function ($container) {
$container.addClass('is-exiting');
smoothState.restartCSSAnimations();
slideout.close();
}
},
onReady: {
duration: 0,
render: function ($container, $newContent) {
$container.removeClass('is-exiting');
$container.html($newContent);
}
},
onAfter: function() {
initSlideout();
}
},
smoothState = $('#animate-wrapper').smoothState(options).data('smoothState');
});
I am learning the backbone currently, and facing the following problem.
I want to append new elements to the this.el element of the View. But it doesn't happen.
here is my code:
var MyData = Backbone.Model.extend({
initialize: function() {
this.bind('error', function(model, error) {
console.log('error:' + error);
});
},
defaults: {
name: "Jo",
age: 18,
skill: 0
},
validate: function(attributes) {
if (attributes.name == "Jonh")
return false;
return true;
}
});
var MyView1 = Backbone.View.extend({
initialize: function() {
this.$el.empty();
},
el: '#middle',
events: {
"click": "render"
},
render: function() {
this.$el.append(this.model.get('name'));
}
});
var myData = new MyData();
var myView1 = new MyView1( {model: myData} );
int the html file there is a div element defined as follows:
<div id="middle"></div>
The code works without error, but I dont see any appended elements.
I have also tried to append like this:
this.$el.append($("<p>").append(this.model.get('name')));
Since your javascript script loads before the page is loaded, Backbone View can't see #middle div, so you have to initialize views after page is loaded:
$(document).ready(function() {
// load your views
});
I just loaded this up in my IDE and it worked fine. You console didn't show you any errors?
So for some reason navigate won't work in one of my views. I'm doing everything in one file for now, so that may be the problem. Also I know the code is horrible, I'm just messing around with backbone right now.
EDIT: I put a console.log() in MarketingPage's function route and it never gets called, so there must be something wrong with the view.
Also, this is the error I'm getting from chrome dev tools:
Error in event handler for 'undefined': IndexSizeError: DOM Exception 1 Error: Index or size was negative, or greater than the allowed value.
at P (chrome-extension://mgijmajocgfcbeboacabfgobmjgjcoja/content_js_min.js:16:142)
at null.<anonymous> (chrome-extension://mgijmajocgfcbeboacabfgobmjgjcoja/content_js_min.js:18:417)
at chrome-extension://mgijmajocgfcbeboacabfgobmjgjcoja/content_js_min.js:1:182
at miscellaneous_bindings:288:9
at chrome.Event.dispatchToListener (event_bindings:390:21)
at chrome.Event.dispatch_ (event_bindings:376:27)
at chrome.Event.dispatch (event_bindings:396:17)
at Object.chromeHidden.Port.dispatchOnMessage (miscellaneous_bindings:254:22)
Here's my code:
/*global public, $*/
window.public = {
Models: {},
Collections: {},
Views: {},
Routers: {
},
init: function () {
console.log('Hello from Backbone!');
}
};
var App = Backbone.Router.extend({
routes: {
'': 'index',
'register': 'route_register',
},
index: function(){
var marketing_page = new MarketingPage();
},
route_register: function(){
var register_view = new RegisterView();
}
});
window.app = new App();
var User = Backbone.Model.extend({
url: '/user',
defaults: {
email: '',
password: ''
}
});
var MarketingPage = Backbone.View.extend({
initialize: function(){
this.render();
},
render: function(){
var template = _.template($("#marketing-page").html());
$('.search-box').after(template);
},
events: {
'dblclick': 'route'
},
route: function(e){
e.preventDefault();
console.log("In route");
window.app.navigate('register', {trigger: true});
this.remove();
}
});
var RegisterView = Backbone.View.extend({
initialize: function() {
this.render();
},
render: function(){
var template = _.template($("#register-template").html());
$('.search-box').after(template);
}
});
$(document).ready(function () {
Backbone.history.start();
});
When I type host/#register into the browser directly, the register view gets rendered, but no matter what I do the click event won't seem to work...
Since the handler function route isn't being called, it's likely that the event delegation isn't working.
One thing to note is that the event handling that is set up in a Backbone View is scoped to only that view's el. I don't see where yours is set up explicitly, so it might be creating an empty div, then handling events inside that empty div (which you don't want).
One trick I use for quick prototypes is to set the view's el with a jQuery selector pointing to something that exists on the page already, then in the render, show it with a .show().
Since you're not really doing that, here's one thing you could try. What we're doing is setting the $el content and then calling delegateEvents to make sure that the events and handlers are being bound.
var MarketingPage = Backbone.View.extend({
initialize: function(){
this.render();
},
render: function(){
this.$el.html(_.template($("#marketing-page").html()));
$('.search-box').after(this.$el);
this.delegateEvents();
},
events: {
'dblclick': 'route'
},
route: function(e){
e.preventDefault();
console.log("In route");
window.app.navigate('register', {trigger: true});
this.remove();
}
});
Backbone.js views delegateEvents do not get bound (sometimes)
http://backbonejs.org/#View-delegateEvents
Here is a example using emberjs router http://jsbin.com/agameq/edit.
Now I wanna have some showup animation, like fadeIn or fadeOut, when route changed. what should I do?
Every View in ember has a method named didInsertElement:
Called when the element of the view has been inserted into the DOM.
Override this function to do any set up that requires an element in
the document body.
All ember views also have a $ which is a reference to jQuery, so you can wrap some element in your view with it and apply any changes to it such as:
// this will animate only the tag h2, which in your case is the title of the users view
App.UsersView = Ember.View.extend({
templateName: 'users',
didInsertElement: function() {
this.$('h2').animate({ fontSize: "3em" }, 900 );
}
});
Or you can call it without arguments (like $()) to return the current view wrapped by jQuery.
To animate a view as you enter in that view/route, do this in your App.UsersView:
// this will animate the entire view
App.UsersView = Ember.View.extend({
templateName: 'users',
didInsertElement: function() {
this.$().animate({ fontSize: "3em" }, 900 );
}
});
(Note: my animation is pretty lame, but it's just to show where to call the methods, do a real animation)
Here's a modified version of your JSBin
Following the answer from #MilkyWayJoe, you probably want to hide the View before inserting it, by setting the isVisible property to false:
App.UsersView = Ember.View.extend({
templateName: 'users',
isVisible: false,
didInsertElement: function() {
var self = this;
this.$().fadeIn(700, function(){
self.set('isVisible', true); //inform observers of `isVisible`
});
}
});
Or use this animation Mixin, which allows you to animate Views by changing a set of observed CSS properties:
App.UsersView = Ember.View.extend( JQ.Animate, {
templateName: 'users',
isVisible: false,
// Observed CSS properties
cssProperties: ['display'],
// Optional animation properties
duration: 700,
easing: 'easeInOutCubic',
didInsertElement: function() {
this.set('display', 'block');
},
afterAnimate: function() {
this.set('isVisible', true);
}
});
I've looked around for a while now and have not been able to find anything that suggests what the cause of this is.
My code:
var faqView = Backbone.View.extend({
tagName: 'div',
id: 'faq-list',
initialize: function() {
var view = this;
this.collection = new faqCollection();
this.collection.fetch({
success: function(collection, response) {
collection.each(function(faq){
view.$el.append(_.template($('script#faq_item').html(),{faq: faq.attributes}));
});
},
error: function(collection, response) {
view.$el.html("<p>Unable to get the FAQ items.<br>Please try again later.</p>");
}
});
},
render: function() {
this.$el.appendTo('div#container');
return this;
},
events: {
'click h3': 'toggleAnswer'
},
toggleAnswer: function(event) {
console.log(this);
console.log(event);
}
});
var router = Backbone.Router.extend({
routes: {
"faq": "faq",
"*other": "defaultRoute"
},
faqView: {},
initialize: function() {
this.faqView = new faqView();
},
defaultRoute: function() {
this.resetPage();
},
faq: function() {
this.resetPage();
$('body').addClass('page-faq');
this.faqView.render();
},
resetPage: function() {
$('body').removeClass('page-faq');
this.faqView.remove();
}
});
The above code is included as the last items in the <body>. The HTML is as follows.
<body>
<div id="container">
</div>
<script type="text/template" id="faq_item">
<h3 class="contracted"><span>{{faq.question}}</span></h3>
<p style="display: none;">{{faq.answer}}</p>
</script>
<script type="text/javascript" src="./js/models.js"></script>
<script type="text/javascript" src="./js/views.js"></script>
<script type="text/javascript" src="./js/collection.js"></script>
<script type="text/javascript" src="./js/router.js"></script>
<script type="text/javascript">
//<![CDATA[
$(function() {
var app = new router;
Backbone.history.start();
});
//]]>
</script>
</body>
All the required elements exist (as far as I can tell) and I'm not manually setting the el attribute of the View. I'm lost as to why the events are not binding/firing when the <h3> is clicked.
Edit No errors thrown and the functionality works if I don't use the router and create the view by it self. e.g.
var app = new faqView();
app.render();
Your problem is in your router. Right here in fact:
resetPage: function() {
$('body').removeClass('page-faq');
this.faqView.remove();
}
View#remove is just jQuery's remove on the view's el by default and that:
[...] method takes elements out of the DOM. [...] all bound events and jQuery data associated with the elements are removed
So once you this.faqView.remove(), the delegate handler that drives the view's events is gone.
The usual approach is to create and destroy views as needed instead of creating a view and caching it for later. Your router should look more like this:
var router = Backbone.Router.extend({
routes: {
"faq": "faq",
"*other": "defaultRoute"
},
defaultRoute: function() {
this.resetPage();
},
faq: function() {
this.resetPage();
$('body').addClass('page-faq');
this.view = new faqView();
this.view.render();
},
resetPage: function() {
$('body').removeClass('page-faq');
if(this.view)
this.view.remove();
}
});
Demo: http://jsfiddle.net/ambiguous/aDtDT/
You could try messing around with detach inside an overridden remove method in faqView as well but there's really no need to have an instance of faqView around all the time: create it when you need it and remove it when you don't.