jQuery limit plugin concerns - javascript

This question is in reference to this jQuery limit plugin. Here's the source:
(function($){
$.fn.extend({
limit: function(limit,element) {
var interval, f;
var self = $(this);
$(this).focus(function(){
interval = window.setInterval(substring,100);
});
$(this).blur(function(){
clearInterval(interval);
substring();
});
substringFunction = "function substring(){ var val = $(self).val();var length = val.length;if(length > limit){$(self).val($(self).val().substring(0,limit));}";
if(typeof element != 'undefined')
substringFunction += "if($(element).html() != limit-length){$(element).html((limit-length<=0)?'0':limit-length);}"
substringFunction += "}";
eval(substringFunction);
substring();
}
});
})(jQuery);
Now I may just be nitpicking here... but maybe I'm missing something. Here are my questions/concerns:
1) What is the purpose of creating the substring function in a string and then eval'ing it? Looking through, it seems like the extension would work perfectly fine if the function was initialized normally.
2) I don't like that it uses a setInterval to execute the substring function. Wouldn't a keypress or similar event be the better and more logical way to do this? Also, I believe this to be the cause of (or at least enabling) the 'flickering text' bug that is referenced in the v1.2 change log (No, it isn't fixed).
3) Why is the variable f initialized? It is never used or referenced.
4) Also, this isn't a chainable method, and as a jQuery extension, it should be. I'm not too familiar with writing jQuery extensions, but this can be accomplished by return this; at the end of the method, correct?
It seems like this is just a case of poor programming, but I'd like to get an outside opinion.

1) I agree. Looks like he doesn't understand closures to me.
2) It's hard to predict exactly which events might change the contents of the textbox. Keypresses are obvious, but maybe mouse events could also do it. It could also be updated by other Javascript functions.
3) I have a couple of guesses: a) He was using it to hold the callback function when he was trying to get the closure to work, and didn't remove the declaration when he switched to the eval kludge; b) it was supposed to hold the substring function string, but he made a mistake and called it substringFunction when he assigned it (notice that he forgot the var declaration there).
4) True.
Just because someone posts their code to a web site doesn't mean they're an expert.

Related

Using eval in a function to replace a prefix on layer names?

I need to write a function where I can pass it a string and it will replace the word "prefix" in it with the contents of that string. So every time I call the function and pass it a new string i.e. alpha, beta, etc it will completely rebuild all the layers and events inside, using that key as the prefix. I suspect I need to use eval, but I'm not really sure how in this case.
layoutViews = (prefix) ->
prefix_layer1 = new Layer
width: 100
height: 100
prefix_layer1.on Events.Click ->
buttonActions()
layoutViews(alpha)
layoutViews(beta)
I'm using CoffeeScript, but any ideas in real JS are also welcome. I realize this specific question hints at my doing something else wrong in the project, but it's mostly for my own curiosity whether this is even possible.
So you want
function build(prefix) {
window[prefix + "_hi"] = function() {
alert("hi");
};
}
Use like this:
build("test");
test_hi();
Window is just the global (! Bad) element in browsers, so you might consider to assign to this instead of window depends on your usage.

alternative to eval() in DOM manipulation

