简体   繁体   中英

How to select HTML DOM elements which have :hover subclass?

CSS

p:hover { ... }
div { ... }
.something { ... }
#content a:hover { ... }

HTML

<div id="content">
    <p>
        <a href="#">Test</a>
    </p>
</div>

I need to select all elements, which have defined :hover subclass in CSS. For this example, it would be <p> and <a> elements.

Is it possible to do it in JavaScript ?

There is a set of functions, the so-called "selectors api" https://developer.mozilla.org/en/docs/Web/API/Document/querySelector , https://developer.mozilla.org/en/docs/Web/API/Document/querySelectorAll and similar. But, nearly the only thing you can't do with these functions, is selecting pseudo classes (such as :hover).

I'm afraid you will have to monitor the mouseover and maybe mouseleave events and store the currently hovered element in a separate variable. By using its parentNode property (and its parent's parentNode property), you will have access to the parent chain.

I would suggest something like this:

var hoveredElement = null;
document.addEventListener( "mouseover", function(e){
    hoveredElement = e.target;
});

At first I didn't think it was possible, but after some thinking I came up with this. I wrote it with ES2015 syntax because a couple of things (like using forEach on non-arrays) is easier with it but it could be written in ES5 syntax too if needed.

 let getElementsWithHoverRule = () => { let getSelectors = rule => { // get everything upto the first curly bracket let selectorText = rule.cssText.match(/^[^{]+/)[0]; // in case a rule has multiple selectors, // we will want to filter them separately // so we don't capture elements that share // styling but have different selectors return selectorText.split(','); }, selectors = [], rxHover = /:hover/; // loop through all the style sheets [...document.styleSheets].forEach(sheet => { // and all of the rules in those style sheets let rules = sheet.cssRules || sheet.rules; if (rules !== null) { [...rules].forEach(rule => { let ruleSelectors = getSelectors(rule); selectors = selectors.concat(ruleSelectors); }); } }); // find all of the rules that contain hover selectors = selectors.filter(selector => rxHover.test(selector)); // remove the :hover from the selectors so we can select them without the user // hovering their mouse over them selectors = selectors.map(selector => selector.replace(rxHover, '')) return document.querySelectorAll(selectors.join(', ')); }; let hoverElement = getElementsWithHoverRule(); console.log(hoverElement); // put red box around matched elements when the page is clicked document.addEventListener('click', () => { [...hoverElement].forEach(el => el.style.border = '5px outset #f00'); }, false); 
 p:hover { background: #eef } span, a:hover { background: #000; color: #fff; } div { color: #000; } .something { color: #00f } #content a:hover { color: #ff0 } 
 <div id="content"> <p> <a href="#">Test</a> non-link text </p> </div> <p>another <span>paragraph</span>. <a href="#">A link that is not inside of content</a></p> <br> <br> <br> 

What it does is use document.styleSheets to get a list of all the style sheets and then loops through all the rules in them extracting their selectors. It then filters out the rules that don't contain :hover and then removes hover from the ones that do and uses those new rules to select the elements.

Edit

In the original code, if a rule had multiple selectors such as .foo, #bar:hover , it would return both .foo and #bar . I've updated the code so it will only return #bar since that is the only selector for the rule that contains :hover

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM