Sisiutl
Sisiutl

Reputation: 4985

How to handle Firefox inserting text elements between tags

I'm trying to write javascript to find page elements relative to a given element by using parentNode, firstChild, nextSibling, childNodes[], and so on. Firefox messes this up by inserting text nodes between each html element. I've read that I can defeat this by removing all whitespace between elements but I've tried that and it doesn't doesn't work. Is there a way to write code that works on all modern browsers?

For example:

<div id="parent"><p id="child">Hello world</p></div>

In IE parent.firstChild is child but in Firefix it's a phantom Text element.

Upvotes: 2

Views: 1873

Answers (4)

David Brockman
David Brockman

Reputation: 567

You have to check that the nodeType == 1.

if (el.nodeType === 1) {
    return el;
}

I wrote a small DOM traversing class for ya (mostly copied from MooTools). Download here: http://gist.github.com/41440

DOM = function () {

    function get(id) {
        if (id && typeof id === 'string') {
            id = document.getElementById(id);
        }
        return id || null;
    }

    function walk(element, tag, walk, start, all) {
        var el = get(element)[start || walk], elements = all ? [] : null;
        while (el) {
            if (el.nodeType === 1 && (!tag || el.tagName.toLowerCase() === tag)) {
                if (!all) {
                    return el;
                }
                elements.push(el);
            }
            el = el[walk];
        }
        return elements;
    }

    return {

        // Get the element by its id
        get: get,

        walk: walk,

        // Returns the previousSibling of the Element (excluding text nodes).
        getPrevious: function (el, tag) {
            return walk(el, tag, 'previousSibling');
        },

        // Like getPrevious, but returns a collection of all the matched previousSiblings.
        getAllPrevious: function (el, tag) {
            return walk(el, tag, 'previousSibling', null, true);
        },

        // As getPrevious, but tries to find the nextSibling (excluding text nodes).
        getNext: function (el, tag) {
            return walk(el, tag, 'nextSibling');
        },

        // Like getNext, but returns a collection of all the matched nextSiblings.
        getAllNext: function (el, tag) {
            return walk(el, tag, 'nextSibling', null, true);
        },

        // Works as getPrevious, but tries to find the firstChild (excluding text nodes).
        getFirst: function (el, tag) {
            return walk(el, tag, 'nextSibling', 'firstChild');
        },

        // Works as getPrevious, but tries to find the lastChild.
        getLast: function (el, tag) {
            return walk(el, tag, 'previousSibling', 'lastChild');
        },

        // Works as getPrevious, but tries to find the parentNode.
        getParent: function (el, tag) {
            return walk(el, tag, 'parentNode');
        },

        // Like getParent, but returns a collection of all the matched parentNodes up the tree.
        getParents: function (el, tag) {
            return walk(el, tag, 'parentNode', null, true);
        },

        // Returns all the Element's children (excluding text nodes).
        getChildren: function (el, tag) {
            return walk(el, tag, 'nextSibling', 'firstChild', true);
        },

        // Removes the Element from the DOM.
        dispose: function (el) {
            el = get(el);
            return (el.parentNode) ? el.parentNode.removeChild(el) : el;
        }

    };
}();



// Now you can do:
DOM.getFirst("parent") // first child
// or
DOM.getFirst("parent", "p") // first p tag child
// or
var el = DOM.get("parent") // get element by id
DOM.getFirst(el) // first child

Upvotes: 1

bandi
bandi

Reputation: 4244

Check the DOM level 2 core reference to see the various possible types of nodes, so you can filter out the undesired ones with a simple snippet of JavaScript. One solution is to monkey patch the object (see the answer of Vernicht) or if you do not like monkey patching, then you can add these methods to your own library, or an even better solution might be to use a fancy library like jQuery or prototype.

Upvotes: 0

Jader Dias
Jader Dias

Reputation: 90465

I have a workaround. You can insert the two methods below:

Element.prototype.fChild = function(){
    var firstChild = this.firstChild;
    while(firstChild != null && firstChild.nodeType === 3){
        firstChild = firstChild.nextSibling;
    }
    return firstChild;
 }
 Element.prototype.nSibling = function(){
    var nextSibling = this.nextSibling;
    while(nextSibling != null && nextSibling.nodeType === 3){
        nextSibling = nextSibling.nextSibling;
    }
    return nextSibling;
 }

and you can now use:

document.getElementById("parent").fChild();
document.getElementById("parent").nSibling();

instead of:

 document.getElementById("parent").firstChild;
 document.getElementById("parent").nextSibling;

Upvotes: 1

Ady
Ady

Reputation: 4736

you could use tagName to check the name of the tag. If undefined then this is your 'phantom' text node.

e.g.

function getFirstTag(node) {
  return ((node.firstChild.tagName) ? node.firstChild : node.firstChild.nextSibling);
}

Upvotes: 0

Related Questions