I'm using a modular pattern for writing my JS code. I have an object with references to all my dom elements. I have put the selector for a div that I'm adding at a later point in my code execution. When I add that div, and use jQuery.css using the reference I stored in my references object, it doesn't work.
NameSpace = {
objects: {
someButton: $('.someButton'),
someDiv: $('.someDiv'),
myDiv: $('.myDiv'), //This will be added later
//Other references
.
.
},
bindHandlers: {
NameSpace.objects.someButton.on('click', someEventHandler);
//Other bindings
.
.
},
eventHandlers: {
someEventHandler: function(e){
var div = jQuery('<div class="myDiv"></div>');
NameSpace.objects.someDiv.append(div);
//Doesn't work! If I use jQuery('.myDiv'), then it works,
//but kills my modular style
NameSpace.objects.myDiv.css({ //some styles });
},
//Other event handlers
}
}
//Other code
This approach works fine for objects that exist in the page, but isn't working for a div that I add like above.
Any help?
Javascript is procedural, this line myDiv: $('.myDiv') is computed only once and not everytime you call it.
It means that your selector $('.myDiv') is filled at the start of your page.
To resolve this you'd have to make your variable a function
objects: {
someButton: $('.someButton'),
someDiv: $('.someDiv'),
myDiv: function(){ return $('.myDiv'); }, //This will be added later
//Other references
.
.
},
It should recalculate the selector everytime you call it.
Let me know if the trick works.
Related
I've been searching for a few hours to try and find a solution to my issue, for some reason partially similar answers on here don't seem to be working for me - so I'm creating my own question.
Basically, I'm loading pre-rendered HTML from the server using jQuery's $.get method, and I need to split the HTML returned into two sections (one that's wrapped in a div called #section-one and the other simply alongside that div, with no parent element).
See the example below:
$.get('http://jamie.st/remote_file.php', function(data){
// I want to get '#section-one' and then remove it from data, basically splitting a single returned HTML resource into two, that can be placed in two different areas of the page.
var sectionOne = $(data).find('#section-one');
// This should only return the HTML of '#section-one'
console.log(sectionOne);
// Also how can I then remove '#section-one' from the 'data' variable? The equivalent of calling the below, but from the 'data' variables string/html.
$(sectionOne).remove();
// So eventually the below would return the HTML without the '#section-one' element (and it's children)
console.log(data);
});
I've also created a jsfiddle which you can play around with if you need to, it's set up to use a real PHP file that I've hosted for demo purposes.
http://jsfiddle.net/6p0spp23/6/
If you can submit a jsfiddle link back that would be much appreciated, thanks in advance guys!
When you create a jQuery object with the remote contents $(data) becomes a collection of elements so instead of find() you want to use filter() like so:
$.get('http://jamie.st/remote_file.php', function(data){
var $data = $(data),
$sectionOne = $data.filter('#section-one'),
$rest = $data.filter(':not(#section-one)');
console.log($sectionOne);
console.log($rest);
});
Demo fiddle
I think the best way to put the received data inside a parent div. Then you can call remove or any other method to use it.
You can make parent div hidden using .hide() method if you don't want to show it.
Here I did it:
http://plnkr.co/edit/jQKXyles8sP8dliB7v0K?p=preview
// Add your javascript here
$(function() {
$.get('http://jamie.st/remote_file.php', function(data) {
$("#parent").hide();
$("#parent").html(data);
$("#section-one").remove();
console.log($("#section-one").html())
alert($("#parent").html())
});
});
When you remove a subsection from a derived jQuery object, the original string is not updated with the change so if you want the updated html content you need to generate it from the jQuery object. One way to do this is to
$.get('http://jamie.st/remote_file.php', function (data) {
var $ct = $('<div />', {
html: data
});
// I want to get '#section-one' and then remove it from data, basically splitting a single returned HTML resource into two, that can be placed in two different areas of the page.
var sectionOne = $ct.find('#section-one');
// This should only return the HTML of '#section-one'
console.log(sectionOne);
// Also how can I then remove '#section-one' from the 'data' variable? The equivilant of calling the below, but from the 'data' variables string/html.
$(sectionOne).remove();
// So eventually the below would return the HTML without the '#section-one' element (and it's children)
console.log($ct.html());
});
Demo: Fiddle
I'm new to jQuery and I can get it to sometimes work, however, for some reason, when I try to call a function, it gives me the title error, but if I do it in developer tools, it works fine.
http://jsfiddle.net/otanan/pmzzLo3e/#&togetherjs=AezijhfBrj
It seems to work fine when retrieving the classes from the DOM, but not when I call a function such as
.click(function() {});
Here's the code:
var downloads = $(".info"),
className = "info_clicked";
for(var i in downloads)
{
downloads[i].click(function()
{
if(!this.hasClass(className))
this.addClass(className);
else
this.removeClass(className);
});
}
When you access a jQuery collection as an array, it returns the DOM elements, not jQuery objects. You should use .each() rather than for (i in downloads):
downloads.each(function() {
$(this).click(function() {
if (!$(this).hasClass(className)) {
$(this).addClass(className);
} else {
$(this).removeClass(className);
}
});
});
You could also simplify the whole thing to:
downloads.click(function() {
$(this).toggleClass(className);
});
Most jQuery methods automatically iterate over all the elements in a collection if it makes sense to do so (the notable exceptions are methods that return information from the element, like .text() or .val() -- they just use the first element). So you generally only have to iterate explicitly if you need to do different things for each element. This is one of the great conveniences of using jQuery rather than plain JS: you rarely have to write explicit iterations.
I think the issue is that you're attempting to call a jQuery function on an object that is no longer a jQuery object.
For example you're saying $(".info"). Which retrieves a single jQuery object. As soon as you index that object downloads[i] it is no longer a jQuery object, it is a plain HTML element and does not have a click function available.
What you really need to do is get the jQuery object for the indexed item:
var downloads = $(".info"),
className = "info_clicked";
for(var i = 0; i < downloads.length; i++)
{
$(downloads[i]).click(function()
{
if(!this.hasClass(className))
this.addClass(className);
else
this.removeClass(className);
});
}
try it:
$(downloads[i]).click(function(){ //...
I have the following script which is working nicely to hide a DIV when its child is empty:
jQuery(".field-items").filter(function() {
return !$.trim(this.innerHTML);
}).parent().parent().parent().parent().parent().parent().hide();
If that same DIV is empty from above I also want to hide another DIV on the same page. It's not a parent.
How can I add the following code to the above code? So that both occur when that specific DIV is empty?
$('#survey-monkey-title').hide();
var $empty = jQuery(".field-items").filter(function() {
return !$.trim(this.innerHTML);
});
if ($empty.length) {
$empty.parent().parent().parent().parent().parent().parent().hide();
$('#survey-monkey-title').hide();
}
I'd also like to give Brian Giaz his propers for utilizing .add() below:
$empty.parent().parent().parent().parent().parent().parent().add('#survey-monkey-title').hide();
You can use the add() function to add additional elements to the jquery object:
$('#elem').parent().add('#otherElem').hide();
for example.
I know you already got it working, but consider updating the stringed parent() calls to just a single parentUntil function.
var $empty = jQuery(".field-items").filter(function() {
return !$.trim(this.innerHTML);
});
if ($empty.length) {
$empty.parentsUntil('.someSelector').hide();
$('#survey-monkey-title').hide();
}
I'm trying to write a plugin-like function in jQuery to add elements to a container with AJAX.
It looks like this:
$.fn.cacheload = function(index) {
var $this = $(this);
$.get("cache.php", {{ id: index }).done(function(data) {
// cache.php returns <div class='entry'>Content</div> ...
$(data).insertAfter($this.last());
});
}
and I would like to use it like this:
var entries = $("div.entry"),
id = 28;
entries.cacheload(id);
Think that this would load another "entry"-container and add it to the DOM.
This is works so far. But of course the variable that holds the cached jQuery object (entries) isn't updated. So if there were two divs in the beginning and you would add another with this function it would show in the DOM, but entries would still reference the original two divs only.
I know you can't use the return value of get because the AJAX-call is asynchronous. But is there any way to update the cached object so it contains the elements loaded via AJAX as well?
I know I could do it like this and re-query after inserting:
$.get("cache.php", {{ id: num }).done(function(data) {
$(data).insertAfter($this.last());
entries = $("div.entry");
});
but for this I would have to reference the variable holding the cached objects directly.
Is there any way around this so the function is self-contained?
I tried re-assigning $(this), but got an error. .add() doesn't update the cached object, it creates a new (temporary) object.
Thanks a lot!
// UPDATE:
John S gave a really good answer below. However, I ended up realizing that for me something else would actually work better.
Now the plugin function inserts a blank element (synchronously) and when the AJAX call is complete the attributes of that element are updated. That also ensures that elements are loaded in the correct order. For anyone stumbling over this, here is a JSFiddle: http://jsfiddle.net/JZsLt/2/
As you said yourself, the ajax call is asynchronous. Therefore, your plugin is asynchronous as as well. There's no way for your plugin to add the new elements to the jQuery object until the ajax call returns. Plus, as you discovered, you can't really add to the original jQuery object, you can only create a new jQuery object.
What you can do is have the plugin take a callback function as a second parameter. The callback could be passed a jQuery object that contains the original elements plus the newly inserted ones.
$.fn.cacheload = function(index, callback) {
var $this = this;
$.get('cache.php', { id: index }).done(function(html) {
var $elements = $(html);
$this.last().after($elements);
if (callback) {
callback.call($this, $this.add($elements));
}
});
return $this;
};
Then you could call:
entries.cacheload(id, function($newEntries) { doSomething($newEntries); } );
Of course, you could do this:
entries.cacheload(id, function($newEntries) { entries = $newEntries; } );
But entries will not be changed until the ajax call returns, so I don't see much value in it.
BTW: this inside a plugin refers to a jQuery object, so there's no need to call $(this).
I really can't figure out how I would do this. It's more of a concept question than a code question so I'll just post an example:
object = $('#div');
function doSomething(object) {
//iterates through a list and creates a UL with items in corresponding to that list.
$(body).append("<li id='clickme'>Hello world</li>");
}
function createModal(object) {
//creates modal dialogue.
doSomething(object);
//more stuff
}
$('#clickme').live("click", function() {
//I need access to object (not the object declared at first,
//the object passed into doSomething) here.
});
Any ideas how I would do such a thing? doSomething would create a set of LIs and have a parameter passed into it. When those LIs the function creates are clicked, they need to interact with the parameter that's passed into doSomething. Is there a way to bind them or something?
Sorry if I didn't make any sense.
You can use jquery data function to associate data to your DOM elements. You then can read those data when handling events.
An alternate way, generally not recommended but useful when you build your html in one big pass and don't have an easy access to the DOM elements, and only have strings (or keys), is to add an attribute and retrieve it later using jquery's attr function. Whenever possible I recommend you to use the data function though.
Store the reference explicitly:
function doSomething(object) {
//iterates through a list and creates a UL with items in corresponding to that list.
$(body).append(
$("<li/>", { id: 'clickme', text: 'Hello world',})
.data('object', object)
);
}
Then the event handler can retrieve the reference:
$('#clickme').live("click", function() {
var object = $(this).data('object');
// ...
});
Also .live() is deprecated:
$('body').on('click', '#clickme', function() {
is the hip new way to bind delegated event handlers.
object = $('#div');
function doSomething(object) {
$(body).append("<li id='clickme'>Hello world</li>");
$('#clickme').click(function(evt) {
// Here you have access to `object`
});
}
function createModal(object) {
//creates modal dialogue.
doSomething(object);
//more stuff
}
This might not be enough. If you are creating multiple links rather than just the single one with id clickme you might have to find a different selector to use when you attach the click-handler. But if you nest the function that way, you have access to the parameter object that was used when the click-handler was created.
Another option would be to declare and attach the handler in a location where the parameter would be in scope, through closures (not tested):
function doSomething(object) {
$(body).append("<li id='clickme'>Hello world</li>").click(function() {
//object is accessible here
});
}