Select element without a child

2019-01-27 19:15发布

问题:

I have a page that might one of the following:

<span id='size'>33</span>

Or

<span id='size'>
    <b>33</b>
    <strike>32</strike>
</span>

I would like to grab the value '33' on both cases, is there a CSS selector I can use? I tried to use the following, #size with no b sibling or b which is a #size sibling:

document.querySelector('#size:not(>b), #size>b').innerText

But I keep getting an error- "Error: SYNTAX_ERR: DOM Exception 12"

According to w3 Spec only Simple Selectors are supported, the thing is that "greater-than sign" (U+003E, >)" is considered as part of the Simple Selectors definition.

回答1:

You can't do it with a regular CSS selector, but you can do it in a few lines of JS:

var element = document.querySelector('#size');
var b = element.querySelector('b');
var text = b ? b.innerText : element.childNodes[0].nodeValue;

console.log(text);


回答2:

So really you want significant text (ie other than whitespace, because in your second example there's probably tabs and returns between the span start tag and the b) of #size, or, if that doesn't exist, the significant text of its first element:

// Is text just whitespace?
function isWhitespace(text){
    return text.replace(/\s+/,'').length === 0;
}

// Get the immediate text (ie not that of children) of element
function getImmediateText(element){
    var text = '';

    // Text and elements are all DOM nodes. We can grab the lot of immediate descendants and cycle through them.
    for(var i = 0, l = element.childNodes.length, node; i < l, node = element.childNodes[i]; ++i){
    // nodeType 3 is text
        if(node.nodeType === 3){
            text += node.nodeValue;
        }
    }

    return text;
}

function getFirstTextNode(element){
    var text = getImmediateText(element);

    // If the text is empty, and there are children, try to get the first child's text (recursively)
    if(isWhitespace(text) && element.children.length){
        return getFirstTextNode(element.children[0])
    }
    // ...But if we've got no children at all, then we'll just return whatever we have.
    else {
        return text;
    }
}


回答3:

The day we'll have CSS Level 4 selectors and the parent selector you'll be able to use a simple selector but for now you can't do it directly.

You could iterate to find the first text node but here's a hacky solution :

var text = document.getElementById('size').innerHTML.split(/<.*?>/)[0];

To be used only if you have some idea of the content of your #size element.