Tagged as javascript

26 Aug 2008
23 Jul 2008

JavaScript line numbering for code samples

Whoopsie! While poking around on this site the other night I was horrified to discover it was pretty badly b0rked in IE. Not quite sure how I managed to miss that, but hopefully I've put it right now.

As bad luck would have it, one of the things that was broken was a javascript line-numbering function that I'd posted here previously. Best make amends with a corrected version for any readers I've lead astray, eh?

So where previously we had:

function codeLineNumbering() {
    $$('pre.code code').each(function(code){
        var count = code.innerHTML.split("\n").length - 1;
        var lines = $A($R(1,count)).join("\n");
        code.insert({before:'<pre class="line"><code>'+lines+'</code></pre>'});
    });
}

We now have:

function codeLineNumbering() {
    $$('pre.code code').each(function(code){
        var nodes = code.childNodes;
        var count = 1;
        var lines = [];
        for (var i=0; i < nodes.length; i++) {
            if (nodes[i].nodeType != 3) continue;
            var matches = nodes[i].nodeValue.match(/[\r\n]/g);
            if (matches) count += matches.length;
        }
        for (var i=1; i < count; i++) { lines.push(i); }
        code.up().insert({before:'<pre class="line"><code>'+lines.join("\n")+'</code></pre>'});
    });
}

The crux of the issue was that innerHTML seems to behave slightly differently in IE. Other browsers give you a faithful recreation of what's present in the document source, while IE gives you it's reconstituted whitespace-insensitive version.

In the old version of the code I was simply splitting innerHTML on newlines and counting how many lines I had in the resultant array. But with IE's interpretation of innerHTML that doesn't work so well since the odd newline may have been ditched or smooshed into its neighbour.

As a workaround, the substitute function uses the child nodes of the syntax-highlighted section of the document instead, which seem to be more faithful to the original source. I iterate through these looking for plain text nodes, and counting up how many newlines or carriage returns are present in each.

As a caveat I should say that I believe some of this behaviour is dependent on what your doctype and white-space CSS property are set to. Also, the code above makes the assumption that newlines will only be present in root text nodes and not nested within child elements. That's a safe assumption given the method of syntax highlighting I'm using, but it may not be for you.

Tsk, the perils of coding in public.

0 comments

10 Jul 2008
8 Jul 2008

A minor morsel

I haven't posted in ages, so I might as well use this to break radio silence. It's a solution to this little programming brain teaser from Dustin Diaz. I don't think it's as much of a puzzle as he seemed think, given by the responses in the comments and how long it took me, but regardless:

var listA = ['a', 'b', 'c', 'c', 'd', 'e', 'e', 'e', 'e', 'e', 'f', 'e', 'f', 'e', 'f', 'a', 'a', 'a', 'f', 'f', 'f'];
var listB = [];

var count = 0;

listA.forEach(function(val,key,arr){
    var matchPrev = (val == arr[key - 1]);
    var matchNext = (val == arr[key + 1]);

    count = matchPrev ? count+1 : 0;

    if (count == 2) {
        val = '<span>' + val;
    }
    if (count >= 2 && !matchNext) {
        val += '</span>';
    }

    listB.push(val);
});

console.log(listB.join(' '));

As some of the commenters' solutions show, it can be done in a one-line regular expression, but I was sticking to the "rules" implicit in the original post.

0 comments

27 May 2008
21 Apr 2008

Prototype, jQuery and the toggling of hidden elements

Just wanted to share a couple of little Prototype niggles. In general I'm pro-Prototype because it feels like it's a closer fit to the typical ways of working that you see in non-library-using Javascript. JQuery, on the other hand, has chaining and acts on elements using the same interfaces regardless of whether there are one or many. From the dabbling I've done, it seems like that gets you into the odd head-twisting situation, but I've yet to embark on a big project with jQuery, so judgement is reserved for now.

Having said that, here's one instance where jQuery comes out on top. Showing & hiding of elements is a pretty common JS/DOM task and you're probably familiar with an issue where hidden elements need an inline style of style="display:none;" to allow them to be shown again. This is because the un-hiding is done by setting the display property of the element to an empty string, thereby allowing the element to re-inherit it's default style. If the styling is done via CSS instead (.hide { display:none; }) then the styling is further up the inheritance chain so the element always inherits from here instead and can't be unhidden.

Prototype has the most basic show/hide implementation and makes no attempt at a work around. Its documentation even makes specific reference to the issue: "Element.show cannot display elements hidden via CSS stylesheets. Note that this is not a Prototype limitation but a consequence of how the CSS display property works".

Here's the implementation (from version 1.6; current at time of writing):

Element.Methods = {
    hide: function(element) {
        $(element).style.display = 'none';
        return element;
    },
    show: function(element) {
        $(element).style.display = '';
        return element;
    }
}

But that's not the end of the story. JQuery does manage a workaround, and an effective one at that. When failing to show a hidden element it adds a temporary, unadorned element of the appropriate type to the document and checks what its display property is set to by default. Finally, if all else fails it resorts to "block".

Here's a version of the jQuery methods, ported for use with Prototype (the originals begin at line 2890 in jquery-1.2.3.js):

Element.addMethods({
    show: function(elem){
        elem.style.display = elem.oldDisplay || '';
        if (elem.getStyle('display') == 'none') {
            var test = document.createElement(elem.tagName);
            $(document.body).insert({bottom: test});
            elem.style.display = test.getStyle('display');
            if (elem.style.display == 'none')
                elem.style.display = 'block';
            test.remove();
        }
        return elem;
    },
    hide: function(elem){
        elem.oldDisplay = elem.oldDisplay || elem.getStyle('display');
        elem.setStyle({display: 'none'});
        return elem;
    }
});

Much nicer. I'm not sure what the guiding philosophy is for what gets included and what's left out of the big JS libraries. It seems like something like this should be in there as an easy fix to a common problem. I would think that the identity of the libraries lies more in their syntax and aesthetics than their capabilities, so there's little to lose from liberal cross-pollenation.

0 comments

26 Jan 2008
6 Oct 2007
17 Sep 2007
26 Jun 2007
26 Sep 2006
8 Mar 2006
29 Aug 2005
25 Aug 2005

Where am I?

This site belongs to Matthew J. Tarbit esquire. A tired old web developer holed-up in a hideaway somewhere in the depths of Leeds, England.

Along with being a home for my ramblings and linkings, it's also a resting place for the bones of a shared blog by the name of Pixelised, now long departed.

If you feel the need, you may add my outpourings to the deluge that is your already overflowing info drip feed.

Or why not rest a while and dig through my entries like a corpulent pig in search of all that is truffle-icious.