Save query selector in variable to append doesnt work - javascript

I want to save an html element inside a variable and than append that element with childelements. I can append that element with the usual jquery selector but not with the variable. Why?
Here is my snippet:
var elements, UserTableWidget = {
elements: {
pagination: $("#pageNumbers")
},
init: function() {
elements = this.elements;
elements.pagination.append("<li>test</li>") // works not
$("#pageNumbers").append("<li>test</li>") // works
},
}

Why is everything so complicated? I simplified some of it.
$(function(){
var UserTableWidget = {
elements: {
pagination: $("#pageNumbers")
},
init: function() {
this.elements.pagination.append("<li>test</li>");
},
}
UserTableWidget.init()
// same as: UserTableWidget.elements.pagination.append("<li>test</li>");
});
As mentioned in the comments, this.elements.pagination is initialized to ("#pageNumbers") probably before the dom is ready. When you call init() later, it uses the value calculated before (empty jQuery collection), so nothing is appended.

Related

cache a DOM element inside an instantaneus function with JQuery [duplicate]

I am having some trouble accessing cached DOM element from the namespace variable. My FeedManager config variable is like this:
var FeedManager = {
config : {
$feedContainer : $('#feedContainer'),
feedUrl : 'http://rss......',
feedLimit : 10
},....
Here the $feedContainer is the ul element in the html. I wanted to add li elements built from the feed. I have a funciton init() inside FeedManager object. But somehow it can not add the child element.
init: function(){
var feed = new google.feeds.Feed(FeedManager.config.feedUrl);
......
......
// this is not working
FeedManager.config.$feedContainer.append(li);
but if inside init() function,I again get the feedContainer element ,it works !
var feedContainer = $('#feedContainer');
feedContainer.append(li);
What can be the problem? and how can I use cached dom element initialzied in my config object.
You'd need to make sure the $('#feedContainer') has already been loaded.
An analogous example:
<script type="text/javascript">
$(function() {
var Manager = {
config: {
$p: $("p#hello")
},
init: function() {
var label = Manager.config.$p.html();
console.log(label);
}
};
Manager.init();
});
</script>
Is your #feedContainer element present in your HTML or is it fetched later? If it is fetched later than you can't cache the element, you'd have to use just a selector string like
feedContainer: '#feedContainer'`
in your FeedManager object and then use something like
$(FeedManager.config.feedContainer).append(li);
If it is present in HTML then make sure that you define your FeedManager object after the DOM is ready, ie. inside of a
$(document).ready(function () {
// ...
});
or $(function () { ... });

Efficiently removing items from the DOM

This is kind of a nitpicky question, but I'm curious which would be a more efficient implementation.
I'm adding an overlay and a spinner over an element, then later I'm removing it.
I can define two methods:
addSpinner: function() {
$('<div></div>').addClass('overlay').appendTo('#myDiv');
$('<img>').attr('src', 'images/spinner.gif').addClass('spinner').appendTo('#myDiv');
},
removeSpinner: function() {
$('.overlay, .spinner', this.el).remove();
},
The remove spinner is going to have to look for those elements each time I call it. So my other option is to have addSpinner re-define removeSpinner with a more direct reference to the elements.
addSpinner: function() {
var overlay = $('<div></div>').addClass('overlay').appendTo('#myDiv');
var spinner = $('<img>').attr('src', 'images/spinner.gif').addClass('spinner').appendTo('#myDiv');
this.removeSpinner = function() {
overlay.remove();
spinner.remove();
};
},
This means no searching the DOM for those two elements, but the function is redefined each time. Pros and Cons?
I don't think it's relevant but these are Backbone/Marionette views.
The most efficient would probably be to keep a reference to the elements that you can use when you remove them, something like
var obj = {
addSpinner: function() {
this.overlay = $('<div />', {'class' : 'overlay'});
this.spinner = $('<img />', {'class' : 'spinner', src : 'images/spinner.gif'});
$('#myDiv').append(this.overlay, this.spinner);
},
removeSpinner: function() {
this.overlay.add(this.spinner).remove();
}
}
That way you don't have to call jQuery or query the DOM uneccessary

$.each function to show each DIV, one at a time

I have the following function. To make a long story short, I have 4 divs that I want to apply an init class to. The init class essentially shows the 4 divs.
It works as it should, however it shows all 4 divs at the same time, which is not what I want. I want to show 1 div, then the next, then the next, etc.
Where am I going wrong?
$('.campaign-item').waypoint({
handler: function() {
var $this = $(this);
$this.each(function(i) {
setTimeout(function() {
$this.addClass('init');
}, i * 500 );
});
},
offset: '60%'
});
$('.campaign-item').each(function (i) {
var $this = $(this);
$this.waypoint({
handler: function() {
setTimeout(function() {
$this.addClass('init');
}, i * 500 );
},
offset: '60%'
});
});
You need to add the class to each individual element, not to all of them.
var $this = $(this);
$this.each(function(i,v){
setTimeout(function(){
$(v).addClass('init');
}, i * 500 );
});
I think your problem is that a jQuery object contains a collection of the objects that match your selector. "$(this)" is that collection. The "each" function gives you access to the individual elements in that collection with the returned second parameter ("function(index, element)"). In your code you are trying to add your "init" class to every member of the collection because "$this" IS the entire collection. You need to add the class to the second parameter returned in each iteration of your each function.

jQuery switching between more than two classes

I've already posted a question about jQuery toggle method here
But the problem is that even with the migrate plugin it does not work.
I want to write a script that will switch between five classes (0 -> 1 -> 2 -> 3 -> 4 -> 5).
Here is the part of the JS code I use:
$('div.priority#priority'+id).on('click', function() {
$(this).removeClass('priority').addClass('priority-low');
});
$('div.priority-low#priority'+id).on('click' ,function() {
$(this).removeClass('priority-low').addClass('priority-medium');
});
$('div.priority-medium#priority'+id).on('click', function() {
$(this).removeClass('priority-medium').addClass('priority-normal');
});
$('div.priority-normal#priority'+id).on('click', function() {
$(this).removeClass('priority-normal').addClass('priority-high');
});
$('div.priority-high'+id).on('click', function() {
$(this).removeClass('priority-high').addClass('priority-emergency');
});
$('div.priority-emergency'+id).on('click', function() {
$(this).removeClass('priority-emergency').addClass('priority-low');
});
This is not the first version of the code - I already tried some other things, like:
$('div.priority#priority'+id).toggle(function() {
$(this).attr('class', 'priority-low');
}, function() {
$(this).attr('class', 'priority-medium');
}, function() {
...)
But this time it only toggles between the first one and the last one elements.
This is where my project is: strasbourgmeetings.org/todo
The thing is that your code will hook your handlers to the elements with those classes when your code runs. The same handlers remain attached when you change the classes on the elements.
You can use a single handler and then check which class the element has when the click occurs:
$('div#priority'+id).on('click', function() {
var $this = $(this);
if ($this.hasClass('priority')) {
$this.removeClass('priority').addClass('priority-low');
}
else if (this.hasClass('priority-low')) {
$this.removeClass('priority-low').addClass('priority-medium');
}
else /* ...and so on... */
});
You can also do it with a map:
var nextPriorities = {
"priority": "priority-low",
"priority-low": "priority-medium",
//...and so on...
"priority-emergency": "priority"
};
$('div#priority'+id).on('click', function() {
var $this = $(this),
match = /\bpriority(?:-\w+)?\b/.exec(this.className),
current = match && match[0],
next = nextPriorities[current];
if (current) {
$this.removeClass(current).addClass(next || 'priority');
}
});
[edit: working demo]
Assuming you have 'priority' as the default class already on the element at the initialization phase, this will cycle through the others:
$('div#priority' + id)
.data('classes.cycle', [
'priority',
'priority-low',
'priority-medium',
'priority-normal',
'priority-high',
'priority-emergency'
])
.data('classes.current', 0)
.on('click', function () {
var $this = $(this),
cycle = $this.data('classes.cycle'),
current = $this.data('classes.current');
$this
.removeClass(cycle[current % cycle.length])
.data('classes.current', ++current)
.addClass(cycle[current % cycle.length]);
});
I have tried myself to do this with the sole help of toggleClass() and didn't succeeded.
Try my method that declares an array with your five classes and toggles dynamically through
them.Do adapt to your own names.
//variable for the classes array
var classes=["one","two","three","four","five"];
//add a counter data to your divs to have a counter for the array
$('div#priority').data("counter",0);
$(document).on('click','div#priority',function(){
var $this=$(this);
//the current counter that is stored
var count=$this.data("counter");
//remove the previous class if is there
if(($this).hasClass(classes[count-1])){
$(this).removeClass(classes[count-1]));
}
//check if we've reached the end of the array so to restart from the first class.
//Note:remove the comment on return statement if you want to see the default class applied.
if(count===classes.length){
$this.data("counter",0);
//return;//with return the next line is out of reach so class[0] wont be added
}
$(this).addClass(classes[count++]);
//udpate the counter data
$this.data("counter",count);
});
//If you use toggleClass() instead of addClass() you will toggle off your other classes.Hope is a good answer.

why is my element not targeted when reload through AJAX

I'm using object literals on my project. I'm targeting selecting with jquery. It works fine the first time but when the part I'm targeting is reloaded with AJAX I can't target those element anymore. But I look into firebug they're there...
I'm even doing console.log() to test if my code works and it works but it just doesn't want to pick those. So in order for it to work, I have to refresh the entire browser.
Do you know what's the deal with AJAX dom reload and selectors.
I think it's something to do with the DOM reloading and redrawing itself or something along those lines...
Here is my code:
Module.editWishlistTitle = {
wishListContent: $('.mod-wish-list-content'),
title: $('.title').find('h2'),
titleTextField: $('#wishlist-title-field'),
titleInnerContainer: $('.title-inner'),
editTitleForm: $('.edit-title-form'),
submitCancelContainer: $('.submit-cancel'),
notIE9: $.browser.msie && $.browser.version < 9,
edit: function () {
var fieldTxt = this.titleTextField.val(),
editForm = this.editTitleForm,
titleParent = this.titleInnerContainer,
fieldCurrentTitle = this.title.text();
this.titleTextField.val(fieldCurrentTitle);
this.submitCancelContainer.removeClass('hidden');
if (!this.notIE9) {
editForm.css('opacity', 0).animate({ opacity: 1 }).removeClass('hidden');
titleParent.addClass('hidden').animate({ opacity: 0 });
console.log(editForm);
} else {
editForm.removeClass('hidden');
titleParent.addClass('hidden');
}
}
init: function () {
var self = this;
console.log(this.editTitleForm);
//edit
this.wishListContent.delegate('.edit-title a', 'click', function (e) {
self.edit();
e.preventDefault();
});
};
If you are replacing an element on the page, you are destroying the original reference to the element. You need to redo the reference to point to the new element.
Create a new method in your code that (re)initializes the references you need. Instead of adding them in the odject, set them in the method.
Basic idea:
Module.editWishlistTitle = {
wishListContent: $('.mod-wish-list-content'),
title: $('.title').find('h2'),
//titleTextField: $('#wishlist-title-field'),
...
...
initReferences : function(){
this.titleTextField = $('#wishlist-title-field');
},
...
...
init: function () {
this.initReferences();
...
...
And when your Ajax call comes back you just need to call initReferences again.
After DOM ready, if you inject any data / class / id will not be available in DOM, so better you use live or delegate to get your new data access.
http://api.jquery.com/delegate/
Best to use delegate, that will take care your new data loaded after dom ready, that way you can avoid to refresh /reload your page.

Categories