Initializing jQuery.load'ed elements - javascript

I'm loading part of my webpage using AJAX, in particular jQuery.load(). With this the usual jQuery pattern
$('.classname').click(...) // Handler
// or, working with bootstrap
$("a[rel=tooltip]").tooltip() // Function
or similar obviously don't work any more, because they are called only when the page is loaded. I realize there is jQuery.on for the first example, but how would I implement the second?
Is there a simple (builtin) way to also apply these to jQuery.loaded stuff, or do I have to work around it myself? Seems like a problem a lot of people should be having.

You have to work around it yourself; but you can easily to this by calling $("a[rel=tooltip]").tooltip() in the callback for load():
$('#blah').load('/somewhere.html', function () {
$('#blah').find('a[rel="tooltip"]').tooltip();
});
Be sure to restrict the tooltip() call to only newly loaded elements, or you'll end up initializing tooltip multiple times per element (which may result in weird behaviour).
To avoid duplicating code you just need to define a helper function;
function initTooltip(root) {
return $(root || document).find('a[rel="tooltip"]').tooltip();
}
Which allows you to init tooltip. The parameter is optional, and lets you restrict initialization to only descendants of the provided element, e.g.:
jQuery(document).ready(function ($) {
initTooltip(); // equivilant to initTooltip(document);
$('#blah').load('/somewhere.html', function () {
initTooltip('#blah');
});
});

Related

After remapping jQuery to $ in my WordPress theme, I can no longer trigger functions from outside the js file

