You cannot do this natively. There is no way of filtering elements by their children in any current selector specification. (This may come in a future selectors specification, however.)
You will therefore need to do it by doing the filtering yourself. This isn't particularly difficult. One way would be by selecting all the relevant elements, then filtering out the ones you don't want:
Array.prototype.slice.call(document.querySelectorAll("body > *"))
.filter(function(el) {
return !el.querySelector(':scope > blockquote');
})
This turns the selection into an array, then uses the Array#filter
method to get rid of all those that contain a blockquote
element.
Obviously this has far worse performance than a native selector, but it is the only way to do what you want in current JS.
As BoltClock rightly points out, :scope
is not yet an official standard. I was lulled into a false sense of security by the fact that Chrome does support it: other browsers as yet do not. If you want to exclude any top level element that contains a blockquote
element at any level, the best way to do this is:
return !el.querySelector('blockquote');