How can I use jQuery 1.5.2+ on a Firefox addon? - javascript

At first I made a function that received a parameter and returned jQuery such as:
function getjQuery(window)
{
/*jquery code*/(window);
return window.jQuery;
}
But then I got an email form the review and they told me I have to use jQuery file with the original file name and completely unmodified.
I started to search for an alternative and found this solution, but there is no way it work.
jQuery object is created, but I can't find any elements. $("#id").length is always 0. With the previous method it was always found.
My current code (which doesn't work)
AddonNameSpace.jQueryAux = jQuery;
AddonNameSpace.$ = function(selector,context) {
return // correct window
new AddonNameSpace.jQueryAux.fn.init(selector,context||contentWindow);
};
AddonNameSpace.$.fn =
AddonNameSpace.$.prototype = AddonNameSpace.jQueryAux.fn;
AddonNameSpace.jQuery = AddonNameSpace.$;
The jQuery file is loading on my browser.xul overlay:
<script type="text/javascript" src="chrome://addon/content/bin/jquery-1.5.2.min.js" />
Am I loading in the right place?
How can I use jQuery to modify the content on a page (HTML) with the original jQuery file, is it even possible?

You need pass the e.originalTarget.defaultView on the second parameter on jquery..
If you don't jquery will use window.document, which is the window.document from the xul.
Use
gBrowser.addEventListener("DOMContentLoaded", function (e) {
$("#id", e.originalTarget.defaultView).length
}, true);
instead of
$("#id").length;
And, for avoid conflicts with other extensions don't use script in the xul page, use MozIJSSubScriptLoader.
Components.classes["#mozilla.org/moz/jssubscript-loader;1"]
.getService(Components.interfaces.mozIJSSubScriptLoader)
.loadSubScript("chrome://youraddon/content/jquery-1.5.2.min.js");
If you use this method, you load jquery only when you need, avoiding memory leak.

The preferred way to load it is with mozIJSSubScriptLoader so you don't collide with other's extensions. I'm not sure why you're having problems, I can use jQuery in my addon like $("#id").hide() with no additional code (although from the sidebar, now browser.xul).
Either way, this blog post provides a pretty good guide and even has an example xpi to download.

Related

Access variable from 1 .js source file (jqTree), from within different .js file, to override method

(new to JS, jQuery, & jqTree)
I am trying to override a method (JqTreeWidget.prototype.openNode) from one .js file (tree.jquery.js) in another (my own custom.js).
I've read that to override a js method in general, I just need to redefine it. So I am trying to do that on the method, and I think I am stuck on accessing the variable that has the original method (JqTreeWidget). I think the challenge is that the original method is in tree.jquery.js (source) that is separate from my own other custom.js file where I want to do the override.
The goal of this Question would be to allow me to write something like this in my custom.js (<reference to JqTreeWidget.prototype.openNode> would be the Answer to this Question):
var originalMethod = <reference to JqTreeWidget.prototype.openNode>;
// Override of originalMethod
<reference to JqTreeWidget.prototype.openNode> = function( node, slide ){
// my code I want to happen 1st here
changeAncestorHeightRecursively( node, true);
// my code is done, and now I'm ready to call the original method
originalMethod.call( this, node, slide );
}
I think that would be the most non-intrusive way to do the override, without actually hacking in to the tree.jquery.js source.
See my custom.js at http://codepen.io/cellepo/pen/LGoaQx
The separate source tree.jquery.js is added externally in the JS settings of that codepen.
How can I get access (from within my custom.js file) to JqTreeWidget variable that is in the source file (tree.jquery.js)? Is it even possible? Is JqTreeWidget not in scope outside of tree.jquery.js, or is it not a global variable? I was hoping treeContainer.tree.prototype would have it, but I haven't had luck so far...
Thanks!
The prototype object can be obtained via:
jQuery.fn.tree("get_widget_class").prototype
Note that this is not a generalized solution for any jQuery plugin. This is something explicitly implemented by the tree plugin.
I found this hacky workaround. But since it's a hack, I'd still prefer to find the Answer as posed in this Question (so please, continue to Answer with respect to the <reference to JqTreeWidget.prototype.openNode> I mentioned in the Question, thanks)...
As stated in this Question, the goal involves making it possible to override JqTreeWidget.prototype.openNode (from tree.jquery.js) externally in custom.js. As such, calls to changeAncestorHeightRecursively (my code) & JqTreeWidget.prototype.openNode would both be made from the override in custom.js, and tree.jquery.js source is not modified at all.
Workaround:
Declare global var in html:
<script type='text/javascript' language="javascript">
changeAncestorHeightRecursively = 1;
</script>
In custom.js, set the globar var to the function (the one I want to be called before JqTreeWidget.prototype.openNode):
window.changeAncestorHeightRecursively = changeAncestorHeightRecursively;
Call the global-var-referenced function at the beginning of JqTreeWidget.prototype.openNode (hack into tree.jquery.js):
JqTreeWidget.prototype.openNode = function(node, slide) {
// Only way I could figure out to get this to execute before the rest of this method
// (global-var-referenced function in custom.js)
changeAncestorHeightRecursively( node, true );
// Rest of original openNode code...
}
This calls my code function from within tree.jquery.js, as opposed to calling the overridden method from within custom.js. So this is hacky because of the global var, and modifying tree.jquery.js source.
This will work for now, but hoping for a less hacky Solution as stated in this original Question... Thanks!

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.

Override .load function of jQuery

I have site which uses $(selector).load(path) function in more than 300 pages. Now my client's requirement has changed and I need to access cross domain to call these pages.
For the purpose I have to replace all the .load( function to some cross-domain function with the help of YQL.
Is it possible to override my .load function and call prevent default and do my own code?
There is no clean way to do this, especially since $.fn.load does different things depending on the arguments and replacing it would affect all those subfunctions.
However, jQuery supports AJAX hooks which you might be able to achieve what you want.
In case all you need is support for IE's XDomainRequest, have a look at this plugin: https://github.com/jaubourg/ajaxHooks/blob/master/src/ajax/xdr.js
Anyway, if you really want to replace the ajax load function of jQuery, this code should do it:
var _load = $.fn.load;
$.fn.load = function(url, params, callback) {
if(typeof url !== "string") {
return _load.apply(this, arguments);
}
// do your ajax stuff here
}
This is exactly the same check jQuery uses to decide whether someone wants to bind the onload event or perform an AJAX load.
The most reasonnable way seems to me to not overload the jquery function but simply do a search and replace in your favorite editor to replace $(xxx).load( by yourpackage.load(xxx,.
This can be done in minutes even on 300 js files. Future changes will be easier and the code will be more readable as the reader never expects a jquery function to do something that isn't on the doc.
Yes, it's possible:
$.fn.load = yourFunc;
Is it recommended? I think not.

Running javascript code called by AJAX

My site uses pushState to load pages. I have one issue, I want to use javascript on one of the pages but can't because it loads everything with AJAX. So what do I do? I've been told something about "parseScript" but I can't find enough information on it.
--Example--
I load using AJAX
On my page I have this script:
<script type="text/javascript">
function go(){
alert('1');
}
</script>
GO!!!
Nothing happens.
--Edit--
If I open up Google Chrome's debugger:
"Uncaught ReferenceError: go is not defined"
And the <script> tag is no where to be found
Browsers don't seem to parse <script> element content that's added to the document via targetElement.innerHTML. That's probably what you're running into.
The best solution is to use a well-tested framework like jQuery for solving problems like this. They've already figured out how to safely and correctly inject scripts into the DOM. There's no sense re-inventing the wheel unless you absolutely can't spare the bandwidth for the library.
One way you might fix this is by separating the JavaScript from the HTML in the Ajax response, either by issuing two requests (probably slower) or by structuring your JavaScript and HTML within a JSON object (probably harder to maintain).
Here's an example:
<script>
function load_content(){
var req = new XMLHttpRequest();
req.open("GET", "ajax.json", true);
req.onreadystatechange = function (e){
if (req.readyState === 4){
if (req.status === 200){
// these three lines inject your JavaScript and
// HTML content into the DOM
var json = JSON.parse(req.responseText);
document.getElementById("target").innerHTML = json.html;
eval(json.js);
} else {
console.log("Error", req.statusText);
}
}
};
req.send(null);
}
</script>
Load more stuff
<div id="target"></div>
The document ajax.json on the server looks like this:
{
"js": "window.bar = function (){ console.log(\"bar\"); return false; }",
"html": "<p>Log a message</p>"
}
If you choose this route, you must either:
namespace your functions: MyApp.foo = function (){ ... };, or
explicitly add your functions to the global namespace: window.foo = function (){ ... };.
This is because eval executes in the current scope, so your function definitions inherit that scope and won't be globally available. In my example, I chose the latter option since it's just a trivial example, but you should be aware of why this is necessary.
Please make sure to read When is JavaScript's eval() not evil? if you decide to implement this yourself.
I think it would be helpful to have a little more detail as to how the Ajax call is made and the content is loaded. That said, a few things of note:
the syntax for javascript:void() is invalid. It should be javascript:void(0). For that matter, using javascript:void() on the href of an anchor tag is generally bad practice. Some browsers do not support it. If you must use an tag, set the href to # and add "return false;" to the click event.
you should use a button tag instead of the a tag in this case anyway.
given what you have provided, it should work (aside from the syntax error with void())
If I were to do this I would use jquery's load call.
That takes care of putting an ajax call ,and parsing tags for script/no-script elements.
IF you dont wanna use jquery, I would suggest you go online and find what the jquery load method does and implement the same as an event handler for your ajax call.

Dynamically including javascript files only once

I have a javascript function I'm writing which is being used to include an external JS file, but only once. The reason I need such a function is because it is being called when some content is loaded via AJAX and I need to run page-specific code to that content (no, just using .live won't cover it).
Here's my attempt, shortened for brevity:
$.include_once = function(filename) {
if ($("script[src='" + filename + "']").length === 0) {
var $node = $("<script></script>")
.attr({
src : filename,
type : "text/javascript"
})
;
$(document.body).append($node);
}
};
This works fine: the function is called, it loads the external file, and that file is being run when loaded. Perfect.
The problem is that it will always re-load that external file: the query I'm using to check for the presence of the script always finds nothing!
When debugging this, I added some lines:
alert($("script").length); // alerts: 4
$(document.body).append($node);
alert($("script").length); // alerts: 4
Looking in the dynamic source (the HTML tab of Firebug), I can't find the script tag at all.
I know that I could maintain an array of files that I've previously included, but I was hoping to go with a method such as this, which (if it worked), seems a bit more robust, since not all the JS files are being included in this way.
Can anyone explain the behaviour seen in this second snippet?
jQuery is a bit of a dumb-dumb in this case; it doesn't do at all what you'd expect. When you append($node) jQuery does this:
jQuery.ajax({
url: $node.src,
async: false,
dataType: "script"
})
Woops! For local files (eg on the same domain) jQuery performs a standard XMLHttpRequest for the .js file body, and proceeds to "eval" it by a whole convoluted process of creating a <script> tag (again!) and settings it's contents to your .js file body. This is to simulate eval but in the global context.
For cross-domain files, since it cannot perform the standard XMLHttpRequest due to the same-domain policy, jQuery once again creates a <script> element and inserts it into <head>.
In both the local and cross-domain cases above jQuery finally gets around to doing this:
head.removeChild(script);
And booms your .length check! Bummer.
So on to your problem, don't bother jQuery with this. Just do
document.getElementsByTagName('head')[0]
.appendChild(
document.createElement('script')
)
.src = filename;
Which will do what you'd expect, particularly wrt querying for it later.
You're trying to solve a problem that has already been solved several times over. Try LazyLoad for example. There are also similar plugins for jQuery.
Instead of setting the source attribute of the script-tag, set the "text" attribute of the script tag. This works in all modern browsers (the application where I use that in practice does not support IE6, so I do not know about this creep...).
In practice it would look like this (you HAVE TO add code to omit double inclusion on yourself - e.g. a simple array of all alread loaded scripts, though thats very application specific, and why should you anyway load code twice? try to omit double-loading code...!):
var script_source_code_string = <some dynamically loaded script source code>;
var $n = $("<script></script>");
$n.get(0).text = script_source_code_string;
$(document.body).append($n);
Or even simpler (without jquery, my code at this stage does not know jquery, it may also be loaded dynamically):
var script_source_code_string = <some dynamically loaded script source code>;
var s = document.createElement('script');
document.getElementsByTagName('head')[0].appendChild(s);
s.text = script_source_code_string;

Categories