Friday, December 21, 2007

Jash is neat

I was using Jash today and I gotta say it's pretty cool.

One of the cool features is the mouseover DOM node selector. Alt+X to enter that mode and Alt+X again to capture whatever node you're mousing over. Then you can access the node via the javascript shell with this.currentNode. Now, that's neat.

For those who had the same issue as me with IE and bookmarklets, create a new bookmark and copy and paste the script below as the URL:

javascript:(function(){document.body.appendChild(document.createElement('script')).src='http://www.billyreisinger.com/jash/source/latest/Jash.js';})();

Thursday, December 20, 2007

IE8 passes acid test

Yay.

Let's hope it's not one of those internal-only-hacked-up-version-for-the-sole-purpose-of-passing-the-acid-test.

Tuesday, December 18, 2007

Jscript 5.7 out for IE

Microsoft recently released a patch for IE 6 which supposedly fixes a number of issues related to garbage collection.

Time to look at those javascript "@cc" conditional comment snippets and make sure they aren't using JScript version to sniff for IE7.

Monday, December 17, 2007

Why I think Knols will flop

So apparently, Google announced a publishing platform called Knol, which is supposedly similar to Wikipedia, in that it is supposed to provide encyclopedia-like information, but giving focus to the authors of each knol.

But without the collaborative effort and the multiple cycles of feedback that make up the core of wikipedia, will knols be as neutral and elaborated as wikipedia? Sure you could make a pretty informative and non-biased knol on ladybugs or potatoes, but what about Java strengths and weaknesses and, for that matter, Bush or Huckabee?

In my opinion, centralizing opinions around authors gives that much more room for incidents like when the Gaia guy bashed AIR, or more recently, when Resig threw a fit over a MooTools presentation.

Add to it that Google plans to add advertising to knols and you have something that scarily resembles the sensationalist tabloid newspaper business model.

Sure, we could argue that the good authors will prevail over the others, but authorities in various fields already blog, and blogs are much more adept at expressing opinions and even a certain degree of entertainment to authors, whereas factual dissertations are much more restrictive and easy targets for scrutiny.

One field where personal knowledge pages are common is the "[insert topic] for dummies", which About.com et al caters to. Thing is, whenever I'm over on those sites, I'm looking for quick answers, not looking to deepen my e-relationships with experienced people in some area where I'm not a guru.

Perhaps knols will be more socially oriented than About.com, but to say that knols will compare to Wikipedia sounds like giving individual authors way too much credit.

Friday, November 2, 2007

position:relative and AJAX quirks in IE

Today I ran into a problem because one of the sites I'm working on was breaking on a AJAX call to refresh the contents of a certain rounded corner box that uses position:relative in its CSS (btw, changing that rule is dangerous since it's used by all of the boxes in the site - which comprise 90% of the structural code)

The problem is that when you want to "refresh" only a certain node in the DOM tree and the height of the content changes, IE has a nice little bug, where it doesn't re-render relatively positioned elements correctly (unless you refresh the page, which beats the point of ajaxing in the first place).

The ideal solution would be to change the CSS, but in this case, it's not feasible, and solutions that require changing the HTML, even if just slightly, as well are out of the question.

Fortunately, there's a hack that solves this problem: just set the element's innerHTML to itself, and this will force the DOM node to redraw itself, as if it was a hard page refresh.

element.innerHTML = element.innerHTML

I'll admit it's not very pretty code, but hey, it works

Tuesday, October 30, 2007

An (almost stupid) idea on an old CSS bug

So I was compiling a list of known CSS bugs during my downtime at work, and one that sorta caught my attention was the one about form elements inheriting margins from parent elements that have layout (which, incidentally, I have never run into myself).

Here's a sample code derived from P.I.E., which will cause a margin of 100px to show to the left of the input element in IE:

