Archive for the ‘Code Snippets’ Category
Posted in 'Code Snippets, JavaScript' by James on January 12th, 2010
Chris Heilmann recently posted on how to use YQL to make cross-domain requests, which would usually be prohibited due to the same-domain-policy. I already knew about YQL, but I had no idea that it allowed retrieval of HTML from other sites, via JSON, returned as a single string!
Instead of asking for JSON format, ask for XML, but also add a callback parameter to your query. Voila!
So, in short, YQL allows us to make cross-domain GET requests!
Chris also posted a demo!
With a bit of hacking, we can make jQuery work with YQL for all cross-domain GET requests. UPDATE: I’ve decided to put this in my “jQuery Plugins” repo at Github:
Cross-Domain Ajax mod @ Github
With this mod, any GET request made via jQuery.ajax to another domain will work!
$('#container').load('http://google.com'); // SERIOUSLY!
$.ajax({
url: 'http://news.bbc.co.uk',
type: 'GET',
success: function(res) {
var headline = $(res.responseText).find('a.tsh').text();
alert(headline);
}
});
// Works with $.get too!
Have fun!
Posted in 'Code Snippets, JavaScript' by James on December 16th, 2009
A while ago I wrote a little “pulse” plugin for jQuery which would make an element pulse between two or more states. I won’t bother linking to it because it’s offline. I decided to have another go at developing it, this time with a more refined and intuitive API.
I’ve tried to emulate the characteristics of jQuery’s animate() method as much as possible. There are only a few slight differences. Here’s a typical call to the new “pulse” plugin:
jQuery(element).pulse({
opacity: [0, 1],
backgroundColor: ['red', 'yellow']
}, 1000, 5, 'linear', function(){
alert("I'm done pulsing!");
});
It’s almost exactly the same parameter pattern as animate(), except that you have to specify an array of values for each CSS property, and also an additional numerical parameter, after the duration, which signifies how many times you want the pulse to run (the above code will run the pulse five times).
It also works with an “options” object, just like animate():
jQuery(element).pulse({
opacity: [0,1]
}, {
duration: 100, // duration of EACH individual animation
times: 3, // Will go three times through the pulse array [0,1]
easing: 'linear', // easing function for each individual animation
complete: function() {
alert("I'm done pulsing!");
}
});
You should note that the duration option only defines how long each individual animation will run, not the entire pulse. To dictate the length of the entire pulse you need to use the times option. Also note that the times option refers to an entire run-through of the largest array found in the properties option.
You can “pulse” through as many values as you want:
jQuery(element).pulse({
backgroundColor: ['red', 'yellow', 'green', 'blue'],
opacity: [0, 1],
});
The arrays don’t all have to be of equal length – with the above code, the opacity would keep changing as is defined by its array.
The plugin itself is quite small. I’ve decided to start a new repo at Github, just for small and useful jQuery plugins like this.
Pulse plugin on Github
I haven’t tested it extensively yet, so, as usual please notify me of any bugs/issues. Thanks!
Posted in 'Code Snippets, JavaScript' by James on November 13th, 2009
In Thomas Fuchs’ latest JavaScript performance presentation he talks about the speed gains that can be experienced by using “unrolled” loops.
A conventional loop:
for (var i = 0; i < 10; ++i) {
doFoo(i);
}
The “unrolled” version of that loop:
var i = 0;
doFoo(i++); doFoo(i++); doFoo(i++); doFoo(i++); doFoo(i++);
doFoo(i++); doFoo(i++); doFoo(i++); doFoo(i++); doFoo(i++);
A partially unrolled version:
for (var i = 0; i < 10; ) {
doFoo(i++);
doFoo(i++);
doFoo(i++);
doFoo(i++);
doFoo(i++);
}
Interestingly, speed gains can be experienced dependent on the loop size, albeit marginal at best. I thought about ways to build this into a clever forEach function and came up with something that ‘pre-compiles’ functions containing partially unrolled loops. Have a look:
var forEach = (function() {
var fns = [],
callers = "true",
numberFn = 10,
i = 1;
for ( ; i <= numberFn; ++i ) {
callers += "&&f(a[++i])!==false";
fns[i] = new Function("a", "f", "l", "var i=0;while (i<l) {"+callers+"}");
}
return function( array, fn ) {
var len = array.length,
n = numberFn, i;
while (i = n--) {
if ( len % i === 0 ) {
return fns[i](array, fn, len);
}
}
};
})();
This function will run one of 10 ‘pre-compiled’ functions on the passed array, dependent on the highest factor of the array’s length. I’m only creating 10 different functions in this example, you could create more.
If you were to pass an array with a length of 14, then fns[7] would be used, since 7 is the highest available factor (the highest number below 10 that 14 can be divided by, to gain a whole number). fns[7] looks something like this:
function anonymous(a, f, l) {
var i = 0;
while (i < l) {
true &&
f(a[++i]) !== false &&
f(a[++i]) !== false &&
f(a[++i]) !== false &&
f(a[++i]) !== false &&
f(a[++i]) !== false &&
f(a[++i]) !== false &&
f(a[++i]) !== false;
}
}
The !== false part is used to create the effect of loop-breaking. Notice that the success of this boolean expression is depended upon to continue the chain of expressions (a && b && c)
I’ve only tested it briefly, and to be honest, there doesn’t seem to be a notable benefit. In IE, I can see a bit of improvement over the conventional forEach implementation but only if I’m using arrays with 1000+ lengths. I think this would only be useful in situations where you absolutely have to squeeze every inch of potential performance out of your app. Anyway, it’s still pretty interesting, I wonder what other fancy things can be created by using pre-compiled functions.
Posted in 'Code Snippets, JavaScript' by James on September 11th, 2009
A while ago I posted a method I had been using at the time to remove comments from JavaScript code. It was pretty decent – instead of using a regular expression it steps through each character and removes comments where it finds them.
At the time I thought stepping through a string character-by-character was the only reliable way to solve the “comments problem” but after giving it another attempt I found that it was possible with a only a few regular expressions and a fairly moderate dose of JavaScript’s replace() function.
Here it is:
function removeComments(str) {
var uid = '_' + +new Date(),
primatives = [],
primIndex = 0;
return (
str
/* Remove strings */
.replace(/(['"])(\\\1|.)+?\1/g, function(match){
primatives[primIndex] = match;
return (uid + '') + primIndex++;
})
/* Remove Regexes */
.replace(/([^\/])(\/(?!\*|\/)(\\\/|.)+?\/[gim]{0,3})/g, function(match, $1, $2){
primatives[primIndex] = $2;
return $1 + (uid + '') + primIndex++;
})
/*
- Remove single-line comments that contain would-be multi-line delimiters
E.g. // Comment /* < --
- Remove multi-line comments that contain would be single-line delimiters
E.g. /* // <--
*/
.replace(/\/\/.*?\/?\*.+?(?=\n|\r|$)|\/\*[\s\S]*?\/\/[\s\S]*?\*\//g, '')
/*
Remove single and multi-line comments,
no consideration of inner-contents
*/
.replace(/\/\/.+?(?=\n|\r|$)|\/\*[\s\S]+?\*\//g, '')
/*
Remove multi-line comments that have a replaced ending (string/regex)
Greedy, so no inner strings/regexes will stop it.
*/
.replace(RegExp('\\/\\*[\\s\\S]+' + uid + '\\d+', 'g'), '')
/* Bring back strings & regexes */
.replace(RegExp(uid + '(\\d+)', 'g'), function(match, n){
return primatives[n];
})
);
}
Theoretically this should work perfectly in almost all situations. Don’t bother even trying it with E4X as that definitely won’t work! E.g.
var someE4X = <box>// this is NOT a comment</box>;
It's impossible to cater to E4X with regular expressions because XML is a recursive structure. I'm not bothered though as E4X isn't exactly a widely used extension. It also doesn't play well with conditional compilation but frankly, conditional compilation shouldn't exist anyway.
Anyway, back to the solution. It takes a pretty conventional approach of removing all strings and regular expressions first and then moving on to the comments. Unfortunately comments are not as simple as \/\*.+?\*\/ - there are nested comments within strings, nested comments within literal-regular-expressions and nested comments within other comments.
Posted in 'Code Snippets, JavaScript' by James on August 31st, 2009
Here’s a nifty little trick that allows you to write “contextual” JavaScript. It’s incredibly obtrusive and probably shouldn’t be used at all but it’s still a pretty cool idea:
<div id="some-div">
<script type=":contextual">
alert(this.id); // "some-div" is alerted
</script>
</div>
Instead of the this keyword referencing the global object (window) we can make it reference the parentNode of the script element. Here’s the code that makes it work:
(function(){
var scripts = document.getElementsByTagName('script'),
script, i = 0;
while ( (script = scripts[i++]) ) {
if ( /:contextual$/.test(script.type) ) {
(new Function(script.innerHTML)).call(script.parentNode);
}
}
})();
Take it or leave it! – I’m indifferent either way – yes, it’s obtrusive but you’ve got to admit, contextual JavaScript looks pretty neat!
Posted in 'Code Snippets, JavaScript' by James on August 20th, 2009
Satisfy is a development-only jQuery plugin* that you can use to quickly generate HTML for testing/debugging. The idea is that you provide a CSS selector and then the plugin will “satisfy” it by generating an HTML structure in accordance with that selector.
* – UPDATE: This is no longer just a jQuery plugin – as of version 0.2 Satisfy has no dependencies. See the comments for more info.
For example:
jQuery('div a').satisfy();
… would return the following HTML structure:
A more snazzy example:
jQuery('ul li:5 span[innerHTML="link"]').satisfy();
… which would return the following:
<ul>
<li><span>link</span></li>
<li><span>link</span></li>
<li><span>link</span></li>
<li><span>link</span></li>
<li><span>link</span></li>
</ul>
(Note: It ignores combinators (+|~|>) and pseudo-classes (:pseudo). It adds support for numerical pseudo-classes (e.g. “:5″) which you can use to specify how many of a particular element you want.)
More information and source available at Github – “satisfy” @ GitHub
Posted in 'Code Snippets, JavaScript' by James on August 7th, 2009
This is not a suggestion for the jQuery core; it’s just something I required recently that some of you may find useful/intriguing…
jQuery.fn.map = (function(_map) {
return function(toMap, prop, jQueryOb) {
if (typeof toMap === 'string') {
var parts = toMap.match(/(\\:|[^:])+/g),
method = parts.shift(),
args = parts,
mapped = _map.call(this, function(){
var $this = $(this),
result = $this[method].apply( $this, args );
return prop ? result[prop] : result;
});
return jQueryOb ? mapped : mapped.get();
} else {
return _map.apply(this, arguments);
}
};
})(jQuery.fn.map);
// Note: this only modifies $.fn.map; not $.map
It allows you to pass a string to $(collection).map(...) instead of a function; it’s useful for returning attributes, DOM properties, CSS styles or data tied to each element without having to spend time/space writing a function, e.g.
$('a').map('attr:href'); // => ['http://abc.com', 'http://123.com', ... (all HREFs)
$('p').map('css:fontSize'); // => ['16px', '12px', '19px' ... (all font-sizes)
$('.class').map('offset', 'left'); // => ['200px', '430px', ... (all left offsets)
$('div').map('data:someData'); // => ['data', ... (added via jQuery.fn.data)
The first argument is split at each colon (use \ to escape a colon), the first part of the split string is assumed to be the jQuery method name which you want to call (e.g. "css" => $().css()). All remaining parts are assumed to be arguments for that method. The second argument (to map()) is an optional property to access following retrieval; this is demonstrated with the “offset” example in the code above. The last argument is a boolean; passing true will make it return a jQuery object instead of a real array.
Posted in 'Code Snippets, JavaScript' by James on July 23rd, 2009
DOM flickering occurs when you hide elements using JavaScript and there’s a noticeable delay between the page being loaded and the element actually hiding. This usually happens because you’re handling the removal upon the onload event; this is generally a bad idea – this event will not fire until everything has loaded, including images!
It’s considered better practice to use one of the various domReady abstractions available. Unfortunately, this won’t always work either, especially with large DOM structures! One method I’ve been using recently to avoid the flicker is to add a class to the body element as soon as it exists, thereby making it possible to affect the styling of these elements before they even exist within the DOM! An example:
<!-- SIMPLIFIED -->
<html>
<head>
<style type="text/css">
body.js .nojs { display: none; }
</style>
</head>
<body>
<!-- BODY element exists! -->
<script>
document.body.className += ' js';
</script>
<div class="nojs">
... No flicker!
</div>
</body>
</html>
Or, more cleanly:
<!-- SIMPLIFIED -->
<html>
<head>
<style type="text/css">
html.js .nojs { display: none; }
</style>
<script>
// Add class to <html> element
document.documentElement.className += ' js';
</script>
</head>
<body>
<div class="nojs">
... No flicker!
</div>
</body>
</html>
How do you solve the “flicker” problem?
Posted in 'Code Snippets, JavaScript' by James on July 19th, 2009
I recently required a pretty-date function to format some twitter dates; I knew there were already quite a few of these floating around the net; but I wanted to give it a try myself:
Regular pretty-date:
Note: Remember, pretty-dates are not meant to be accurate; they’re meant to be “pretty” – once you go past months it does get pretty inaccurate; afterall, logically, when we’re talking about time difference, what is a month? 30 days? Four weeks? The amount of days in a year divided by the amount of months in a year? The same dilemma occurs with larger measures like years; – if I say “one year ago”, are you thinking 365 days? yes? – well then 5 years ago would have to be [365*5] days, right? Nope, not if you assume one of those years is a leap year!
var niceTime = (function() {
var ints = {
second: 1,
minute: 60,
hour: 3600,
day: 86400,
week: 604800,
month: 2592000,
year: 31536000
};
return function(time) {
time = +new Date(time);
var gap = ((+new Date()) - time) / 1000,
amount, measure;
for (var i in ints) {
if (gap > ints[i]) { measure = i; }
}
amount = gap / ints[measure];
amount = gap > ints.day ? (Math.round(amount * 100) / 100) : Math.round(amount);
amount += ' ' + measure + (amount > 1 ? 's' : '') + ' ago';
return amount;
};
})();
Usage:
niceTime( 1 ); // => "39.57 years ago"
niceTime( "Sun Mar 01 20:20:02 +0000 2009" ); // => "4.65 months ago"
niceTime( "July 19, 2009 12:06:00" ); // => "26 seconds ago"
I then decided to take it a step further and create a recursive version that keeps going until it hits the smallest measure of time available:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
| var prettyTimeDiff = (function() {
var ints = {
second: 1,
minute: 60,
hour: 3600,
day: 86400,
week: 604800,
month: 2592000,
year: 31536000,
decade: 315360000
};
return function(aTime, bTime) {
aTime = +new Date(aTime);
bTime = bTime === undefined ? +new Date() : +new Date(bTime);
var timeGap = Math.abs(bTime - aTime) / 1000,
amount, measure, remainder, smallest;
for (var i in ints) {
if (timeGap > ints[i] && ints[i] > (ints[measure] || 0)) {
measure = i;
}
if (!smallest || ints[i] < smallest) {
smallest = ints[i];
}
}
amount = Math.floor(timeGap / ints[measure]);
if (timeGap > 31536000) {
/* Handle leap years */
timeGap -= Math.floor(ints[measure] * amount / 31536000 / 4) * 86400;
}
amount += ' ' + measure + (amount > 1 ? 's' : '');
remainder = timeGap % ints[measure];
if (remainder >= smallest) {
amount += ', ' + arguments.callee( +new Date() - remainder*1000 );
}
return amount;
};
})(); |
I decided to call it prettyTimeDiff as it can actually take two dates and then will give you the difference between them in pretty-format. If you only pass one date then it’ll assume you want to compare it to today.
Usage:
prettyTimeDiff( 0 );
// => "3 decades, 9 years, 6 months, 2 weeks, 4 days, 51 minutes, 25 seconds"
prettyTimeDiff( "July 1, 2005", "July 2, 2006" );
// => "1 year, 24 hours"
prettyTimeDiff( 0, 5000 );
// => "5 seconds"
prettyTimeDiff( "Sun Mar 01 20:20:02 +0000 2009" ) + ' ago';
// => "4 months, 2 weeks, 4 days, 37 minutes, 49 seconds ago"
You can limit what units it uses to measure time; for example, if you don’t want decades then just delete ints['decade']. The function will use whatever it can; so, if you only want it to measure time in minutes, years and decades then simply comment-out/remove all the other units:
var ints = {
//second: 1,
minute: 60,
//hour: 3600,
//day: 86400,
//week: 604800,
//month: 2628000,
year: 31536000,
decade: 315360000
};
An example with the above adjustment:
prettyTimeDiff( "January 19, 1988" )
// => "2 decades, 1 year, 262873 minutes"
It’s quite quick so could easily be used within any number of applications; for example, if you ever needed to countdown to the year 3000!
Posted in 'Code Snippets, JavaScript' by James on July 14th, 2009
I recently released some useful additions to String.prototype over on Github (linky). One of the more useful methods in there is extract. I’m sure something similar to this already exists but I’ve yet to find it.
It’s useful when you need to extract a particular group of every match from a global regular expression. Normally you’d have to use the RegExp.exec method along with a while loop to extract that info, and, that is exactly what’s going on behind the scenes. Have a look:
The code:
String.prototype.extract = function( regex, n ) {
n = n === undefined ? 0 : n;
if ( !regex.global ) {
return this.match(regex)[n] || '';
}
var match,
extracted = [];
while ( (match = regex.exec(this)) ) {
extracted[extracted.length] = match[n] || '';
}
return extracted;
};
Example:
('hi @rob and @adam, oh and @bob').extract(/@(\w+)/g, 1);
// => ['rob', 'adam', 'bob']
(With the above example, you could achieve it with a simple match() if JavaScript supported look-behinds…)