I am converting tutorials for students (2nd language speakers, 9 to 12 yrs old) to access in an offline / intranet context. Hence the websites I would like them to use are unavailable.
I am trying to mimic the 'alter the code' style of tutorials for helping with JavaScript / HTML5 Canvas.
This works :
<canvas id="myCanvas" height="400px" width="400px"></canvas>
<script>
function update(){
eval(document.getElementById('demoScript').value);
}
var ctx = document.getElementById("myCanvas").getContext("2d");
</script>
<textarea id="demoScript">
ctx.fillRect(100,100,50,50);
</textarea>
<input type="button" value="update" onClick="update()">
... but everything I have read says eval() is a bad idea.
I can get the textarea content to pop-up in a div if I want, but I can't get it to pop-up in a script anywhere ... leaving me with just eval().
Options and recommendations for alternatives please ... or this is as good as it gets ?
This is an acceptable use for eval, because at worst a student will lock up their own browser with an infinite loop.
First of all, it's not a "bad idea" to use eval.
Second: anything that replaces eval will have the same "disadvantages" since it executes code. You'll have to execute code to do this. If you don't want to make your own interpreter (which is at least ten times worse and more vulnerable) you'll have to stick with eval or something similar.
Now what is the danger of it? Nothing else but the fact that it executes code. It's like telling someone that a hammer is dangerous because it hits hard - YES, and it's necessary when it gets to nailing something. Of course, a hammer can kill.
So,
Use eval,
...but sanitize the code it gets (= watch out for dangerous expressions, etc).
You can limit a lot of things for the user, like only one instruction per line, only double quotes, etc, to make it more controllable. Anything that's off the limit will be deleted. If no dangerous thing can be pushed thru the input, eval is harmless.
Options and recommendations for alternatives please ... or this is as good as it gets ?
I'd suggest using the Function constructor instead of eval. While in your simple example it may not make much difference, in other cases it may.
This will make the code evaluate in the global scope, so none of your local variables can be touched. It also allows JS engines to more easily optimize the local code. Using eval() can disable optimizations.
So to use the Function constructor, just pass the code to eval as the last argument. Since you have no parameters to define for the new function, it'll be the only argument.
var f = new Function("return " + document.getElementById("demoScript").value);
Then invoke the function.
f();
Notice that I concatenated a return statement into the provided code. This isn't required if you don't care about getting the returned value from the code your invoking, and should be removed if it might interfere with the provided code.
And of course, you can do this all in one line if you're only going to invoke it once.
new Function(document.getElementById("demoScript").value)();
You can get the value string from the textarea, split, validate and run it manually.
For a demonstration like this, where ctx is given, something like this should work
var ctx = document.getElementById("myCanvas").getContext("2d");
function update(){
var val = document.getElementById('demoScript').value,
fn = val.match(/\.(.*?)\(/),
arg = val.match(/\((.*?)\)/);
if (fn && fn.length > 0) {
if (arg && arg.length > 0) {
var args = arg[1].indexOf(',') != -1 ? arg[1].split(',') : [arg[1]];
ctx[fn[1]].apply(ctx, args);
}else{
ctx[fn[1]]();
}
}
}
FIDDLE
You could do
var fn = document.getElementById("demoScript").value;
window[fn]();

Is it possible to make this script more efficient?

I have just finished my script, using flot and jquery. Now to my question, it is fast in opera and Firefox, but it is painfully slow in internet explorer (no surprise), so thats why I wonder if there is a way to make my script more efficient (In other words perhaps remove some of the "for loops" etc)? So if there are any code gurus out there who have some spare time to kill, please help me out, because I myself am terrible at writing efficient code :P
Thanks so much in advance =)
It can be found
on this address
A few more tips:
It was pointed out that:
... $(this).attr('id');
... $(this).attr('name');
is expensive, you don't need $ here at all, just use:
... this.id;
... this.name;
Also, using .css(...) is a huge waste, use a class and put the CSS in an style element.
You can store references like $('#x') in closures. Again, you don't need $, it's far more efficient to get a reference directly to the element using document.getElementByid so that rather than:
$('#x').text(pos.x.toFixed(2));
you can have:
x.innerHTML = pos.x.toFixed(2);
which replaces several function calls with a single property access. The basic idea is to remove as much jQuery as you can, keep references to things rather than getting them frequently and use direct property access, not functions.
Incidentally, when I try to copy from the jsFiddle javascript region, Safari freezes. I'm not a big fan of that site.
for(k; k<datasets.length; k++){
Every time the loop is executed next, you are calling the length property, it's better if you store it in a variable at the start of the loop only, like this:
for(var k, len = datasets.length; k < len; k++){
Also here you are wasting resources:
key = $(this).attr("id");
subsystem = $(this).attr("name");
just stich $(this) into a variable, cause every time you use $() a clone of the passed element is created. Just do like this:
var $this = $(this);
And use $this from there on instead of $(this), only reuse $(this) when this become a different object.
Firstly, with jQuery selectors, if using a classname, it's more efficient if you can make that more specific. e.g instead of
var checkboxContainerFailed = $(".failedCheckboxes");
try
var checkboxContainerFailed = $("#graph-table-bottom td.failedCheckboxes");
Secondly, it's generally considered better to use [] notation instead of var subsystemNames = new Array();
Thirdly, you have a trailing comma in the data array here. This might cause IE problems:
"test2-a4/",
]
Finally, try running the whole thing through JSLint for any errors.

Javascript function pointers with Jquery causing domManip is not a function error

JQuery Version
jQuery JavaScript Library v1.4.4
Problem
The solution may be blatantly obvious, however I'm scratching my head.
The problem is while doing some code optimisation I came across a loop that called append on a jquery element a few times and it was a rather large loop. So it looked something like this:
var list_of_goodies = [1,2,3,...];
$.each(list_of_goodies, function(val) {
$('div.toaddto').append(val);
...some more code...
$('div.toaddto').append(otherval);
});
As you can see not really optimised so I tried to use function pointers so it looked something like this:
var list_of_goodies = [1,2,3,...];
var toaddtoAppend = $('div.toaddto').append;
$.each(list_of_goodies, function(val) {
... the other code...
toaddtoAppend(val).append(otherval);
});
It may not seem like a huge optimisation, but it's a large list and this can save a lot of lookup time especially in the older IE's.
This however causes an error.
The Error
this.domManip is not a function
Unfortunately this is from the minified jQuery, so there isn't much more info, it seems to happen within wrapInner() however.
Question
Is this a scope issue or a reference issue? I've tested it without using jQuery and the function pointer worked.
$.each or a for loop end up with the same results. Anyone know where I'm going wrong here?
I know very little about how javascript handles function pointers, especially when they're supposed to be associated to an instance of something as opposed to just static, so forgive any ignorance here.
Browsers Tested In
Firefox 3.6
IE 6, 7 & 8
Chrome 9.0.something
Regardless of the browser the results are always the same, which seems to suggest it's not how the browser is handling the pointer that's causing it to break.
Test it Yourself
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
<script type="text/javascript">
$(function(){
var iterables = [];
for (var i = 0; i < 1000; i++) iterables.push(i);
var divAppend = $('#test').append;
$.each(iterables, function(val) {
divAppend(val);
});
});
</script>
</head>
<body>
<div id="test"></div>
</body>
</html>
Thanks,
dennmat
When you write var divAppend = $('#test').append, you're storing a reference to jQuery's standard append function.
It's equivalent to writing var divAppend = $.fn.append—$('#test') isn't stored anywhere.
When you call divAppend as a normal function, its this is window, not a jQuery object.
You need to store a function that explicitly calls $('#test').append(...), or use divAppend.call($('#test'), val) to explicitly call append on the right object.
When you do this:
var toaddtoAppend = $('div.toaddto').append;
you do indeed get a reference to the "append" function, but you lose the relationship to the jQuery object. Thus when it's called later, there's no this reference for it to work on.
There's a method on Function instances available in newer browsers (and JavaScript engines) called "bind" that could do what you want here:
var toaddtoAppend = $('div.toaddto').append.bind($('div.toaddto'));
(I wouldn't write it that way of course, with the redundant call, but it's just an illustration.) That would give you a reference to a function that would call "append" with this being a reference to that jQuery object.
It is a scope issue, this will refer to window by the time you call the function.
You could wrap the call to append in another function and store that for later use.
However, the real thing that you'll want to prevent is is not the lookup of a function but querying the DOM tree multiple times.
You can store the jQuery object with all the items in a variable and refer to that.
var toaddto = $('div.toaddto');
$.each(list_of_goodies, function(val) {
toaddto.append(val);
...some more code...
toaddto.append(otherval);
});
If you insist on storing the reference to the function you could do that as well:
var toaddto = $('div.toaddto');
var myappend = toaddto.append;
$.each(list_of_goodies, function(val) {
myappend.call(tooaddto, val);
...some more code...
myappend.call(tooaddto, otherval);
});

jQuery pitfalls to avoid [closed]

As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 11 years ago.
I am starting a project with jQuery.
What pitfalls/errors/misconceptions/abuses/misuses did you have in your jQuery project?
Being unaware of the performance hit and overusing selectors instead of assigning them to local variables. For example:-
$('#button').click(function() {
$('#label').method();
$('#label').method2();
$('#label').css('background-color', 'red');
});
Rather than:-
$('#button').click(function() {
var $label = $('#label');
$label.method();
$label.method2();
$label.css('background-color', 'red');
});
Or even better with chaining:-
$('#button').click(function() {
$("#label").method().method2().css("background-color", "red");
});
I found this the enlightening moment when I realized how the call stacks work.
Edit: incorporated suggestions in comments.
Understand how to use context. Normally, a jQuery selector will search the whole doc:
// This will search whole doc for elements with class myClass
$('.myClass');
But you can speed things up by searching within a context:
var ct = $('#myContainer');
// This will search for elements with class myClass within the myContainer child elements
$('.myClass', ct);
Don't use bare class selectors, like this:
$('.button').click(function() { /* do something */ });
This will end up looking at every single element to see if it has a class of "button".
Instead, you can help it out, like:
$('span.button').click(function() { /* do something */ });
$('#userform .button').click(function() { /* do something */ });
I learned this last year from Rebecca Murphy's blog
Update - This answer was given over 2 years ago and is not correct for the current version of jQuery.
One of the comments includes a test to prove this.
There is also an updated version of the test that includes the version of jQuery at the time of this answer.
Try to split out anonymous functions so you can reuse them.
//Avoid
$('#div').click( function(){
//do something
});
//Do do
function divClickFn (){
//do something
}
$('#div').click( divClickFn );
Avoid abusing document ready.
Keep the document ready for initialize code only.
Always extract functions outside of the doc ready so they can be reused.
I have seen hundreds of lines of code inside the doc ready statement. Ugly, unreadable and impossible to maintain.
While using $.ajax function for Ajax requests to server, you should avoid using the complete event to process response data. It will fire whether the request was successful or not.
Rather than complete, use success.
See Ajax Events in the docs.
"Chaining" Animation-events with Callbacks.
Suppose you wanted to animate a paragraph vanishing upon clicking it. You also wanted to remove the element from the DOM afterwards. You may think you can simply chain the methods:
$("p").click(function(e) {
$(this).fadeOut("slow").remove();
});
In this example, .remove() will be called before .fadeOut() has completed, destroying your gradual-fading effect, and simply making the element vanish instantly. Instead, when you want to fire a command only upon finishing the previous, use the callback's:
$("p").click(function(e){
$(this).fadeOut("slow", function(){
$(this).remove();
});
});
The second parameter of .fadeOut() is an anonymous function that will run once the .fadeOut() animation has completed. This makes for a gradual fading, and a subsequent removal of the element.
If you bind() the same event multiple times it will fire multiple times . I usually always go unbind('click').bind('click') just to be safe
Don't abuse plug-ins.
Most of the times you'll only need the library and maybe the user interface. If you keep it simple your code will be maintainable in the long run. Not all plug-ins are supported and maintained, actually most are not. If you can mimic the functionality using core elements I strongly recommend it.
Plug-ins are easy to insert in your code, save you some time, but when you'll need an extra something, it is a bad idea to modify them, as you lose the possible updates. The time you save at the start you'll loose later on changing deprecated plug-ins.
Choose the plug-ins you use wisely.
Apart from library and user interface, I constantly use $.cookie , $.form, $.validate and thickbox. For the rest I mostly develop my own plug-ins.
Pitfall: Using loops instead of selectors.
If you find yourself reaching for the jQuery '.each' method to iterate over DOM elements, ask yourself if can use a selector to get the elements instead.
More information on jQuery selectors:
http://docs.jquery.com/Selectors
Pitfall: NOT using a tool like Firebug
Firebug was practically made for this kind of debugging. If you're going to be mucking about in the DOM with Javascript, you need a good tool like Firebug to give you visibility.
More information on Firebug:
http://getfirebug.com/
Other great ideas are in this episode of the Polymorphic Podcast:
(jQuery Secrets with Dave Ward)
http://polymorphicpodcast.com/shows/jquery/
Misunderstanding of using this identifier in the right context. For instance:
$( "#first_element").click( function( event)
{
$(this).method( ); //referring to first_element
$(".listOfElements").each( function()
{
$(this).someMethod( ); // here 'this' is not referring first_element anymore.
})
});
And here one of the samples how you can solve it:
$( "#first_element").click( function( event)
{
$(this).method( ); //referring to first_element
var $that = this;
$(".listOfElements").each( function()
{
$that.someMethod( ); // here 'that' is referring to first_element still.
})
});
Avoid searching through the entire DOM several times. This is something that really can delay your script.
Bad:
$(".aclass").this();
$(".aclass").that();
...
Good:
$(".aclass").this().that();
Bad:
$("#form .text").this();
$("#form .int").that();
$("#form .choice").method();
Good:
$("#form")
.find(".text").this().end()
.find(".int").that().end()
.find(".choice").method();
Always cache $(this) to a meaningful variable
especially in a .each()
Like this
$(selector).each(function () {
var eachOf_X_loop = $(this);
})
Similar to what Repo Man said, but not quite.
When developing ASP.NET winforms, I often do
$('<%= Label1.ClientID %>');
forgetting the # sign. The correct form is
$('#<%= Label1.ClientID %>');
Events
$("selector").html($("another-selector").html());
doesn't clone any of the events - you have to rebind them all.
As per JP's comment - clone() does rebind the events if you pass true.
Avoid multiple creation of the same jQuery objects
//Avoid
function someFunc(){
$(this).fadeIn();
$(this).fadeIn();
}
//Cache the obj
function someFunc(){
var $this = $(this).fadeIn();
$this.fadeIn();
}
I say this for JavaScript as well, but jQuery, JavaScript should NEVER replace CSS.
Also, make sure the site is usable for someone with JavaScript turned off (not as relevant today as back in the day, but always nice to have a fully usable site).
Making too many DOM manipulations. While the .html(), .append(), .prepend(), etc. methods are great, due to the way browsers render and re-render pages, using them too often will cause slowdowns. It's often better to create the html as a string, and to include it into the DOM once, rather than changing the DOM multiple times.
Instead of:
var $parent = $('#parent');
var iterations = 10;
for (var i = 0; i < iterations; i++){
var $div = $('<div class="foo-' + i + '" />');
$parent.append($div);
}
Try this:
var $parent = $('#parent');
var iterations = 10;
var html = '';
for (var i = 0; i < iterations; i++){
html += '<div class="foo-' + i + '"></div>';
}
$parent.append(html);
Or even this ($wrapper is a newly created element that hasn't been injected to the DOM yet. Appending nodes to this wrapper div does not cause slowdowns, and at the end we append $wrapper to $parent, using only one DOM manipulation):
var $parent = $('#parent');
var $wrapper = $('<div class="wrapper" />');
var iterations = 10;
for (var i = 0; i < iterations; i++){
var $div = $('<div class="foo-' + i + '" />');
$wrapper.append($div);
}
$parent.append($wrapper);
Using ClientID to get the "real" id of the control in ASP.NET projects.
jQuery('#<%=myLabel.ClientID%>');
Also, if you are using jQuery inside SharePoint you must call jQuery.noConflict().
Passing IDs instead of jQuery objects to functions:
myFunc = function(id) { // wrong!
var selector = $("#" + id);
selector.doStuff();
}
myFunc("someId");
Passing a wrapped set is far more flexible:
myFunc = function(elements) {
elements.doStuff();
}
myFunc($("#someId")); // or myFunc($(".someClass")); etc.
Excessive use of chaining.
See this:
this.buttonNext[n ? 'bind' : 'unbind'](this.options.buttonNextEvent, this.funcNext)[n ? 'removeClass' : 'addClass'](this.className('jcarousel-next-disabled')).attr('disabled', n ? false : true);
Explanation
Use strings accumulator-style
Using + operator a new string is created in memory and the concatenated value is assigned to it. Only after this the result is assigned to a variable.
To avoid the intermediate variable for concatenation result, you can directly assign the result using += operator.
Slow:
a += 'x' + 'y';
Faster:
a += 'x';
a += 'y';
Primitive operations can be faster than function calls
Consider using alternative primitive operation over function calls in performance critical loops and functions.
Slow:
var min = Math.min(a, b);
arr.push(val);
Faster:
var min = a < b ? a : b;
arr[arr.length] = val;
Read More at JavaScript Performance Best Practices
If you want users to see html entities in their browser, use 'html' instead of 'text' to inject a Unicode string, like:
$('p').html("Your Unicode string")
my two cents)
Usually, working with jquery means you don't have to worry about DOM elements actual all the time. You can write something like this - $('div.mine').addClass('someClass').bind('click', function(){alert('lalala')}) - and this code will execute without throwing any errors.
In some cases this is useful, in some cases - not at all, but it is a fact that jquery tends to be, well, empty-matches-friendly. Yet, replaceWith will throw an error if one tries to use it with an element which doesn't belong to the document. I find it rather counter-intuitive.
Another pitfall is, in my opinion, the order of nodes returned by prevAll() method - $('<div><span class="A"/><span class="B"/><span class="C"/><span class="D"/></div>').find('span:last-child').prevAll(). Not a big deal, actually, but we should keep in mind this fact.
If you plan to Ajax in lots of data, like say, 1500 rows of a table with 20 columns, then don't even think of using jQuery to insert that data into your HTML. Use plain JavaScript. jQuery will be too slow on slower machines.
Also, half the time jQuery will do things that will cause it to be slower, like trying to parse script tags in the incoming HTML, and deal with browser quirks. If you want fast insertion speed, stick with plain JavaScript.
Using jQuery in a small project that can be completed with just a couple of lines of ordinary JavaScript.
Not understanding event binding. JavaScript and jQuery work differently.
By popular demand, an example:
In jQuery:
$("#someLink").click(function(){//do something});
Without jQuery:
<a id="someLink" href="page.html" onClick="SomeClickFunction(this)">Link</a>
<script type="text/javascript">
SomeClickFunction(item){
//do something
}
</script>
Basically the hooks required for JavaScript are no longer necessary. I.e. use inline markup (onClick, etc) because you can simply use the ID's and classes that a developer would normally leverage for CSS purposes.

Categories

Resources