I'm working on a WordPress theme with a lot of jQuery in it. By default, WordPress doesn't allow you to use the $ shortcut and you have to use the full jQuery instead - e.g. jQuery('.class') rather than $('.class').
This isn't too much of a hassle over a few lines of code, but I've got a lot now, so I remapped jQuery to $ using:
(function($){
...my functions here...
})(window.jQuery);
This works fine for functions triggered from within that file, but if I use any inline triggers in the PHP, they no longer work. For example:
<a onclick="loadFullPost('<?=get_permalink()?>?ajax=true','<?=$post->post_name?>',<?=$post->ID?>)">Read more</a>
worked fine before remapping but doesn't now. I can't bind the event as usual within the js file, because I won't be able to access the PHP and WordPress functions I need - unless I'm missing something. For example, this wouldn't work:
$( "#target" ).click(function() {
loadFullPost('<?=get_permalink()?>?ajax=true','<?=$post->post_name?>',<?=$post->ID?>)
});
Is there any way around this?
The issue is that your functions are no longer globals. This is a Good Thing™. (See below for why.)
Is there any way around this?
By far the best way would be to not hook up events like that. Instead, keep your code and markup separate, and hook up your functions using jQuery's on and similar. See below for more.
But if you feel you have to, you can make your functions globals by assigning them as properties on window:
(function($) {
window.loadFullPost = function() {
// ...
};
)(jQuery);
or
(function($) {
function loadFullPost() {
// ...
}
window.loadFullPost = loadFullPost;
)(jQuery);
So how would you do
<a onclick="loadFullPost('<?=get_permalink()?>?ajax=true','<?=$post->post_name?>',<?=$post->ID?>)">Read more</a>
...without using a global function? Like this:
<a class="load-full-post" data-permalink="<?=get_permalink()?>" data-ajax=true data-post-name="<?=$post->post_name?>" data-post-id="<?=$post->ID?>">Read more</a>
and then one handler for them
$(document).on("click", ".load-full-post", function() {
var link = $(this);
// Use the values from link.attr("data-permalink") and such
// to do the work
});
Or if you wanted to use your existing loadFullPost function:
$(document).on("click", ".load-full-post", function() {
var link = $(this);
return loadFullPost(
link.attr("data-permalink"),
link.attr("data-ajax") === "true",
link.attr("data-post-name"),
+link.attr("data-post-id") // (+ converts string to number)
);
});
I should mention that you'll get people telling you to access those data-* attributes via the data function. You can do that, but unless you're using the various additional features of data, it's unnecessary overhead (creating jQuery's cache for the data, etc.). data is not an accessor function for data-* attributes, it's much more (and less) than that.
You can also pass your information as JSON:
<a class="load-full-post" data-postinfo="<?=htmlspecialchars(json_encode(array("permalink" => get_permalink(), "ajax" => true, "name" => $post->post_name, "id" => $post->ID))0?>">Read more</a>
(or something like that, my PHP-fu is weak)
Then:
$(document).on("click", ".load-full-post", function() {
var postInfo = JSON.parse($(this).attr("data-postinfo"));
return loadFullPost(
postInfo.permalink,
postInfo.ajax,
postInfo.name,
postInfo.id
);
});
Why making your functions non-global is a Good Thing™: The global namespace is very crowded, particularly when you're dealing with multiple scripts and plugins and Wordpress itself. The more globals you create, the greater the odds of conflicting with one from another script. By having your functions nicely contained inside your scoping function, you avoid the possibility of stomping on someone else's function/element/whatever and vice-versa.
You can add function from your enclosure to window like that:
(function($){
function loadFullPost(...) {
...
}
window.loadFullPost = loadFullPost;
}).(window.jQuery);
Then your function will be visible for onlick attribute etc.

Conditionally attaching jQuery plugin functions to elements on the site

I want to attach a jQuery plugin function to an element on my site that exists only on one page. Currently, I'm using this conditional to prevent triggering the limiter function and throwing an error when there is no #advertiser_co_desc in view.
jQuery(document).ready(function($) {
var elem = $('#charNum');
if ($('#advertiser_co_desc').length) {
$('#advertiser_co_desc').limiter(180, elem);
}
});
On my website #advertiser_co_desc is present only on one page.
My solution does the job, but my qualm stems from the fact that the jQuery plugin code as well as the plugin function call presented above (they are both in the same file) get fetched by the browser and the condition is continuously evaluated regardless of whether a user ever gets to a page where #advertiser_co_desc exists.
Is the method I'm using optimal, or is there a better way to attach this particular JS only to the page where '#advertiser_co_desc` exists? Naturally, I wan to avoid adding my scripts in the same file with the PHP code.
Or you can wrap the plugin method as,
var _limiter = $.fn.limiter;
$.fn.limiter = function(limit, element) { // provide complete argmuments
if(this.length) {
_limiter.call(limit, element);
}
};
Make sure that plugin is loaded before this statements.
The best and optimal way to check existence of an element by jquery, is $('#advertiser_co_desc').length that you have used already. So no need to change your code.

extend a javascript option to add functionality

I need to call "MyOtherFunction" when "MyFunction"(which creates an element) completes, without MyFunction knowing what MyOtherFunction is.
The reason I need this is for extension of a jquery powered fileupload User Control that is used in several places with different functionality. A specific page shows a header and file count for it, and when the upload completes, I need to modify the file count according to how many files are displayed(by created elements) I thought :
$(UserControl).on(MyFunction, UploadElem, MyOtherFunction);
but this route is not accomplishing anything. The most I can alter the User Control is add in a function call, but without effecting the original user control functionality.
I'm not sure if because MyFunction isn't an event and doesn't bubble up or if it just isn't possible to use a defined function as a parameter of .on() is the reason I cannot get this code to work. Any suggestions?
Easiest way I can think of, is duck punching respectively hooking that method:
var _oldMyFunction = MyFunction;
MyFunction = function() {
_oldMyFunction.apply( this, arguments );
MyOtherFunction();
};
I managed to solve my own issue, but the context is important for the answer:
// Using a Global JavaScript object I created:
GlobalNameSpace.ExtensionFunction = function(oParam1, oParam2, oParam3)
{
/// <summary>All parameters are optional</summary>
return; // For instances when it is not being overwritten, simply return
}
//In the Code for the user control:
GlobalNameSpace.UploadControl.UploadComplete(oSender, oArgs)
{
///<summary>Handles the Upload process</summary>
// process the upload
GlobalNameSpace.ExtensionFunction(oSender, oArgs);
}
//and finally in the code to extend the functionality
GlobalNameSpace.Page.Init
{
///<summary>Initializes the page</summary>
// redefine the extension function
GlobalNameSpace.ExtensionFunction = function(oSender, oArgs)
{
GlobalNameSpace.Page.Function(oSender, oArgs);
}
}
This allows me to extend anything I need it to without polluting my objects, and having something generic already existing to call on to make my changes. This solution solves my problem of needing a onCreate function for the elements I create to represent my uploaded items to trigger the header displaying the number of files. Very useful

Is there a way to trigger an event when inserting html?

It's for a backbone application,
I'm using Jquery html() function to insert my views templates into the layout everywhere, and I would like to be able to trigger an event each time the html() function of jQuery is called to check the html of the page.
Is there a way to do that ?
( Like App.on('html', blablabla...); )
Thank you !
As Marc B suggested DOM MutationEvents is available on some browsers (not many). By default jQuery does not fire any event when using html, but you can define your own behaviour for this, for example:
(function($) {
var html_ref = $.fn.html;
$.fn.extend({
html : function() {
$(document).trigger( 'html_change' );
return html_ref.apply(this, arguments);
}
});
})($);
It should work, didn't test it though. You can use the same with .text method. Now you can simply use:
$(document).bind( 'html_change', function() {
// Hurray! Html changed!
});
That's the idea, use it as you wish.
AFAIK, the jQuery html() method doesn't fire any subscribable events per se, but you could probably roll your own implementation of a simple Observer pattern. I use this across a large number of projects and it provides a great, clean, lightweight way to encapsulate arbitrary event handling across loosely-coupled modules.
However, this is presuming that you have programmatic control over every time the html() method is called - if not, then this would be more difficult, as there is no callback function to hook into.

Watch for a property creation event?

I need to be able to determine when an object is created (not a DOM element -- a JavaScript object).
An answer to this question has some very useful looking code for creating observable properties, so you can have a function fire when a property changes.
In my situation I need to do something when the object/property is created, not an existing property changed, and my limited understanding of such matters did not help me figure out if or how I could use that code to do this after much squinting.
The situation is: page loads a bunch of scripts. Some of the scripts create things that are needed by other scripts, e.g:
ThisStuff = (function () {
// blah blah
return self;
} ());
Some other code needs to initialize this ThisStuff, whenever it's available, which may be after the DOM is done loading. The user doesn't actually need ThisStuff right away, so it's fine for it to happen whenever the script is done loading. So I would like to do something along lines of:
$(document).ready(function() {
wheneverIsAvailable(window,'ThisStuff', function(object) {
object.init(args);
})
});
I realize there are other solutions to this problem (changing script order, or loading scripts on demand) but those are difficult because of the architecture. So I'm only interested in a way to do this versus other solutions. If jQuery offers some such functionality, that's fine also as I'm using it.
You could have a setInterval checking a number of times a second to watch the specific variable. You can check whether it is created using obj.hasOwnProperty(prop). When it is created, you invoke the function, and clear the interval.
It might be dirty but it might also just work fine for you.
Edit: I coded this for you: http://jsfiddle.net/jhXJ2/2/. It also supports passing additional arguments to the function.
window.__intervals = [];
function wheneverIsAvailable(obj, prop, func) {
var id = (Math.random()+"").substring(2);
var args = arguments;
window.__intervals[id] = window.setInterval(function() {
if(obj.hasOwnProperty(prop)) {
window.clearInterval(window.__intervals[id]);
func(Array.prototype.slice.call(args, 3));
// Call function with additional parameters passed
// after func (from index 3 and on)
}
}, 1000/ 50);
}
wheneverIsAvailable(window, 'test', function() {
alert(arguments[0]);
}, 'Woot!');
window.setTimeout('window.test = 123', 1000);
This is a bit far-fetched but it might work.
You would need to use knockoutjs, a javascript library. It's awesome but is built for a slightly different purpose.
Anyways it has a dependentObservable thing which allows to fire up an event whenever a certain value changes. Now I know you want on creation but you can check whether your variable holds any value (other than what you provided initially), if yes then consider it initialize.
Let me know if you think this sounds feasible.

Categories