JoLoCo
JoLoCo

Reputation: 1375

How best to determine how far through the DOM an element/node is?

I'm trying to find out how far through a document a specific element is, ideally as a percentage.

Specifically, I have a long XHTML document (it's a component of an ePub file, in fact), and I've got one <span id="where_am_i">hello</span> somewhere within it.

If this was the very first element in the document, the "percentage through" value would be 0. If it was exactly half-way through the document, it would be 50.

It won't necessarily be the top layer, it may be nested within other nodes.

I've considered using something like a recursive $(node).contents().each(function(){...}); and counting words to get to it, though I wonder if that might be a slow way to do it?

The document is text, it's very unlikely to contain images, or massively differing text size, so it's fine to just find out how far through the text #where_am_i is.

Thank you in advance!

Upvotes: 0

Views: 89

Answers (3)

jfriend00
jfriend00

Reputation: 707318

You can use this treeWalk function to find out where in the document a given element is:

Working demo: http://jsfiddle.net/jfriend00/LGT6x/

function findElementPercentage(target) {
    var cnt = 0;
    var pos;
    treeWalkFast(document.body, function(node) {
        if (node === target) {
            pos = cnt;
        }
        ++cnt;
    });
    // handle situation where target was not found
    if (pos === undefined) {
        return undefined;
    }
    // return percentage
    return (pos / cnt) * 100;
}

var treeWalkFast = (function() {
    // create closure for constants
    var skipTags = {"SCRIPT": true, "IFRAME": true, "OBJECT": true, 
        "EMBED": true, "STYLE": true, "LINK": true, "META": true};
    return function(parent, fn, allNodes) {
        var node = parent.firstChild, nextNode;
        while (node && node != parent) {
            if (allNodes || node.nodeType === 1) {
                if (fn(node) === false) {
                    return(false);
                }
            }
            // if it's an element &&
            //    has children &&
            //    has a tagname && is not in the skipTags list
            //  then, we can enumerate children
            if (node.nodeType === 1 && node.firstChild && !(node.tagName && skipTags[node.tagName])) {
                node = node.firstChild;
            } else  if (node.nextSibling) {
                node = node.nextSibling;
            } else {
                // no child and no nextsibling
                // find parent that has a nextSibling
                while ((node = node.parentNode) != parent) {
                    if (node.nextSibling) {
                        node = node.nextSibling;
                        break;
                    }
                }
            }
        }
    }
})();

FYI, you could probably also just use document.body.getElementsByTagName("*") and then walk through the nodeList until you find your element and use that index as a measure of how far you are through the whole list.

function findElementPercentage(target) {
    var all = document.body.getElementsByTagName("*");
    for (var i = 0; i < all.length; i++) {
        if (all[i] === target) {
            return (i/all.length) * 100;
        }
    }
}

Working demo: http://jsfiddle.net/jfriend00/AUjq7/

Upvotes: 1

juvian
juvian

Reputation: 16068

Jquery solution:

<div>
    <div id="test"></div>
    <div></div>
</div>
<div id="result"></div>

var all=$("*").not("script, IFRAME, STYLE, EMBED, OBJECT, head, html,body, meta, title, link");
function getPercentage(el){
    return all.index($(el))/all.length*100;
}
console.log(getPercentage("#test"))//25

Upvotes: 1

Aur&#233;lien Gasser
Aur&#233;lien Gasser

Reputation: 3120

If you have your document as a string, you can get a very rough estimate by using the indexOf javascript function

var html = $('body').html(); // or provided string
var percentage = html.indexOf('where_am_i') / html.length; 
var found = percentage >= 0;

Upvotes: 1

Related Questions