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 ?
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With