jQuery/JavaScript Execution Order - javascript

I have some items that I'd like the user to be able to filter using jQuery. The user selects their filter criteria and hits submit, and I make a call to my database. When this call completes, I fade out the existing containers with this function:
function clearGrid() {
var theseDivs = $('.grid-item');
$('.grid-item').fadeOut('fast', function() {
$(theseDivs).remove();
});
}
and then I append my new data with the following function:
function repopulate() {
<% #stuff.each do |gb| %>
$('#grid').append('<%= escape_javascript(render "/stuff", :gb => gb) %>');
<% end %>
resizeGrid();
}
The resizeGrid() function does some absolute positioning based on the other elements in the Grid. It seems like repopulate() is being called BEFORE the other elements are removed with clearGrid(), and thus the positioning of the new elements is off, and they're rendered as if the old elements are still there.
Is there a way to ensure that my repopulate() function doesn't get called until the other elements are well out of the way?

You should allow the clearGrid() function to call a callback once it's completed, and then pass it repopulate as that callback. Change clearGrid() to look like this:
function clearGrid(callback) {
var theseDivs = $('.grid-item');
theseDivs.fadeOut('fast', function() {
theseDivs.remove();
if(callback) {
callback();
}
});
}
And then, assuming your current code to call those two looks like this:
clearGrid();
repopulate();
You can change it to look like this:
clearGrid( repopulate );
Note: repopulate should not have () after it because you want to pass a reference to it, not call it.
Second note: I also changed clearGrid() to just use theseDivs rather than call jQuery again. It's slightly faster this way, although you probably won't be able to notice the difference.
If you know that there will only ever be <div> tags that you're working with, you could change the selector to $('div.grid-item') for another small speedup.

Try slowing down the repopulate method with
setTimeout( function(){ repopulate(); }, 50 );

Related

jQuery trigger function from code loaded by append

Is there anyway to call a function or trigger an event from html loaded from an append() call? I have a tag It is filled when a user licks on a list of things like this. in my index.html I have something like this:
function doThis(someData) {
$.get("/url/"+someData, function(htmlFromServer) {
$("#something").append(htmlFromServer);
});
}
function doSomething(moreData) {
alert(moreData);
}
I want to be able to do something like this in the returned html
<div>
<p>This is an important message</p>
<script>
doSomething("this message is different for each page");
</script>
</div>
I want to be able to call one function, but depending on what is returned, I alert a different message. I want the front end to call one endpoint, but what happens next is dynamic. I don't want to do a huge if block in my doThis(), or worse, a function for each possibility that "someData" may have.
You could pass a function to doThis. You also probably meant to concatenate the someData.
function doThis(someData, cb) {
$.get("/url/" + someData, function(htmlFromServer) {
$("#something").append(htmlFromServer);
cb()
});
}
doThis('somepath', () => console.log('append successful'));
I got this to work by defining all my functions in a javascript file and import like usual.
I then make a decision on the back end that all of the possible html that is returned by the jQuery get() call, has a hidden form field with the id of "x" and the value being the function name I want to call after the .append(htmlFromServer) is done.
So i have this function in a .js file defined int the head as usual. I then do this:
$.get("/url/"+someData, function(htmlFromServer) {
$("#myDiv").append(htmlFromServer).append(function() {
var functionToCall = $("#x").val(); // x is the id of the element
window[functionToCall]();
});
}
And I now have a generic way of handling different data depending on the server response without a big if block.

How to update cached jquery object after adding elements via AJAX

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).

How do I bind a "data appended" event in js / jquery?

At the moment, I have a piece of code looking like this:
$("#somediv").append(data);
somethingToDoAfterDataAppended();
It seems that the data is appended asynchronously, therefor the next function is not necessarily invoked after data is actually appended.
I was thinking about a way to bind this function with 'data appended' event - is it possible?
Any other solution would be equally useful.
It simple. You can change the append method and insert there a trigger(s) for you special event.
$.fn.append = (function(old){
return function(){
$(this).trigger('beforeAppend'); // if needed
var result = old.apply(this,arguments);
$(this).trigger('afterAppend');
return result;
}
})($.fn.append);
Somewhere in your initialization logic:
$("#somediv").bind('afterAppend',somethingToDoAfterDataAppended);
And :)
("#somediv").append(data);

JavaScript/DOM - Help removing my inline JavaScript

Being new to JavaScript I have not been able to come up with a solution to this issue.
I want each "Add to Cart" button to invoke the same function "AddtoCart". I have achieved this but at the cost of inline JavaScript - something I would like to avoid.
onclick=
"AddToCart(document.getElementById('toy_title1').innerHTML,document.getElementById('toy_quantity1').value,document.getElementById('toy_price1').innerHTML)
So how would I achieve including this as part of the external JavaScript file, bearing in mind I have to be able to apply this to all 4 unique items
You could change your function that way:
function AddToCart(toyId) {
var title = document.getElementById('toy_title'+toyId).innerHTML;
var quantity = document.getElementById('toy_quantity'+toyId).value;
var price = document.getElementById('toy_price')+toyId).innerHTML
}
Then on each button you just pass the toy's ID
Just be carefull about sensitive data like price, leaving it on Javascript(I'm supposing you will send it to your back-end after this) is dangerous, it could be easily manipulated.
But if your intention is just a test or something like that, its ok.
EDIT:
to call your this function you would do something like that:
onclick="AddToCart(1)"
Where 1 is your toy's ID, you should change it to 2,3... depending on your toy.
then you should read more about addEventListener(standard) and attachEvent(IE)
//assume element means the button
//you can use getElementsByTagName, getElementsByClassName, querySelectorAll etc.
//to fetch your elements
//DRY, store the operation in a function so it's reusabe and not written twice
function thisFunction(){
AddToCart(document.getElementById('toy_title1').innerHTML,
document.getElementById('toy_quantity1').value,
document.getElementById('toy_price1').innerHTML)
}
if(element.addEventListener){ //check if the standard is supported
element.addEventListener('click',function(){ //use it to add the handler
thisFunction();
});
} else {
element.attachEvent('onclick',function(){ //else, we use IE's version
thisFunction();
}, false);
}

Returning a value to another function

I am new to jQuery. I have created a form where I hide some fields. I have created a function on the click of a button field. Here in this function definition I unhide the hidden fields one being my text field and another a button. I code that I use is:
finishOrder: function() {
document.getElementById("create-pwd").style.display = "block"
document.getElementById("finish-ok").style.display = "block" // this is my another button
// do further processing
},
Now on the click of another button (please see the comment "this is my another button") I call another function like this:
FinishcheckPassword: function() {
var pas = document.getElementById("pos-password")
var user = new db.web.Model("res.users").get_func("read")(this.session.uid, ['password']).pipe(function(result) {
if(pas.value == result.password){
return true
});
},
After the if condition returns true value, I want to the control to be transferred to the first function where I can do further processing. Is it possible, if yes how can this be achieved? Any help will be appreciated.
Sure, something like this:
$('#finish-ok').click(function(){
if(FinishcheckPassword()){
finishOrder();
}
}
Of course, this is probably not exactly the right code for you. The fact that you are assigning all your functions with : rather than = suggests that they are inside of some larger object. Therefore, they'd have to be called like myObject.finishOrder(). But the general approach of what I wrote above will work.
As a couple side notes, you have tagged the question with jQuery and refer to it in your post, but there isn't actually a single line of jQuery in your code.

Categories