JS: Get array of all selected nodes in contentEditable div

Here's a version that gives you the actual selected and partially selected nodes rather than clones. Alternatively you could use my Rangy library, which has a getNodes() method of its Range objects and works in IE < 9.

function nextNode(node) {
    if (node.hasChildNodes()) {
        return node.firstChild;
    } else {
        while (node && !node.nextSibling) {
            node = node.parentNode;
        }
        if (!node) {
            return null;
        }
        return node.nextSibling;
    }
}

function getRangeSelectedNodes(range) {
    var node = range.startContainer;
    var endNode = range.endContainer;

    // Special case for a range that is contained within a single node
    if (node == endNode) {
        return [node];
    }

    // Iterate nodes until we hit the end container
    var rangeNodes = [];
    while (node && node != endNode) {
        rangeNodes.push( node = nextNode(node) );
    }

    // Add partially selected nodes at the start of the range
    node = range.startContainer;
    while (node && node != range.commonAncestorContainer) {
        rangeNodes.unshift(node);
        node = node.parentNode;
    }

    return rangeNodes;
}

function getSelectedNodes() {
    if (window.getSelection) {
        var sel = window.getSelection();
        if (!sel.isCollapsed) {
            return getRangeSelectedNodes(sel.getRangeAt(0));
        }
    }
    return [];
}

You're so close! When you append the Document Fragment to the temporary span element, you've turned them into a manageable group, accessible through the trusty childNodes array.

    var selnodes = tempspan.childNodes;

Additionally, you're setting yourself up for some trouble with that for(i in selnodes) loop, which would return the elements in the array, PLUS the length property, and the __proto__ property, and any other properties the object may have.

You should really only use those kinds of for loops when looping over the properties in an object, and then always with if (obj.hasOwnProperty[i]) to filter out properties inherited from the prototype.

When looping through arrays, use:

    for(var i=0,u=selnodes.length;i<u;i++)

Finally, once you load that array, you'll actually need to check each element to see if it's a DOM node or a Text node before you can handle it. We can do that by checking to see if it supports the tagName property.

    if (typeof selnodes[i].tagName !== 'undefined')

Here's the whole thing:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript">
function getSelectedNodes(){
    var sel = window.getSelection();
    try{var frag=sel.getRangeAt(0).cloneContents()}catch(e){return(false);}
    var tempspan = document.createElement("span");
    tempspan.appendChild(frag);
    console.log(tempspan);
    window.selnodes = tempspan.childNodes;
    var output = ''
    for(var i=0, u=selnodes.length;i<u;i++){
        if (typeof selnodes[i].tagName !== 'undefined'){
          output += "A "+selnodes[i].tagName+" was found\n"
        }
        else output += "Some text was found: '"+selnodes[i].textContent+"'\n";
        //do something cool with each element here...
    }
    return(output)
}
</script>
</head>

<body contentEditable="true" onkeypress="return(keypress(event))">
<div>This <strong>div</strong> is <em>content</em> <span class='red'>editable</span> and has a couple of <em><strong>child nodes</strong></em> within it</div>
<br />
<br />
<a href="#" onmouseover="alert(getSelectedNodes())">hover here</a>
</body>
</html>