I am developing a small javascript framework for internal use in our company. In some cases our framework is overwriting basic jQuery functionality in order to handle internal logic or fix browser bugs etc. I want these cases to be completely transparent for our developers so they can just keep using jQuery the way they are used to.
I have come across a problem that I can’t seem to get my head around to do in a nice transparent way – at least I can’t come up with a good way to do it - so I turn to you :).
What I want to do is overwrite jQuery in a way so I can execute a certain piece of code each time something “becomes visible”. For instance if a developer runs the show() method, I want to execute my code after the element has become visible. In fact no matter how the developer “makes an element visible” (e.g. show(), css(), animate() etc.) I want my code to run
I know that show() for instance has a “callback” parameter that can be used for just that. But the point is; this should be totally transparent for the developer. He does not need to know that this code is “run underneath”.
All my overwrites is done using closures, so I can save a reference to the original method, do my thing and execute the original. Here is an example of how I am doing this with the show method:
(function ($) {
var originalShowMethod = jQuery.fn.show;
jQuery.fn.show = function (speed, easing, callback) {
// Do stuff before
var theReturn = jQuery(originalShowMethod.call(this, speed, easing, callback));
// Do stuff after
return theReturn;
};
})(jQuery);
My initial approach was to just simply do it this way (running my code at “// Do stuff after”). That also works great except when you are passing a speed parameter (because then it's using something like setTimeout or setInterval internally), or using one of the other “show an element” methods.
Is there some master “show an element” method in jQuery that I can overwrite so all methods that has something to do with “showing elements” will be affected by my piece of code? Or do I have to overwrite all "showing methods"? Can anyone give an example of how I can accomplice this task? and what i need to overwrite if that's the way to do it.
Thanks in advance.
I think you want something like the .is() and the :visible selector, it sounds like you might want to run a function like this using setInterval
function checkElementVis(){
if($(".element").is(":visible")){
alert("is visible");
} else {
alert("not visible");
}
}
Related
This is a general question about best practice in jQuery syntax/code organisation.
Consider the following snippet, used in a jQuery AJAX function:
if(obj.status == "error"){
$("#alert").html(obj.message);
}
I have also seen this written as:
function alert_box(str)
{
var html_str = '';
$("#alert").html(html_str);
}
if(obj.status == "error"){
alert_box(obj.message);
}
Functionally, this is precisely the same. My question: Is there any semantic or practical reason for differentiating between the two? What about load time / performance issues?
This is seems to be a question of "why use functions in general"? The idea behind a function is that you're making a code block reusable without having to write out the same code again. If you want to do that same thing in several places throughout your script, the function makes sense. If you only do that once, it may not be as practical. Then again, functions also help you hide details where you don't care about them - so you can summarize an action while the details of that action are somewhere in that function definition.
In this specific case, that function is broken anyway. Rather than using the passed in argument str, you have an empty variable html_str that you're replacing the html contents of an element with. Also, there's no need here to use html rather than text, which is better performance.
function alert_box(str) {
$("#alert").text(str);
}
Even though this is only a one liner, this can still be practical because it would let you use alert_box in several places throughout the script and not have to change those places later if you decide to change what alert_box does. Even something like changing the id of the element would require changes in several places, for example.
It also worth noting that this function searches the DOM for "#alert" each time it runs. It would be most optimal to cache that reference like this:
$alert = $("#alert");
function alert_box(str) {
$alert.text(str);
}
A few things that are great to study:
KISS
DRY
SOLID aka OOP
Suppose I had the following function:
function alertMesg()
{
alert("This ok function alerts message!");
}
Now at run time I would like to change the alertMesg function to do something else. My thought was to do somehting like this.
var temp = window.alertMesg.toString.replace("ok","great")
temp = temp.replace('function alertMesg()',"");
window.alertMesg = new Function(temp);
Basically, the problem is I have no control over the source in the alertMesg function. I would like to change the function, but I can't actually change the source of it because it is produced server side. That being said, I need it to act differently.
PS: I forgot to mention an important part: I have to keep most of the function. I can't just replace the function out right. I have to keep 95% of the function the way it is, and change the other five percent.
#Barlow Tucker, quixoto, pekka
Thanks, for the interest.
Basically, I don't think the proxy idea will work because I am not just adding functionality, I am changing the functionality of the code. I want for example, the third line of the function to be different. In my real life example I have to add a line right in the middle of a function.
If you must replace the content of a function, it is possible:
function alertMesg()
{
alert ("This ok function alerts my message!");
}
alertMesg(); // alerts "This ok function alerts my message!"
// do whatever you want to the function here
var temp = alertMesg.toString();
temp = temp.replace('my', 'your');
// now replace the original function
alertMesg=new Function(temp.substring(temp.indexOf('{')+1,temp.lastIndexOf('}')));
alertMesg(); // alerts "This ok function alerts your message!"
This probably isn't the best way to do what you're trying to achieve, but I can't really suggest anything else unless you provide more details.
Dynamic code replacement like you're suggesting might work in some cases, but it's a scary road to go down-- fragile, one wrong step and you're busted, and it quickly becomes a maintenance nightmare.
As a commenter said, your cleaner bet is to just wrap the native window.alert and then do the right thing when the string you care about comes through, per the answer here:
JavaScript: Overriding alert()
(Insert standard comment here about how you should get your server side people on the same page/team as you on this so you don't need to hack around your own page.)
UPDATE: You're not asking about alert, you're asking about this problem generally. Yes, you could what others are suggesting. But if you have the original code for the function, why not just replace it entirely? If the function you want is a global function called foo(), you can run JS that does:
window.foo = function() {
// The stuff I know is there (A)
...
// Some new stuff I want to change (B)
...
// More stuff I know is there (C)
}
Which will throw away the original and replace it with your version. This would work reasonably well, although "monkey patching" the stuff in the page definitely comes with some maintenance headaches.
I will definitely note here that if you can't do this for some reason and thus insist on doing textual code replacement in the middle of existing functions, you're abusing the language/environment for the purposes of maintainable anything, and you are truly screwed in multiple ways.
I would really like to provide the user some scripting capabilities, while not giving it access to the more powerful features, like altering the DOM. That is, all input/output is tunneled thru a given interface. Like a kind of restricted javacsript.
Example:
If the interface is checkanswer(func)
this are allowed:
checkanswer( function (x,y)={
return x+y;
}
but these are not allowed:
alert(1)
document.write("hello world")
eval("alert()")
EDIT: what I had in mind was a simple language that was implemented using javascript, something like http://stevehanov.ca/blog/index.php?id=92
(Edit This answer relates to your pre-edit question. Don't know of any script languages implemented using Javascript, although I expect there are some. For instance, at one point someone wrote BASIC for Javascript (used to have a link, but it rotted). The remainder of this answer is therefore pretty academic, but I've left it just for discussion, illustration, and even cautionary purposes. Also, I definitely agree with bobince's points — don't do this yourself, use the work of others, such as Caja.)
If you allow any scripting in user-generated content, be ready for the fact you'll be entering an arms race of people finding holes in your protection mechanisms and exploiting them, and you responding to those exploits. I think I'd probably shy away from it, but you know your community and your options for dealing with abuse. So if you're prepared for that:
Because of the way that Javascript does symbol resolution, it seems like it should be possible to evaluate a script in a context where window, document, ActiveXObject, XMLHttpRequest, and similar don't have their usual meanings:
// Define the scoper
var Scoper = (function() {
var rv = {};
rv.scope = function(codeString) {
var window,
document,
ActiveXObject,
XMLHttpRequest,
alert,
setTimeout,
setInterval,
clearTimeout,
clearInterval,
Function,
arguments;
// etc., etc., etc.
// Just declaring `arguments` doesn't work (which makes
// sense, actually), but overwriting it does
arguments = undefined;
// Execute the code; still probably pretty unsafe!
eval(codeString);
};
return rv;;
})();
// Usage:
Scoper.scope(codeString);
(Now that uses the evil eval, but I can't immediately think of a way to shadow the default objects cross-browser without using eval, and if you're receiving the code as text anyway...)
But it doesn't work, it's only a partial solution (more below). The logic there is that any attempt within the code in codeString to access window (for instance) will access the local variable window, not the global; and the same for the others. Unfortunately, because of the way symbols are resolved, any property of window can be accessed with or without the window. prefix (alert, for instance), so you have to list those too. This could be a long list, not least because as bobince points out, IE dumps any DOM element with a name or an ID onto window. So you'd probably have to put all of this in its own iframe so you can do an end-run around that problem and "only" have to deal with the standard stuff. Also note how I made the scope function a property of an object, and then you only call it through the property. That's so that this is set to the Scoper instance (otherwise, on a raw function call, this defaults to window!).
But, as bobince points out, there are just so many different ways to get at things. For instance, this code in codeString successfully breaks the jail above:
(new ('hello'.constructor.constructor)('alert("hello from global");'))()
Now, maybe you could update the jail to make that specific exploit not work (mucking about with the constructor properties on all — all — of the built-in objects), but I tend to doubt it. And if you could, someone (like Bob) would just come up with a new exploit, like this one:
(function(){return this;})().alert("hello again from global!");
Hence the "arms race."
The only really thorough way to do this would be to have a proper Javascript parser built into your site, parse their code and check for illegal accesses, and only then let the code run. It's a lot of work, but if your use-case justifies it...
T.J. Crowder makes an excellent point about the "arms race." It's going to be very tough to build a watertight sandbox.
it's possible to override certain functions, though, quite easily.
Simple functions:
JavaScript: Overriding alert()
And according to this question, even overriding things like document.write is as simple as
document.write = function(str) {}
if that works in the browsers you need to support (I assume it works in all of them), that may be the best solution.
Alternative options:
Sandboxing the script into an IFrame on a different subdomain. It would be possible to manipulate its own DOM and emit alert()s and such, but the surrounding site would remain untouched. You may have to do this anyway, no matter which method(s) you choose
Parsing the user's code using a white list of allowed functions. Awfully complex to do as well, because there are so many notations and variations to take care of.
There are several methods to monitor the DOM for changes, and I'm pretty sure it's possible to build a mechanism that reverts any changes immediately, quite similar to Windows's DLL management. But it's going to be awfully complex to build and very resource-intensive.
Not really. JavaScript is an extremely dynamic language with many hidden or browser-specific features that can be used to break out of any kind of jail you can devise.
Don't try to take this on yourself. Consider using an existing ‘mini-JS-like-language’ project such as Caja.
Sounds like you need to process the user entered data and replace invalid mark-up based on a white list or black-list of allowed content.
You can do it the same way as Facebook did. They're preprocessing all the javascript sources, adding a prefix to all the names other than their own wrapper APIs'.
I got another way: use google gears WorkerPool api
See this
http://code.google.com/apis/gears/api_workerpool.html
A created worker does not have access
to the DOM; objects like document and
window exist only on the main page.
This is a consequence of workers not
sharing any execution state. However,
workers do have access to all
JavaScript built-in functions. Most
Gears methods can also be used,
through a global variable that is
automatically defined:
google.gears.factory. (One exception
is the LocalServer file submitter,
which requires the DOM.) For other
functionality, created workers can ask
the main page to carry out requests.
What about this pattern in order to implement a sandbox?
function safe(code,args)
{
if (!args)
args=[];
return (function(){
for (i in window)
eval("var "+i+";");
return function(){return eval(code);}.apply(0,args);
})();
}
ff=function()
{
return 3.14;
}
console.log(safe("this;"));//Number
console.log(safe("window;"));//undefined
console.log(safe("console;"));//undefined
console.log(safe("Math;"));//MathConstructor
console.log(safe("JSON;"));//JSON
console.log(safe("Element;"));//undefined
console.log(safe("document;"));//undefined
console.log(safe("Math.cos(arguments[0]);",[3.14]));//-0.9999987317275395
console.log(safe("arguments[0]();",[ff]));//3.14
That returns:
Number
undefined
undefined
MathConstructor
JSON
undefined
undefined
-0.9999987317275395
3.14
Can you please provide an exploit suitable to attack this solution ? Just to understand and improve my knowledge, of course :)
THANKS!
This is now easily possible with sandboxed IFrames:
var codeFunction = function(x, y) {
alert("Malicious code!");
return x + y;
}
var iframe = document.createElement("iframe");
iframe.sandbox = "allow-scripts";
iframe.style.display = "none";
iframe.src = `data:text/html,
<script>
var customFunction = ${codeFunction.toString()};
window.onmessage = function(e) {
parent.postMessage(customFunction(e.data.x, e.data.y), '*'); // Get arguments from input object
}
</script>`;
document.body.appendChild(iframe);
iframe.onload = function() {
iframe.contentWindow.postMessage({ // Input object
x: 5,
y: 6
}, "*");
}
window.onmessage = function(e) {
console.log(e.data); // 11
document.body.removeChild(iframe);
}
(I'm using prototype.js here, but I imagine the same holds true across other libraries as well)
I often find myself writing code like this:
var search_box;
Event.observe(window, 'load', function() {
search_box = $('search_box');
});
function doSomething(msg) {
search_box.innerHTML = msg;
}
Rather then writing it simply like this:
function doSomething(msg) {
$('search_box').innerHTML = msg;
}
My intention is to avoid having to traverse the entire DOM searching for the "search_box" element everything I need access to it. So I search for it once on page load and then stick the reference in a global variable. However, I don't recall ever seeing anyone else do this? Am I needlessly making my code more complex?
This is called premature optimization.
You are introducing a global variable to optimize something you have not profiled.
Your assumption that the $ "traverses the DOM" is incorrect. This function is implemented using document.getElementById which is the fastest way to access an element in the DOM.
I suggest coding your javascript using basic programming best practices such as avoiding global variables, and not optimizing without profiling. Once your application is working as expected, then you should profile it (using firebug) and address the area(s) where it is slow.
I usually do the same thing, the reason you don't see it often is probably because you don't see well written code often that's optimized ( nevermind the whole preoptimization is evil thing ) - I say if you can optimize it without headaches then why not?
Realistically speaking though that's a very very trivial DOM lookup, you should only begin to worry if you're iterating through dozens of elements and being vague in the selectors.. so I wouldn't worry too much about it unless you can really notice certain parts of your web page loading rather slowly, in which case you should store the multiple elements you access in the outer scope's variable.
Good:
(function() {
var els = $$('.foo span'); // also better to specify a context but I'm not sure how that's done in Prototype, it's the second param in jQuery.
function foo() {
els.something();
}
els.somethingElse();
})();
Bad:
(function() {
var els = $$('.foo span'); // also better to specify a context but I'm not sure how that's done in Prototype, it's the second param in jQuery.
function foo() {
$$('.foo span').something();
}
$$('.foo span').somethingElse();
})();
I decided to spend a bit of time doing some testing to get some hard data. The answer is that preloading the elements into global variables is twice as efficient as accessing them using the DOM getElementById method. (At least under FF 3.6).
Subsequent accesses to the objects is also more efficient using the global variable method, but only marginally so.
I find myself doing a lot of this kind of JQuery:
$('.filter-topic-id').each(function () {
var me = $(this);
if (me.text() == topics[k]) {
me.parent().show();
}
});
I store $(this) in a variable called me because I'm afraid it will re-evaluate $(this) for no reason. Are the major JavaScript engines smart enough to know that it doesn't have to re-evaluate it? Maybe even JQuery is smart enough somehow?
They are not smart enough to know not to revaluate $(this) again, if that's what your code says. Caching a jQuery object in a variable is a best practice.
If your question refers to your way in the question compared to this way
$('.filter-topic-id').each(function () {
if ($(this).text() == topics[k]) { // jQuery object created passing in this
$(this).parent().show(); // another jQuery object created passing in this
}
});
your way is the best practice.
Are the major JavaScript engines smart enough to know that it doesn't have to re-evaluate it?
No. But if you are using jQuery you are presumably aiming for readability rather than necessarily maximum performance.
Write whichever version you find easiest to read and maintain, and don't worry about micro-optimisations like this until your page is too slow and you've exhausted other more significant sources of delay. There is not a lot of work involved in calling $(node).
You could try to profile your code with Firebug and see if using $(this) many times slows your app or not
There is no good way that a javascript can determine that the following is true:-
fn(x) == fn(x);
Even if this was possible not calling the second fn could only be valid if it could be guaraneed that fn has not have other side-effects. When there is other code between calls to fn then its even more difficult.
Hence Javascript engines have no choice but to actually call fn each time it is invoked.
The overhead of calling $() is quite small but not insignificant. I would certainly hold the result in a local variable as you are doing.
this is just a reference to the current DOM element in the iteration, so there's little or no overhead involved when calling $(this). It just creates a jQuery wrapper around the DOM element.
I think you'll find that calling the jQuery function by passing a dom element is perhaps the least-intensive of ways to construct the object. It doesn't have to do any look-ups or query the DOM, just wrap it and return it.
That said, it definitely doesn't hurt to use the method you're using there, and that's what I do all the time myself. It certainly helps for when you create nested closures:
$('div').click(function() {
var $this = $(this);
$this.find("p").each(function() {
// here, there's no reference to the div other than by using $this
alert(this.nodeName); // "p"
});
});