.login {background-color:#eee;margin-left:100px;padding:10px;width:30em;}
<form action="ignore">
 <div class="login">
  Username:<br>
  <input type="text"><br>
 </div>
</form>
Now, wouldn't it make more sense to just have done this to begin with?
.login {background-color:#eee;margin-left:100px;padding:10px;width:30em;}
.login label {display:block;}
<form action="ignore">
 <div class="login">
  <label>Username:</label>
  <input type="text">
 </div>
</form>

Tuesday, September 4, 2007

Opera never fails to impress me

Opera 9.5 alpha has been released.

The feature list is quite impressive, ranging from a speed overhaul to the already lightning-fast engine to content-sensitive history searches.

Safari is going to have to work really hard to beat this release.

Wednesday, July 4, 2007

hackless buttons

I wonder why people hack IE without trying to set the background-position first =/

<style>

button {background:url(btn_blue_right_hover.gif) no-repeat 100% center;border:0;overflow:visible;padding:0 20px 0 0;}
button span {background:url(btn_blue_left_hover.gif) no-repeat 0 center;float:left;line-height:50px;padding:0 0 0 20px;white-space:nowrap;}

</style>
<button><span>test lorem ipsum dolor</span></button>

And there we have it. Semantically correct rounded corner buttons using sliding doors, the <button> element, and no hacks.

Tuesday, June 12, 2007

Safari 3 for Windows is out

Safari 3 beta is out... for Windows too! Get it while it's hot

Monday, May 7, 2007

javascript:void(0) is meaningless

If you are absolutely going to use <a> to create non-linking functionality, chances are that you are using custom event handlers and adding an href="javascript:void(0)" attribute so that IE will play nicely with the CSS :hover pseudo-element.

The problem with that is that javascript:void(0) says absolutely nothing about what the custom anchor does. All it does is return undefined.

A better cheap trick to tell your user what the anchor does is to use a javascript label. Like so:

<a href="javascript:ShowBooks:">View</a>

For the javascript runtime, the label is an empty instruction, which returns undefined, just like void(0), but with the added benefit that it can be used to make the status bar more user-friendly.

Tuesday, April 10, 2007

Hidden submit buttons and IE

Internet Explorer has this annoying bug that breaks submit-on-enter-key if the submit button is not visible during page load. Setting it to display:none and using position:absolute;left:-5000px both cause this bug to appear.

Here's one solution (assumes all submit buttons have a class="submit"):

<!--[if IE]>
<style type="text/css">
.submit {
 height:0;
 width:0;
 overflow:hidden;
}
</style>
<![endif]-->

This doesn't work in Firefox, so make sure you sniff for IE when applying this technique.

Thursday, April 5, 2007

fixing the IE7 onchange bug on checkboxes

IE7 thinks "onchange" means "onblurafterchange", here's a way to get around it in checkboxes.

window.jscript/*@cc_on=@_jscript_version@*/
if (jscript==5.7) {//if IE7
 var inputs = document.getElementsByTagName("input"), i=-1, l=inputs.length;
 while (++i!==l) {
  var inputs_i=inputs[i];
  if ((inputs_i.type=="checkbox")&&inputs_i.onchange) {
   inputs_i._onchange=inputs_i.onchange;
   inputs_i.onchange=null;
   inputs_i.onpropertychange=function() {if (event.propertyName=='checked') this._onchange();};
  }
 }
}

Wednesday, April 4, 2007

Javascript-based apps and images

So I had this prototype lying around for a few months. With Firefox, it looks pretty choppy and I've no idea how to fix it. Opera and IE look ok though.

Unfortunately we can only reach a maximum of 60FPS with javascript, and that number goes down as other logic adds processing load. Considering how stand-alone compiled games usually render at approximately 75 FPS or more, I guess Javascript isn't suitable enough for fast paced games.

Saturday, March 31, 2007

The attack of the forEach loops?

Many library writers are (finally) tuning into library performance and speed optimization. DOM query libraries are getting pretty good, but I still find that most libraries have made the very grave mistake of making a forEach-like structure available.

Sure, Firefox has a fast native implementation, but for the great majority of browsers out there, implementing forEach in javascript is a huge performance hit. And not only that, today I found out that even the plain old for loop used by many of these implementations is slow as well.

Here's my benchmark code and results:

Code

var bm=function(action,load) {
 for (var i=0;i<load;i++) action+=action+action;
 var t0=now();
 eval(action);
 var tf=now();
 return tf-t0;
};
var div=document.getElementById("output");//this is my output div, just add one to the page.
var a="var a=[0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9],b=function() {return this;};"
var load=6;
var test = function() {
 var resultsets="";
 for (var i=0;i<15;i++) {
  var results=[];
  results.push(bm(a+"for (var i=0,l=a.length;i<l;i++) b();",load));//for
  results.push(bm(a+"var i=a.length;while (--i!==0) b();",load));//while backwards
  results.push(bm(a+"var i=-1,l=a.length-1;while (++i!==l) b();",load));//while forward
  resultsets+=results+"<br>";
 }
 div.innerHTML=resultsets;
};

Results

note: I'm just posting the results from my final algorithms, tested in Opera.

forwhile backwardswhile forward
1567878
787279
936394
787878
787878
786378
936378
947878
787878
797878
787878
946279
936394
787878
787878
132610781204

The last column shows the sum of all times. The forward while loop performed 9.2% better than the for loop, and the backwards while loop performed 18.7% better.

Breakdown

Things that I noticed while testing and trying different looping algorithms:

  • The number of declared variables matter. If possible, use only one.
  • Using array.length in the exit condition instead of assigning it to a variable first (i.e. var length = array.length) slows things down a lot.
  • The operator in the exit condition matters. The === and !== operators are faster than <, > and variations.
  • Math operators for the incrementation don't seem to affect the speed of the loop. --i takes the same time as ++i.

Final thoughts

One last phenomenon that I think is worth mentioning on this whole forEach talk: many developers have the tendency of "latching" onto familiar practices. What this means is that updating existing functionality gets done using a structured programming approach. So, it's not uncommon to see things like this:

//the old version
array.forEach(doSomething);
//becomes
array.forEach(function(i) {doSomething(i,someNewParam)});

Notice the double function call? In browsers without native support for Array.forEach, this also needs to internally call a Function.call() for every iteration. Definitely not cheap. Use it in the core of a nifty popular library for maximum damage amplification.

Bottom line: the forEach pattern is redundant and expensive. And now, with the talks of centralizing javascript distribution, (like Yahoo is doing with YUI) the code footprint argument is becoming obsolete too.

Note to self: my getElementsByClassName function needs to be updated now =P

Saturday, March 24, 2007

Javascript OOP

Today I was trying to write a new "Class" module to be able to easily create an OOP patterns in Javascript. Actually, I had some 10 or 20 different variations. Most lacked one feature or another, or implemented things awkwardly, or simply became too big.

My goal (quite an ambitious one) was to create something as feature-packed as Dean Edwards' Base and at the same, as small as Kevin Lindsay's pattern. And be easily documentable.

After a whole day of writing and rewriting variations, I ended up with the same script I had written a while ago (except cleaner looking).

I was about done with Class patterns, but on my way to the shower, I had a "brilliant" idea: Simply append a string to the name of inherited members. Like so:

var Class=function() {};
Class.Extend=function(Class) {
 var ThisProto=this.prototype, SubClass=Class.Constructor||ThisProto.Constructor||function() {};
 SubClass.Extend=this.Extend, SubClass.prototype=Class;
 for (var Member in ThisProto) SubClass.prototype["undefined"==typeof Class[Member]?Member:Member+"Base"]=ThisProto[Member];
 return SubClass;
};

Yes, pretty stupid I know, but hey it's 7 lines of code and it keeps the correct scope in base methods. Here's a demonstration:

var Animal=Class.Extend({
 Constructor:function() {
  var Who="World";//private
  this.Who=function(Value) {return Who;}//privileged
 },
 Greeting:"Hello",//public
 Say:function() {alert(this.Greeting+" "+this.Who());}//public
});
var Dog=Animal.Extend({
 Say:function() {
  this.SayBase();//calling the base method
  alert("Woof Woof");
 }
});
var dog=new Dog()
dog.Say()//Hello World
 //Woof Woof

Monday, March 19, 2007

Location.href doesn't seem to work? Use "return false;"

Sometimes, people write stuff like this:

<a href="javascript:void(0)" onclick="doSomething(); location.href='newPage';">Click me</a>

and wonder why IE is being stupid and not going to the page we're telling it to go. To get around the problem DON'T use a setTimeout. It's bad practice. Just add a "return false" to your onclick like so:

onclick="doSomething(); location.href='newPage'; return false;" 

Of course, it's even better to avoid writing javascript in a html file in the first place, but I'm simplifying for the sake of explaining the idea.

Saturday, March 17, 2007

Amusing accident

So today I was adding content to a javascript tutorial written by a co-worker. I wrote and tested quite a bit of code before I noticed that in one line, my co-worker had written a URL. It wasn't in a string, nor a regular expression, nor it was commented out. I assume he meant to comment it out, but it was there in its own line between two other functions, kinda like so:

function example1() {/*...*/}
http://www.domain.com/page/1/lorem/ipsum/
function example2() {/*...*/}

It's amusing how he accidentally wrote perfectly valid javascript code and probably isn't even aware how that line is actually a label (used for goto statements) followed by a comment.

Thursday, March 15, 2007

RegExp good practices

After not finding anything from a Google search on "RegExp good practices", I figured someone should write something about it. Here's what I think are important tips when writing code with regular expressions:

  1. Avoid them if possible - regexp bugs are hard to trace
  2. Be careful with .* and negatives ([^...]) - matching too much is the leading cause of regexp bugs
  3. Comment regular expressions - the longer it is, the more ellaborate you should be
  4. Never ever regex document.body.innerHTML!

1) The main reason why I recommend staying away from regular expressions is that they are difficult to test, especially when used to handle input.

2) More often than not, developers don't test all cases. What happens if there are french characters? Line breaks? Some may argue that there are testing frameworks out there to help, but let's face it, not everyone uses them, and even if you do, complex regular expressions can still sometimes get away with obscure unusual character-sequence matches that only a regexp guru would be able to pick up.

3) All code should, of course, be commented, but reality is far from perfect. The least you as a developer should do is comment regular expressions, simply because they are so cryptic by nature. I find that "translating" the regexp into plain english and then stating what it is supposed to accomplish is reasonable.

4) I hack a lot while experimenting, and I'll tell you this first hand: Regular expressions are for small strings only! If your project absolutely requires regex'ing through paragraphs and paragraphs of content, you're probably neglecting your server-side capabilities, and heading towards a glacially slow dead-end.

Last thoughts

I'm mostly talking with javascript in mind here, but I think it's safe to say that these regexp rules apply in any environment. If you have any to add, feel free to post a comment.

Tuesday, March 13, 2007

Flex, CSS and code separation

Ok, so I've been looking into Flex these past few weeks. From a quick glance, it looked promising: XML markup to declare components, the ability to extend component via ActionScript 3, and a nice data binding scheme.

While I respect the efforts the Flex team made into creating a nice application development platform, I found that using it was far more restricting that I would like, particularly when it came to styling applications.

The CSS support that Flex provides is horrible, to put it nicely. It seems that the Flex development team never ever built a single CSS-based layout, or any post-1995 layout for that matter.

The official way of styling text (directly from the docs) involves having 2 nested nodes, with <FONT> tags in a CDATA section to define basic things like font size.

Flex does allow us to put some styles in a separate stylesheet file, but I wouldn't call it CSS per se. It provides mechanisms that are not part of W3C's specs, and it fails miserably at rendering styles from something as simple as an id selector. Class selectors with underscores even throw compiler errors!

Not only that, basic rules like left, top are not supported in CSS (they need to be inline in the MXML file). Even more basic things like text-align are not supported at all in some components! The closest attempt at fixing it (that I could find) is a huge 30 line monster. There must be some really dumb aspect that I am overlooking; it really shouldn't have to be that complicated.

One other thing worth mentioning is that the extra compiling cycle can really slow down production, especially when running into so many CSS support issues. A fairly simple application takes on average 5 seconds to compile. Considering also the learning curve associated with any OOP framework, 5 seconds of compile time is definitely not to be taken lightly.

Last thoughts

Flex is a very powerful tool and as such, very dangerous. Whereas in the XHTML/CSS circles, there has been extensive discussions on semantic markup, style separation, and unobtrusive javascript, the Flex community simply doesn't have mature resources to help new developers avoid mingling structure, styles and functionality into untraceable memory-hogging rat's nests.

Monday, March 12, 2007

getElementsByClassName speed optimization

From my experience in coding javascript, I came to realize that as cool as bleeding-edge javascript libraries and full CSS3 and XPath support are, they are fairly slow, particularly when it comes to DOM manipulation.

John Resig recently did a neat benchmark test on different techniques for implementing the getElementsByClassName method (in light of the recent news that Firefox 3 will have its own native implementation). While it gives a good overall idea of what algorithms should be avoided, it would certainly be nicer to just have a copy-and-paste solution that runs the fastest way possible in all major browsers.

Most libraries have pretty good implementations, but I have yet to see one that is truly optimized for speed. Here's my variation, written with the sole goal of being the fastest possible getElementsByClassName() script:

var getElementsByClassName=function(className,parentNode) {
 var results = [];
 //if (document.getElementsByClassName) document.getElementsByClassName(className,parentNode);//this is the code for FF3. Prototype 1.5 doesn't play nice with this code, though.
 if (document.evaluate) {
  var query = document.evaluate(".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]", parentNode || document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
  for (var i = 0, length = query.snapshotLength; i < length; i++) results.push(query.snapshotItem(i));
 }
 else {
  var nodes = (parentNode || document).getElementsByTagName("*");
  for (var i = 0, j = 0, length = nodes.length; i < length; i++) {
   var nodes_i=nodes[i];
   if ((" "+nodes_i+" ").indexOf(" "+className+" ") > -1) results[j++] = nodes_i;
  }
 }
 return results;
};

Benchmarking

For my test, I created a page with 200 sibling divs, out of which, half had class names. Each test queried the DOM 30 times, 1 second after page load, to minimize interference from network-related threads.

Benchmark results for library equivalents were fairly similar in Firefox (all of them scored somewhere near 190-200ms), but there were large performance differences in Internet Explorer 6. Prototype's implementation was 3 times slower than the script above.

Compared to CSS selector query engines like Prototype's $$() or JQuery, the script above performed between 10 to 20 times better, depending on the browser being tested. (In other words, $$(".my_class") is a big no-no).

The limitations of the script

  • It only takes spaces as class separators. No tabs or line feeds. This is the main reason why the script is faster in browsers that don't support document.evaluate
  • It assumes a browser, and doesn't check whether the "document" global exists. Rhino would always need to specify the root element anyways.

What makes it fast

  • No expensive regular expressions
  • document.evaluate delegates all the expensive DOM parsing to the underlying, compiled-code engine, where available.
  • Using variables instead of object members cuts down a lot of overhead. A lot of people don't believe me when I say this stuff is important until they actually see the numbers. Just today I got the javascript execution time of an e-commerce site down from 970ms to 180ms simply by fixing a few for loops.

Last thoughts

I personally don't see why anyone would want to separate class names with a tab. Line feeds are even worse since they break the indentation flow and make HTML virtually impossible to read. Sure it's part of the specs, but I'd much rather use a fast and sensible subset.