Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 ?

like image 291
mspiderv Avatar asked Jul 12 '26 09:07

mspiderv


1 Answers

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

like image 108
Useless Code Avatar answered Jul 14 '26 00:07

Useless Code



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!