Reputation: 3912
I am trying to create widget using jquery. So I create a js file, that is responsible for spitting html to any website (say xyz.com) . That js file is as follows:
// @credit to:
// http://alexmarandon.com/articles/web_widget_jquery/
// http://stackoverflow.com/a/2190927/1560470
var MYCARD = MYCARD || (
function() {
// Localize variables
var jQuery, _cid;
return {
init : function (id) {
_cid = id;
/******** Load jQuery if not present *********/
if (window.jQuery === undefined || window.jQuery.fn.jquery !== '1.11.2') {
var script_tag = document.createElement('script');
script_tag.setAttribute("type","text/javascript");
script_tag.setAttribute("src", "http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js");
if (script_tag.readyState) {
script_tag.onreadystatechange = function () { // For old versions of IE
if (this.readyState == 'complete' || this.readyState == 'loaded') {
this.scriptLoadHandler();
}
};
} else {
script_tag.onload = this.scriptLoadHandler;
}
// Try to find the head, otherwise default to the documentElement
(document.getElementsByTagName("head")[0] || document.documentElement).appendChild(script_tag);
} else {
// The jQuery version on the window is the one we want to use
jQuery = window.jQuery;
}
},
scriptLoadHandler: function () {
// Restore $ and window.jQuery to their previous values and store the
// new jQuery in our local jQuery variable
jQuery = window.jQuery.noConflict(true);
},
render: function() {
jQuery(document).ready(function($) {
/******* Load CSS *******/
var css_link = $("<link>", {
rel: "stylesheet",
type: "text/css",
href: "mystyle.css"
});
css_link.appendTo('head');
/******* Load HTML *******/
var jsonp_url = "https://twitter.com/"+_cid+"?modal=true&callback=?";
$.getJSON(jsonp_url, function(data) {
$("#"+_cid+"-widget-container-for-card").html("This data comes from twitter: " + data.html);
});
});
}
};
}()
);
And in xyz.com's page, user can insert my widget as follows: (My goal is to pass an id from each widget, so that widget can render accordingly. Below you can see MYCARD.init("cnn");
where cnn
is the id)
<html>
<head></head>
<body>
<h2>CNN on Twitter</h2>
<script src="http://widgetwebsite.com/embed.js" type="text/javascript"></script>
<script type="text/javascript">
MYCARD.init("cnn");
MYCARD.render();
</script>
<div id="cnn-widget-container-for-card"></div>
<h2>BBC on Twitter</h2>
<script src="http://widgetwebsite.com/embed.js" type="text/javascript"></script>
<script type="text/javascript">
MYCARD.init("bbc");
MYCARD.render();
</script>
<div id="bbc-widget-container-for-card"></div>
<h2>CNN on Twitter</h2>
<script src="http://widgetwebsite.com/embed.js" type="text/javascript"></script>
<script type="text/javascript">
MYCARD.init("cnn");
MYCARD.render();
</script>
<div id="cnn-widget-container-for-card"></div>
</body>
However when I run this, I get Uncaught TypeError: jQuery is not a function
, occurred at jQuery(document).ready(function($) {
while jQuery
should have been initialized at init
method. Any idea what is going wrong here?
Please note, user may include same widget multiple times on his page (by mistake), and if that happens, then this code should handle that corner case.
Upvotes: 1
Views: 1380
Reputation: 4093
The problem here is that you assume that the inline script is executed after your script has finished loading jQuery, but that is not the case. Here is the sequence of actions that is actually happening:
In your case, if this is all you have to pass to your script, you may consider using data-
attributes so you'd have only your script inclusion tag looking like this:
<script src="http://widgetwebsite.com/embed.js"
type="text/javascript"
data-my-card-id="cnn"></script>
Then in your code, when jQuery has loaded, your look for $("script[data-my-card-id]")
tags and fetch the values.
If for some reason you really want to use an inline script, it must wrap its content within a callback that will get called from your code:
<script type="text/javascript">
var MYCARD_ready_callback = function() {
MYCARD.init("cnn");
MYCARD.render();
};
</script>
Then, once jQuery has loaded, you call your callback:
scriptLoadHandler: function () {
// Restore $ and window.jQuery to their previous values and store the
// new jQuery in our local jQuery variable
jQuery = window.jQuery.noConflict(true);
// Call callback define in inline script
MYCARD_ready_callback();
}
To support multiple inclusions the approach I take is to add some guard in my widget main function that checks for a global variable:
var MYCARD = MYCARD || (
function() {
if (window.MYCARD_is_already_loaded) {
return;
}
window.MYCARD_is_already_loaded = true;
// rest of the code
// ...
}()
);
Then in your widget code you deal with multiple inclusions, either by fetching the values from data-
attributes or by executing callback handlers, in which case you need an array of callbacks rather than a single callback:
<script type="text/javascript">
var MYCARD_ready_callbacks = MYCARD_ready_callback || [];
var MYCARD_ready_callbacks.push(function() {
MYCARD.init("cnn");
MYCARD.render();
});
</script>
Always think of the implications of asynchronous loading :)
Upvotes: 1
Reputation: 171698
You can't call your render method until jQuery.js
is loaded
Instead of calling it right after init()
in global space, call it internally after you have the load complete and scriptLoadHandler()
has been called since that's where you are defining your local jQuery
variable
Upvotes: 0