Skip to content

Instantly share code, notes, and snippets.

@danburzo
Last active June 30, 2020 13:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danburzo/e9c495ae2860161966d7800ee3203695 to your computer and use it in GitHub Desktop.
Save danburzo/e9c495ae2860161966d7800ee3203695 to your computer and use it in GitHub Desktop.
Highlight CSS stacking contexts on a web page

Highlight the CSS stacking contexts on a page.

This has been incorporated in a small library.

How to use

Paste the content of the stacking-contexts.js file into the browser's dev tools. Elements that (at least in theory) are stacking contexts will be highlighted with a red border / overlay. The elements' title attribute will reflect the reason and the z-index.

Note: in a browser that does not support a certain CSS property — e.g. Safari with contain — the property will not show up in the element's computed styles, so running this code in Safari will not highlight an object with contain: content as a stacking context. Make sure to test in a variety of browsers to track down differences.

/*
Find out if a certain DOM node is a stacking context,
and the reason why that's the case.
Reference:
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context
*/
function stackingCtx(el) {
if (el === document.documentElement) {
return ['doc'];
}
let s = getComputedStyle(el);
if (s.zIndex !== 'auto') {
if (s.position === 'absolute' || s.posiiton === 'relative') {
return [`position (${s.position})`, s.zIndex];
}
let ps = getComputedStyle(el.parentNode);
if (ps.display === 'flex') {
return ['flex-child', s.zIndex];
}
if (ps.display === 'grid') {
return ['grid-child', s.zIndex];
}
}
if (s.position === 'sticky' || s.position === '-webkit-sticky' || s.position === 'fixed') {
return [`position (${s.position})`, s.zIndex];
}
if (s.opacity !== '1') {
return ['opacity', s.zIndex];
}
if (s.mixBlendMode !== 'normal') {
return ['mix-blend-mode', s.zIndex];
}
if (s.transform !== 'none') {
return ['transform', s.zIndex];
}
if (s.filter !== 'none') {
return ['filter', s.zIndex];
}
if (s.perspective !== 'none') {
return ['perspective', s.zIndex];
}
if (s.clipPath !== 'none') {
return ['clip-path', s.zIndex];
}
if (CSS.supports('mask', 'none') && s.mask !== 'none') {
return ['mask', s.zIndex];
}
if (CSS.supports('mask-image', 'none') && s.maskImage !== 'none') {
return ['mask-image', s.zIndex];
}
if (CSS.supports('mask-border', 'none') && s.maskBorder !== 'none') {
return ['mask-border', s.zIndex];
}
if (s.isolation === 'isolate') {
return ['isolation', s.zIndex];
}
if (s.webkitOverflowScrolling === 'touch') {
return ['-webkit-overflow-scrolling', s.zIndex];
}
let wc = s.willChange.split(',').map(it => it.trim());
if (wc.some(k => k === 'opacity' || k === 'mix-blend-mode' || k === 'transform' || k === 'filter' || k === 'perspective' || k === 'clip-path' || k === 'mask' || k === 'mask-image' || k === 'mask-border' || k === 'isolation')) {
return ['will-change', s.zIndex];
}
if (CSS.supports('contain', 'none')) {
let sc = s.contain.split(/\s+/).map(it => it.trim());
if (sc.some(k => k === 'content' || k === 'strict' || k === 'paint' || k === 'layout')) {
return ['contain', s.zIndex];
}
}
return false;
}
/* <style> TTL */
function css(strings) {
let el = document.createElement('style');
el.textContent = strings.join('');
return el;
}
document.head.appendChild(css`
.is-stacking-ctx {
outline: 2px solid red;
}
.is-stacking-ctx:after {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 0, 0, 0.1);
content: ' ';
pointer-events: none;
}
`);
function markStackingCtxElements() {
[...document.querySelectorAll('*')].forEach(el => {
let ctx = stackingCtx(el);
if (ctx) {
if (!el.classList.contains('is-stacking-ctx')) {
el.classList.add('is-stacking-ctx');
}
let new_title = `reason: ${stackingCtx(el)[0]}; z-index: ${stackingCtx(el)[1]}`;
if (el.title !== new_title) {
el.title = new_title;
};
} else {
if (el.classList.contains('is-stacking-ctx')) {
el.classList.remove('is-stacking-ctx');
}
if (el.title) {
el.title === '';
}
}
});
}
(new MutationObserver(markStackingCtxElements))
.observe(document.documentElement, {
attributes: true,
childList: true,
subtree: true
});
markStackingCtxElements();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment