Friday, November 20, 2009

IE9 First Look

The folks over at the IEBlog just posted An early look at IE9 for developers.

Synopsis:

Javascript: almost as fast as ff 3.5, about half as fast as chrome. They brag about improved js speed, then turn around and slightly downplay the importance of it, pointing to the fact that there are many other factors that affect page render time.

CSS: They explain why they only got 32/100 on the acid3 test: Regarding the technologies that acid3 tests, "many [are] still in the “working draft” stage of standardization". (Editorial: And what? You don't deign to implement things that haven't fully settled down to your satisfaction? You're not getting any brownie points with consumers or developers with that attitude, IE team. Remember the good ol' days when you *invented* and implemented the standards before anyone else? Remember how it was that risk-taking, can-do, forge-ahead attitude that won you the browser war? You won't win this one by waiting for the spec to become 100% final before you dare to touch it.) Then the good news: we've got rounded corners now. CSS3 selectors seem to be coming along swimmingly, passing 574/578 tests on css3.info.

But perhaps the best news?

"We’re changing IE to use the DirectX family of Windows APIs to enable many advances for web developers. The starting point is moving all graphics and text rendering from the CPU to the graphics card using Direct2D and DirectWrite. Graphics hardware acceleration means that rich, graphically intensive sites can render faster while using less CPU."

Friday, September 25, 2009

Preload Images the Right Way

Sure, all of us cringe a little when we see that phrase. Even so, for things like overlaysof which modals and tooltips are memberspreloading the background images is a reasonable thing to do. (I still don't fully endorse it, more on that later.)1.

I tried this preload code:


(function(){
    var i, img, imageLoader = {
        imagesToLoad: [
            'resources/css/images/btn-default-sprite.png',
            'resources/css/images/bg-tooltip-left.png',
            'resources/css/images/bg-tooltip-right.png',
            'resources/css/images/bg-tooltip-image-left.png',
            'resources/css/images/bg-tooltip-image-right.png'
        ],
        loadedImages: []
    };
    for(i in imageLoader.imagesToLoad){
        img = new Image();
        img.src = imageLoader.imagesToLoad[i];
        imageLoader.loadedImages.push( img );
    }
})();

The images didn't seem to be loading any sooner though. So I put a tracer in the list - an image source reference that I knew didn't exist. Then I fired up the page and looked at fiddler. (I would've looked at firebug's net tab, but I was troubleshooting IE6 specifically.) No 404s. So then I tried this code:


(function(){
    var imagesToLoad = [
        'resources/css/images/btn-default-sprite.png',
        'resources/css/images/bg-tooltip-left.png',
        'resources/css/images/bg-tooltip-right.png',
        'resources/css/images/bg-tooltip-image-left.png',
        'resources/css/images/bg-tooltip-image-right.png'
    ];
    $('<div class="no-print" style="position:absolute;left:-9999px"/>')
        .appendTo('body')
        .html('<img src="'+imagesToLoad.join('"/><img src="')+'"/>');
})();

And I got my 404.

For the purposes of this discussion, preloading doesn't mean loading the images before the rest of the page, it means loading them before they're used.

Monday, August 24, 2009

Beyond Sprites - The New WAR

I was just looking at a set of image files which are used to decorate custom form elements. I was analyzing how best to turn them into sprites1. One particular set of 3 images caused me to dream up a 'new' web standard: a resource tar.

The set was a left end-cap, a right end-cap, and a repeatable middle section to accommodate a control of fixed height but variable width. The end caps were say, 20px wide, but the repeatable middle section image was - and only needed to be - 1px wide. Now, if I were to combine the 3 in a stacked sprite, the 1px wide middle section would have to be stretched to the 20px width to match the end-caps. True, widening the middle section would compress well in PNG or GIF format - as it was destined to become -- but these were just 3 of the 20 or so images. I could make the tiny sacrifices along the way and probably create 7 sprites out of the 20 images .. but that's still 7 http requests.

Maybe right now you're thinking what I was - there should be a way to send all 7 at once. And if there was a way to do *that* - then why muss about with all this sprite nonsense at all2? Just leave the images alone in their 20 separate files, but send them all in one jar. It would be the browser's responsibility to interpret a jar file that came from "http://site.com/path/to/jars/jarfile.jar" and had an internal directory structure of
/.
/img/
/img/1.gif
/img/2.gif
...
so that the images could be referenced in CSS as "http://site.com/path/to/jars/img/1.gif", "http://site.com/path/to/jars/img/2.gif"...

So what's with the title phrase "the new WAR"? Sending along the image assets for custom controls is just one application of this new "jar" or "war" file concept. Another is to send a packet that has the base images for a site: the logo and primary site decoration elements. Other people may conceive of other sets of resources that it make sense to combine and send along together in one packet.

1 For an excellent description of sprites, including some fantastic examples, see The Mystery Of CSS Sprites: Techniques, Tools And Tutorials.

2 Ok, the reason to still create the sprites is so that 'older' browsers - you know, the ones that don't have this as-yet unproposed feature - will still have 7 http requests instead of 20 (instead of the 1 they could have with this!)

Saturday, August 22, 2009

Auto-close open overlays


GLOBAL = {
    //...
    //...
    init:function(){
        //...
        //...
        //wire up language selection .. assuming it will be on [nearly] all pages.
        $('#glb-hdr-toolbar .heading').bind('click keypress',function(e){
            if(!e.keyCode || e.keyCode == 32 || e.keyCode == 13){ //32=space, 13=enter
                var p = this.parentNode;
                $(p).toggleClass('open')
                //keep track of open overlays so that we are able to intelligently auto-close-open-overlays
                GLOBAL.overlays.pushUnique(p);
            }
        });
        
        //wire up intelligent auto-close-open-overlays
        // part of the convention, or 'magic', is that a className of "open" is used to control whether the overlay is 'open'.
        $('body').bind('click keypress',function(e){
            if(!e.keyCode || e.keyCode == 32 || e.keyCode == 13){ //32=space, 13=enter
                var targ = e.target,
                    i = 0,
                    keepOpen,
                    list = GLOBAL.overlays.list;
                ///
                while(targ && ++i<10){
                    for(var j in list){
                        if(targ == list[j]){
                            keepOpen = list[j];
                        }
                    }
                    targ = targ.parentNode;
                }
                
                for(var j in list){
                    if(keepOpen != list[j]){
                        $(list[j]).removeClass('open'); //or, we could just .hide() it... or similar... 
                    }
                }
                
            }
        });
    },
    
    //keep track of open overlays so that we are able to intelligently auto-close-open-overlays
    overlays:{
        list:[],
        pushUnique:function(o){
            var l = GLOBAL.overlays.list;
            for(var i in l){
                if(l[i] == o) return;
            }
            l.push(o);
        }
    },
    //...
    //...
};

Wednesday, August 12, 2009

Offscreen, not invisible.

Ran into this .. had a container that I was hiding with visibility:hidden, while loading markup into it.

Worked great except, of course, for all versions of IE. IE wouldn't show the badge until you moused over it. I tried all the usual IE slap-in-the-face-so-you-behave tricks to no avail (zoom, position, z-index, background, borders).

So I chose to hide the container by positioning it offscreen with left:-9999px. Bingo.

The particular content I was loading was a linkedin iframe badge - the "inline" version off their developer api page. I was loathe to switch over to the "popup" version, and am glad I dodged that bullet.

Wednesday, July 8, 2009

custom scrollbar

jScrollPane .. I've heard bad things about it.

fleXscroll .. I've used it, it's nice, but it's not free.

jsScrolling .. I haven't used it, but it looks nice and it's free.

Improved email validation, plus multiple emails validation

jQuery validation plugin .. was causing ff2 to choke. It also didn't have a multiple-email validator .. or a split-up phone validator -- although on that last one, I highly advise you push back - a phone # field should just be ONE INPUT.


Tuesday, July 7, 2009

The *real* way to disable text selection

I was recently asked by UX to make the text of my fancy custom dropdown selects unselectable. A lot of silly scripts out there that disable text selection are application-unfriendly. They go beyond unselectable and make the element totally non-interactive. But I still want the dropdown to work when you click it. Solution = css for the "good" browsers, and javascript for internet explorer.

CSS


.unselectable{
    -moz-user-select: none;
    -webkit-user-select: none;
}


JS


if(document.attachEvent){
    function returnFalseFn(){
        return false;
    }
    jQuery('.unselectable').each(function(){
        this.attachEvent('onselectstart', returnFalseFn);
    });
}

Wednesday, June 24, 2009

Strange IE6 bug - show/hide select, hemorrhage option innerHTML

We've all had to code up those ever-so-fun branching selects. I chose to implement it by showing or hiding (display:block/none) the container of the label+select corresponding to the appropriate branch. And IE6 decided to hemorrhage option innerHTML onto the page - for 1 of the 3 branches: the middle one. I solved it by adding "position:absolute" to the css for the container.

Friday, June 12, 2009

Some cross-browser findings

ff2 mac: doesn't understand floating-point percentages (ie: 107.3% = 107%) - ran into this with font sizes.

ff3 thinks that if your parent is visibility:hidden, but you are visibility:visible, that you should display. (wrong.)

IE8 VHD from microsoft on vpc2007 is not the same as ie8 in the wild. Button element widths seem to be at least 1px wider in the wild.

Friday, May 22, 2009

A Little Elementool Love

I started a google code project because I've been using elementool lately and felt that it needed some help.

Elementool firefox greasemonkey userscript

Current features

  • Adds row-highlight rollovers
  • Click anywhere to go to the only link in that row (that bug)

Suggestions welcome!

Friday, May 15, 2009

JASH - debug safari

Need to debug javascript/css on .. safari? Sure, the thing is coming along in that respect, but its built-in tools and even firebug lite just don't cut it sometimes. Give JASH a go.

(Not sure I'd recommend JASH for ie debugging, since the script debugger / debugbar / ie developer toolbar are a little more convenient)

UPDATE: I've used JASH a little more, and found it useful a little less.

jQuery settles down with dojo, gains weight

Sure, this is months-old news, but still. I remember back in the good ol' days when jQuery was a young, athletic rising star. Then it met dojo and settled down.. and gained weight. Look under the jQuery hood and you'll see .. two libraries blast-welded together, complete with their own separate prenups, er, copyright blocks: jQuery and Sizzle, from the dojo foundation.

Wednesday, May 13, 2009

Disabled input fires no events

Evidently, a dom node "<input disabled="disabled"/>" will not fire any events, even if you do, say, something as direct as this: "<input disabled="disabled" onclick="alert('clicked it.');"/>"

Tuesday, May 12, 2009

IE JSON silent fail - size limit

Trying to get a ton (600K) of JSON via XHR into IE.. silently failing. Oh, it fetches the data, it just can't parse the data, either through jQuery's built-in JSON parsing or even by the ol' eval.

Trimming the data down to 60K, there were no problems. The data is a repeating set of dummy objects, so I'm almost certain that I didn't just happen to trim out some problem section of the data. I'm convinced that IE is choking on the data simply because it is more than it can handle. My guess is that JScript doesn't throw the usual "your script is taking too long to execute" warning when executing a single "eval".

I 'solved' the problem by ditching XHR and using dynamic script tags:

data.js


namespace.onData({theGigantic:json............});

Saturday, May 2, 2009

ie6 and PNGs

Thanks to a tip from Gena Wilson:

http://www.dillerdesign.com/experiment/DD_belatedPNG/

The cool part: it doesn't use AlphaImageLoader, it uses VML (Microsoft's idea of svg).

How to Avoid CSS Hacks

I've decided to use a very tiny bit of javascript to free me of css hacks. I already use js to add a class "jsEnabled" to the <html> element so that I can style things differently if I know that javascript is going to swoop in and alter them later (tabs, carousels, etc). Taking this a step further, I put the browser name + version number in the html element classname. Then in the css, you can write ".ie7 .my-div {float:none;}" Sure, we're not supposed to rely on javascript. But we usually do. And this is just for minor tweaks anyway. The site will still *work* with js turned off, you'd just lose that tiny css tweak you made that relies on it.

Thursday, April 23, 2009

ie6 + position:fixed nugget

Anyone who hates css expressions .. feel free to leave now, and end up doing the same thing in a more convoluted manner in 'regular' javascript. If it eases your conscience any -- I know it does mine -- this css is sourced in a conditional comment just for ie6


#my-fixed-position-element{
    top:expression(Math.max(document.documentElement.scrollTop + (document.getElementsByTagName('html')[0].offsetHeight / 2), 20) + 'px');
}

Wednesday, April 22, 2009

Don't Leave Keyboarders in the Cold

A handy jQuery plugin to make it easier to play nice with the keyboard-centric folks and improve accessibility

This code assumes the definition of a 'keys' object that identifies keycodes by more readable names, such as keys = {space:32, enter:13};


    // make a new plugin that handles click + keypress,
    // assuming that hitting space or enter counts as a 'click'.
    $.fn.klik = function(f){
        return this.bind('click keypress', function(e){
            if(!e.keyCode || e.keyCode == keys.space || e.keyCode == keys.enter){
                return f.call(this, e);
            }
        });
    };

Thursday, April 16, 2009

Close on MouseOut, Except .. Related Target

For a mouseout event, the event target is the thing you're leaving - mousing out of - the event relatedTarget is the thing you're leaving to, or entering.


        jQuery('#something').mouseout(function(e){
            /*
                don't close the subnav if we've moused out -> onto an autocomplete resultset.
            */
            var targ = e.relatedTarget, i = 0;
            //we're only checking the most recent 5 parents (ancestors).
            while(targ && ++i<5){
                //don't close the subnav if we're mousing over an autocomplete list.
                if(targ.className && targ.className.indexOf('ac_results')>-1){
                    return;
                }
                targ = targ.parentNode;
            }
            /**/
           
            //ok, close me..
            $(this).hide();
        });

YUI-Like Javascript Namespaces

I really like the idea of Javascript namespaces for projects that have a lot of javascript (or even a little javascript on a lot of pages). YUI has a really good implementation, so I took a look at how they did theirs and ripped out the namespace creation part so I could use it on any project I wanted without having to include the YUI library.

The biggest benefit to using namespaces is avoidance of collisions. When using namespaces, you can be sure that no 3rd-party library or other coder is overwriting your functions or properties. Also, you can also use a javascript compresser / combiner to combine a lot of JS files into 1 JS file in order to reduce the amount of requests you make to the server. If you use namespaces, you can be sure that when you put all of the JS files together, you won't have any collisions.

To use the code, first put the following code in a globl js file:

if (typeof YourSite == "undefined") {
   var YourSite = {};
}

YourSite.namespace = function() {
    var a = arguments, o = null, i, j, d;
    for (i = 0; i < a.length; i = i + 1) {
        d = a[i].split(".");
        o = window;
        for (j = 0; j < d.length; j = j + 1) {
            o[d[j]] = o[d[j]] || {};
            o = o[d[j]];
        }
    }
    return o;
};

Then for each page or section of your site you can create a namespace:
YourSite.namespace("YourSite.Section1");
Then when creating functions or properties, you can write:

YourSite.Section1.myProperty = "blah";

YourSite.Section1.myFunction = function(){
    alert(YourSite.Section1.myProperty);
}

YourSite.Section1.myFunction();


Wednesday, April 15, 2009

What's the body's height?

Written jQuery-style, but only mildly dependent.

I needed/wanted this code because I was making a modal overlay and wanted the modality to exactly cover the body. The 'modality' is what I've termed the click-proof smoked-glass panel (div).

Note: this is not intended to give you the viewport diemensions, but rather the entire [scrollable] document dimensions.


    // var docSize is declared elsewhere
    $(window).resize(function(){
        var h = $('body')[0];
        docSize = {
            width: h.scrollWidth
            ,height: Math.max(
                $('html')[0].scrollHeight, //webkit
                h.scrollHeight, //ff
                document.documentElement.offsetHeight //ie
            )
        };
    });

Wednesday, March 18, 2009

'Dynamic' Image Headers

Sure, sifr is probably the way to go. Unless it's not. Or the client says no.

Prerequisite: A text file listing all the headers to be made.

So, first off, I tried ImageMagick. It worked like a charm for OPAQUE gifs. Then stirred in some fancy masking stuff to clear out all white pixels. Now I had very nice 22pt headers. But the small headers looked miserable. 13pt looked just fine, but 12pt looked terrible. And it had to be 12pt. I tried smoothing commands to no avail.

So I decided to create one tall image with all the headers by just pasting the text file into fireworks and saving as png8 with transparency. That way, they *look* perfect. And I wrote a processing script, or "sketch" to create all the headers from the master headerlist-image.

Processing Code Snippet


String[] headlineStrings = {
"HOME",
"ABOUT US",
"ETCETERA",
"THESE ARE NOT THE HEADERS I WAS GIVEN",
"THE LIST WAS 168 HEADERS"
};

int LARGE = 1;
int SMALL = 2;
int usingSize = LARGE;
String mySize = usingSize == LARGE ? "large" : "small";
String prefix = usingSize == LARGE ? "lrg" : "sm";
int myWidth = usingSize == LARGE ? 391 : 213;
int myHeight = usingSize == LARGE ? 16 : 9;
int myOffsetHeight = usingSize == LARGE ? 22 : 12;

PGraphics pg = createGraphics(myWidth, myHeight, JAVA2D);
PImage a = loadImage("c:\\documents and settings\\dennish\\desktop\\columbia headers\\headers-" + mySize + ".png");
//PFont font = loadFont("DispatchCond-Bold-48.vlw");
//PFont font = createFont("DispatchCond-Bold", 12, true);

//String[] fontList = PFont.list();
//println(fontList);

//size(440, 900);
//fill(0);
smooth();

for(int i = 0; i < headlineStrings.length; i++){
    //textFont(font, 12);
    //text(headlineStrings[i], 0, i*12);
    pg.beginDraw();
    pg.background(0,0);
    pg.image(a, 0, 0 - (i * myOffsetHeight)); //12px because the font-size is 12px, and therefore each line of text will occupy 12px vertically.
    pg.endDraw();
    pg.save("c:\\dev\\headlines\\20090317\\hdr-" + prefix + "-" + headlineStrings[i] + ".png");
    pg.dispose();
}

One last problem: these have to be GIFs, and the only way the technique would work so far is by making them PNGs.

So .. back into fireworks for a batch export. Final gotchya: have to click "edit" next to the filetype export selection dropdown, and make sure the GIF has (alpha) transparency enabled.

Wednesday, March 11, 2009

LinkMania

I plan to post more links here over time. Linkrot commence!

Have I just become a tool?

Can you detect font availability?

Speaking of Fonts...

jquery-fontavailable

cufón - fonts for the people

cufón is a newish sifr [clone].

Just posting here for my ~ahem~, our future reference.

http://cufon.shoqolate.com/

JAWR = awesome, but please make it work with sitemesh

Any astute reader out there.. use JAWR on your J2EE projects. It's fabulous. It just doesn't seem to play well with sitemesh (and I put the blame on sitemesh).

JAWR allows for properties-file-driven bundling + auto-minification (via YUI-Compressor [or your favorite, maybe]) of CSS and JS. It also supports a debug mode, which you can switch on with one variable change.

Know of a great .NET, PHP, or Python equivalent of JAWR? Please comment!

H1 > IMG or CSS-Replacement?

It always depends on the circumstances, but I'm generally leaning slightly in favor of using img tags in headers instead of the fuss of css image replacement.

And please don't bring up sifr (siffer) - I like, you like it, or maybe neither of us do... but sometimes it just isn't a good fit.

  • + Css image replacement: a touch more accessible
  • + IMG tag in the header: faster to code, easier to maintain, unless you've already built/bolted on an automated solution for both the images and the css.

SEO values for either are open to wild speculation, but the general opinion is a tad in favor of the img tag setup. Add to it the dev. time (time=$, no not javascript library, money) you save, and that explains my leaning. Note - leaning - not zealotry or dogmatism, just leaning.

Wednesday, January 21, 2009

Chrome's view source isn't all there

Left: Chrome, Right: Firefox.

UPDATE: view-source is just making a new request for the page, which does not reflect any posted data--ie. a new blank, initial form.

Chrome's view-source omitted some text.

I think it has to do with the "pretty-formatting" that chrome applies to the view source. It was just a sample spring+struts+tiles application I was playing with. It was text that was a direct child of the body tag - not something that I'd ever code that way, but surprising that it didn't show up in view source in chrome.