Last active
November 29, 2018 12:17
-
-
Save tomhodgins/a106d938cbab066f1c9621aed1dde883 to your computer and use it in GitHub Desktop.
Download ZIP to view and edit, or preview index.html online at: http://staticresource.com/helpers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Ancestor Selector | |
function ancestor(selector, ancestor, rule) { | |
let styles = '' | |
let count = 0 | |
Array.from(document.querySelectorAll(ancestor)) | |
.filter(tag => tag.querySelector(selector)) | |
.forEach(tag => { | |
const attr = (selector+ancestor).replace(/\W/g, '') | |
tag.setAttribute(`data-ancestor-${attr}`, count) | |
styles += `${ancestor}[data-ancestor-${attr}="${count}"] { ${rule} }\n` | |
count++ | |
}) | |
return styles | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Aspect Ratio | |
function aspectRatio(selector, ratio) { | |
let styles = '' | |
let count = 0 | |
document.querySelectorAll(selector).forEach(tag => { | |
const attr = selector.replace(/\W/g, '') | |
const width = tag.offsetWidth | |
const height = `${width/ratio}px` | |
tag.setAttribute(`data-aspect-${attr}`, count) | |
styles += `${selector}[data-aspect-${attr}="${count}"] { height: ${height} }\n` | |
count++ | |
}) | |
return styles | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Auto Expand | |
function autoExpand(selector, option) { | |
let styles = '' | |
let count = 0 | |
const features = { | |
width: tag => { | |
const computed = getComputedStyle(tag) | |
tag.style.width = 'inherit' | |
const width = parseInt(computed.getPropertyValue('border-left-width'), 10) | |
+ parseInt(computed.getPropertyValue('padding-left'), 10) | |
+ tag.scrollWidth | |
+ parseInt(computed.getPropertyValue('padding-right'), 10) | |
+ parseInt(computed.getPropertyValue('border-right-width'), 10) | |
tag.style.width = '' | |
return `width: ${width}px;` | |
}, | |
height: tag => { | |
const computed = getComputedStyle(tag) | |
tag.style.height = 'inherit' | |
const height = parseInt(computed.getPropertyValue('border-top-width'), 10) | |
+ parseInt(computed.getPropertyValue('padding-top'), 10) | |
+ tag.scrollHeight | |
+ parseInt(computed.getPropertyValue('padding-bottom'), 10) | |
+ parseInt(computed.getPropertyValue('border-bottom-width'), 10) | |
tag.style.height = '' | |
return `height: ${height}px;` | |
}, | |
both: tag => { | |
return features.width(tag) + features.height(tag) | |
} | |
} | |
document.querySelectorAll(selector).forEach(tag => { | |
const attr = selector.replace(/\W/g, '') | |
const evaluated = features[option](tag) | |
tag.setAttribute(`data-${attr}`, count) | |
styles += `${selector}[data-${attr}="${count}"] { ${evaluated} }\n` | |
count++ | |
}) | |
return styles | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Closest Selector | |
function closest(selector, ancestor, rule) { | |
let styles = '' | |
let count = 0 | |
Array.from(document.querySelectorAll(selector)) | |
.filter(tag => tag.closest(ancestor)) | |
.forEach(tag => { | |
const attr = (selector+ancestor).replace(/\W/g, '') | |
tag.closest(ancestor).setAttribute(`data-closest-${attr}`, count) | |
styles += `${ancestor}[data-closest-${attr}="${count}"] { ${rule} }\n` | |
count++ | |
}) | |
return styles | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Compare attribute values | |
function compareAttribute(selector, attribute, test, stylesheet) { | |
let styles = '' | |
let count = 0 | |
Array.from(document.querySelectorAll(selector)) | |
.filter(tag => test(attribute === 'value' ? tag.value : tag.getAttribute(attribute))) | |
.forEach(tag => { | |
const attr = selector.replace(/\W/g, '') | |
styles += stylesheet.replace(/:self|\$this/g, `[data-compare-${attr}="${count}"]`) | |
tag.setAttribute(`data-compare-${attr}`, count) | |
count++ | |
}) | |
return styles | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Container Queries | |
function container(selector, conditions, stylesheet) { | |
let styles = '' | |
let count = 0 | |
const features = { | |
minWidth: (el, number) => number <= el.offsetWidth, | |
maxWidth: (el, number) => number >= el.offsetWidth, | |
minHeight: (el, number) => number <= el.offsetHeight, | |
maxHeight: (el, number) => number >= el.offsetHeight, | |
minChildren: (el, number) => number <= el.children.length, | |
maxChildren: (el, number) => number >= el.children.length, | |
minCharacters: (el, number) => number <= ((el.value && el.value.length) || el.textContent.length), | |
maxCharacters: (el, number) => number >= ((el.value && el.value.length) || el.textContent.length), | |
minScrollX: (el, number) => number <= el.scrollLeft, | |
maxScrollX: (el, number) => number >= el.scrollLeft, | |
minScrollY: (el, number) => number <= el.scrollTop, | |
maxScrollY: (el, number) => number >= el.scrollTop, | |
minAspectRatio: (el, number) => number <= el.offsetWidth / el.offsetHeight, | |
maxAspectRatio: (el, number) => number >= el.offsetWidth / el.offsetHeight, | |
orientation: (el, string) => { | |
switch (string) { | |
case 'portrait': return el.offsetWidth < el.offsetHeight | |
case 'square': return el.offsetWidth === el.offsetHeight | |
case 'landscape': return el.offsetWidth > el.offsetHeight | |
} | |
} | |
} | |
document.querySelectorAll(selector).forEach(tag => { | |
const attr = (selector | |
+ Object.keys(conditions) | |
+ Object.values(conditions)).replace(/\W/g, '') | |
const results = [] | |
for (let test in conditions) { | |
results.push(features[test](tag, conditions[test]) ? true : false) | |
} | |
if (!results.includes(false)) { | |
tag.setAttribute(`data-container-${attr}`, count) | |
styles += stylesheet.replace(/:self|\$this/g, `[data-container-${attr}="${count}"]`) | |
count++ | |
} else { | |
tag.setAttribute(`data-container-${attr}`, '') | |
} | |
}) | |
return styles | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Days of the week | |
function days(options, stylesheet) { | |
const day = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'] | |
const results = [] | |
for (let test in options) { | |
results.push(day[new Date().getDay()] === options[test] ? true : false) | |
} | |
return results.includes(true) ? stylesheet : '' | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Elder Selector | |
function elder(selector, rule) { | |
let styles = '' | |
let count = 0 | |
document.querySelectorAll(selector).forEach(tag => { | |
const attr = selector.replace(/\W/g, '') | |
const siblings = Array.from(tag.parentNode.getElementsByTagName('*')) | |
const index = siblings.indexOf(tag) | |
siblings | |
.filter(sibling => siblings.indexOf(sibling) < index) | |
.forEach(sibling => { | |
sibling.setAttribute(`data-elder-${attr}`, count) | |
styles += `[data-elder-${attr}="${count}"] { ${rule} }\n` | |
count++ | |
}) | |
}) | |
return styles | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Element Query | |
function element(selector, test, stylesheet) { | |
let styles = '' | |
let count = 0 | |
document.querySelectorAll(selector).forEach(tag => { | |
const attr = (selector+test).replace(/\W/g, '') | |
if (test(tag)) { | |
tag.setAttribute(`data-${attr}`, count) | |
styles += stylesheet.replace(/:self|\$this/g, `[data-${attr}="${count}"]`) | |
count++ | |
} else { | |
tag.setAttribute(`data-${attr}`, '') | |
} | |
}) | |
return styles | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Element-based units | |
function eunit(selector, rule) { | |
let styles = '' | |
let count = 0 | |
document.querySelectorAll(selector).forEach(tag => { | |
const attr = selector.replace(/\W/g, '') | |
rule = rule.replace(/(\d*\.?\d+)(?:\s*)(ew|eh|emin|emax)/gi, | |
(match, number, unit) => { | |
switch(unit) { | |
case 'ew': | |
return tag.offsetWidth / 100 * number + 'px' | |
case 'eh': | |
return tag.offsetHeight / 100 * number + 'px' | |
case 'emin': | |
return Math.min(tag.offsetWidth, tag.offsetHeight) / 100 * number + 'px' | |
case 'emax': | |
return Math.max(tag.offsetWidth, tag.offsetHeight) / 100 * number + 'px' | |
} | |
}) | |
tag.setAttribute(`data-eunit-${attr}`, count) | |
styles += `[data-eunit-${attr}="${count}"] { ${rule} }\n` | |
count++ | |
}) | |
return styles | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// First Ever | |
function firstEver(selector, rule) { | |
let styles = '' | |
const tag = document.querySelector(selector) | |
if (tag) { | |
const attr = selector.replace(/\W/g, '') | |
tag.setAttribute(`data-first-${attr}`, '') | |
styles += `${selector}[data-first-${attr}] { ${rule} }\n` | |
} | |
return tag ? styles : '' | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// :has() selector | |
function has(selector, child, rule) { | |
let styles = '' | |
let count = 0 | |
Array.from(document.querySelectorAll(selector)) | |
.filter(tag => tag.querySelector(child)) | |
.forEach(tag => { | |
const attr = (selector+child).replace(/\W/g, '') | |
styles += `[data-has-${attr}="${count}"] { ${rule} }\n` | |
tag.setAttribute(`data-has-${attr}`, count) | |
count++ | |
}) | |
return styles | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// HEXA Colors | |
function hexa(hex, opacity) { | |
let color = hex.replace(/#/, '') | |
if (color.length === 3) { | |
color = color[0] + color[0] + color[1] + color[1] + color[2] + color[2] | |
} | |
let red = parseInt(color.substring(0, 2), 16) | |
let green = parseInt(color.substring(2, 4), 16) | |
let blue = parseInt(color.substring(4, 6), 16) | |
return `rgba(${red}, ${green}, ${blue}, ${opacity})` | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Visible Vertically Within Parent | |
function inSight(selector, rule) { | |
let styles = '' | |
let count = 0 | |
document.querySelectorAll(selector).forEach(tag => { | |
const attr = selector.replace(/\W/g, '') | |
let underTop = tag.offsetTop + tag.offsetHeight >= tag.parentElement.scrollTop | |
let aboveBottom = tag.offsetTop < tag.parentElement.scrollTop + tag.parentElement.offsetHeight | |
if (underTop && aboveBottom) { | |
styles += `[data-insight-${attr}="${count}"] { ${rule} }\n` | |
tag.setAttribute(`data-insight-${attr}`, count) | |
count++ | |
} else { | |
tag.setAttribute(`data-insight-${attr}`, '') | |
} | |
}) | |
return styles | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// String Match | |
function includes(selector, string, rule) { | |
let styles = '' | |
let count = 0 | |
Array.from(document.querySelectorAll(selector)) | |
.filter(tag => tag.textContent.includes(string)) | |
.forEach(tag => { | |
const attr = (selector+string).replace(/\W/g, '') | |
tag.setAttribute(`data-contains-${attr}`, count) | |
styles += `[data-contains-${attr}="${count}"] { ${rule} }\n` | |
count++ | |
}) | |
return styles | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta charset=utf-8> | |
<meta name=viewport content="width=device-width, initial-scale=1"> | |
<title>JS-in-CSS Helper Functions in ES6</title> | |
<!-- JS-in-CSS Stylesheet --> | |
<link rel=stylesheet href=styles.jic type=text/css> | |
<!-- JS-in-CSS plugin --> | |
<script src=https://staticasset.s3.amazonaws.com/jic.js></script> | |
<!-- JS-in-CSS Helper Functions --> | |
<script src=ancestor-selector.js></script> | |
<script src=aspect-ratio.js></script> | |
<script src=auto-expand.js></script> | |
<script src=closest-selector.js></script> | |
<script src=compare-attribute.js></script> | |
<script src=container-query.js></script> | |
<script src=days.js></script> | |
<script src=elder-selector.js></script> | |
<script src=element-query.js></script> | |
<script src=eunit.js></script> | |
<script src=first-ever.js></script> | |
<script src=has.js></script> | |
<script src=hexa.js></script> | |
<script src=includes.js></script> | |
<script src=in-sight.js></script> | |
<script src=last-ever.js></script> | |
<script src=next-sibling.js></script> | |
<script src=non-empty.js></script> | |
<script src=overflow.js></script> | |
<script src=out-of-sight.js></script> | |
<script src=parent.js></script> | |
<script src=previous-sibling.js></script> | |
<script src=protocol.js></script> | |
<script src=regex.js></script> | |
<script src=tag-count.js></script> | |
<script src=scoped-eval.js></script> | |
<script src=specificity.js></script> | |
<script src=variables.js></script> | |
<script src=viewport.js></script> | |
<script src=xpath-selector.js></script> | |
<h1>JS-in-CSS Helper Functions in ES6</h1> | |
<p><a href=https://gist.github.com/tomhodgins/a106d938cbab066f1c9621aed1dde883>Click here view on Github</a></p> | |
<h2>First Ever</h2> | |
<ul class=first> | |
<li>item | |
<li>item | |
<li class=target>target | |
<li>item | |
<li>item | |
</ul> | |
<h2>Last Ever</h2> | |
<ul class=last> | |
<li>item | |
<li>item | |
<li class=target>target | |
<li>item | |
<li>item | |
</ul> | |
<h2>Previous Element</h2> | |
<ul class=prev> | |
<li>item | |
<li>item | |
<li class=target>target | |
<li>item | |
<li>item | |
</ul> | |
<h2>Parent Element</h2> | |
<ul class=parent> | |
<li>item | |
<li>item | |
<li class=target>target | |
<li>item | |
<li>item | |
</ul> | |
<h2>Ancestor Selector</h2> | |
<ul class=ancestor> | |
<li>item with <span>span</span> | |
<li>item | |
<li class=target>target | |
<li>item | |
<li>item | |
</ul> | |
<h2>Next Element</h2> | |
<ul class=next> | |
<li>item | |
<li>item | |
<li class=target>target | |
<li>item | |
<li>item | |
</ul> | |
<h2>Auto Expand Demo</h2> | |
<div class=auto-expand> | |
<h3>Width</h3> | |
<input> | |
<h3>Height</h3> | |
<textarea></textarea> | |
</div> | |
<h2>Closest Selector</h2> | |
<div class=closest> | |
<div> | |
<div class=demo> | |
<div> | |
<div> | |
<div class=demo> | |
<div> | |
<div> | |
<div class=target>target</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<h2>Elder Siblings</h2> | |
<ul class=elder> | |
<li>item | |
<li>item | |
<li class=target>target | |
<li>item | |
<li>item | |
</ul> | |
<h2>XPath Selector</h2> | |
<div class=xpath> | |
<p>This paragraph contains the word 'test'</p> | |
<p>This paragraph does not</p> | |
</div> | |
<h2>Scoped Eval</h2> | |
<div class=scoped> | |
<textarea placeholder="Type to change the background"></textarea> | |
</div> | |
<h2>Element Queries</h2> | |
<div class=element-query> | |
<input placeholder="type more than 5 characters"> | |
</div> | |
<h2>Container Queries</h2> | |
<div class=container-query> | |
<input placeholder="type more than 5 characters"> | |
</div> | |
<h2>String Match</h2> | |
<ul class=includes> | |
<li>item | |
<li>target | |
<li>item | |
</ul> | |
<h2>Regex Match</h2> | |
<ul class=regex> | |
<li>item | |
<li>target | |
<li>item | |
</ul> | |
<h2>Element Based Units</h2> | |
<ul class=eunit> | |
<li class=ew>EW Units | |
<li class=eh>EH Units | |
<li class=emin>EMIN Units | |
<li class=emax>EMAX Units | |
</ul> | |
<h2>Aspect Ratio</h2> | |
<div class=aspect-ratio> | |
<div></div> | |
</div> | |
<h2>Frontend Variables</h2> | |
<ul class=variables data-background="gold"> | |
<li>item | |
<li data-color="red" data-background="lime">item | |
<li>item | |
<li>item | |
</ul> | |
<h2>Custom Specificity</h2> | |
<ul class=specificity> | |
<li>regular | |
<li class=target>class | |
<li id=target>id | |
</ul> | |
<h2>Viewport Visibility</h2> | |
<div class=viewport> | |
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p> | |
</div> | |
<script> | |
/* | |
Using a separate JS-in-CSS stylesheet to test this function so we're not reprocessing all of the rest of the styles on window.scroll | |
*/ | |
window.addEventListener('load', viewportStyles) | |
window.addEventListener('resize', viewportStyles) | |
window.addEventListener('scroll', viewportStyles) | |
function viewportStyles() { | |
var tag = document.querySelector('#viewportStyles') | |
if (!tag) { | |
tag = document.createElement('style') | |
tag.id = 'viewportStyles' | |
document.head.appendChild(tag) | |
} | |
tag.innerHTML = ` | |
.viewport p { | |
padding: 2em; | |
} | |
${viewport('.viewport p', 'partly', ` | |
color: red; | |
`)} | |
${viewport('.viewport p', 'fully', ` | |
background: lime; | |
`)} | |
` | |
} | |
</script> | |
<h2>Days of the week</h2> | |
<div class=days> | |
<div class=sunday>sunday</div> | |
<div class=monday>monday</div> | |
<div class=tuesday>tuesday</div> | |
<div class=wednesday>wednesday</div> | |
<div class=thursday>thursday</div> | |
<div class=friday>friday</div> | |
<div class=saturday>saturday</div> | |
<h3></h3> | |
</div> | |
<h2>Protocol Sniffer</h2> | |
<h3 class=protocol></h3> | |
<h2>Tag Count</h2> | |
<ul class=tagCount> | |
<li>item | |
<li>item | |
<li>item | |
<li>item | |
<li>item | |
<li>item | |
<li>item | |
<li>item | |
<li>item | |
<li>item | |
</ul> | |
<h2>HEXA Colors</h2> | |
<div class=hexa>Hover me to switch opacities</div> | |
<h2>Horizontal Overflow</h2> | |
<div class=overflow> | |
<pre>Lorem ipsum dolor sit amet.</pre> | |
<span class=left></span> | |
<span class=right></span> | |
</div> | |
<div class=overflow> | |
<pre>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</pre> | |
<span class=left></span> | |
<span class=right></span> | |
</div> | |
<div class=overflow> | |
<pre>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor.</pre> | |
<span class=left></span> | |
<span class=right></span> | |
</div> | |
<div class=overflow> | |
<pre>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore.</pre> | |
<span class=left></span> | |
<span class=right></span> | |
</div> | |
<div class=overflow> | |
<pre>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation.</pre> | |
<span class=left></span> | |
<span class=right></span> | |
</div> | |
<script> | |
window.addEventListener('load', overflowDemo) | |
window.addEventListener('resize', overflowDemo) | |
document.querySelectorAll('.overflow pre').forEach(tag => { | |
tag.addEventListener('scroll', overflowDemo) | |
}) | |
function overflowDemo() { | |
var tag = document.querySelector('#overflowDemo') | |
if (!tag) { | |
tag = document.createElement('style') | |
tag.id = 'overflowDemo' | |
document.head.appendChild(tag) | |
} | |
tag.innerHTML = ` | |
.overflow { | |
margin: 1em; | |
position: relative; | |
border-radius: 3px; | |
border: 1px solid; | |
} | |
.overflow pre { | |
margin: 0; | |
padding: 2em; | |
white-space: pre; | |
overflow: auto; | |
overflow-x: scroll; | |
} | |
.overflow .left, | |
.overflow .right { | |
display: block; | |
width: 50px; | |
height: 100%; | |
position: absolute; | |
top: 0; | |
opacity: 0; | |
pointer-events: none; | |
transition: .5s ease-in-out; | |
} | |
.overflow .left { | |
left: 0; | |
background: linear-gradient(90deg, rgba(0,0,0,.3) 0%, rgba(0,0,0,0) 100%); | |
} | |
.overflow .right { | |
right: 0; | |
background: linear-gradient(90deg, rgba(0,0,0,0) 0%, rgba(0,0,0,.3) 100%); | |
} | |
${overflow('.overflow pre', 'left', ` | |
:self ~ .left { | |
opacity: 1; | |
} | |
`)} | |
${overflow('.overflow pre', 'right', ` | |
:self ~ .right { | |
opacity: 1; | |
} | |
`)} | |
` | |
} | |
</script> | |
<h2>:has() selector</h2> | |
<div class=has> | |
<ul> | |
<li>item | |
</ul> | |
<ul> | |
<li>item | |
<li>item | |
</ul> | |
<ul> | |
<li>item | |
<li>item | |
<li>item | |
</ul> | |
<ul> | |
<li>item | |
<li>item | |
<li>item | |
<li>item with <span>span</span> | |
</ul> | |
</div> | |
<h2>In-Sight & Out-of-Sight Vertically within parent</h2> | |
<div class=sight>Lorem ipsum <a href=#>dolor sit</a> amet, consectetur adipisicing elit, <input> sed do eiusmod tempor incididunt ut labore et dolore magna <a href=#>aliqua</a>. Ut enim ad minim veniam, quis nostrud <input> exercitation ullamco laboris nisi ut aliquip ex ea commodo <a href=#>consequat</a>. Duis aute irure dolor in reprehenderit <input> in voluptate velit esse cillum dolore eu fugiat nulla <a href=#>pariatur</a>. Excepteur sint occaecat cupidatat non <input> proident, sunt in culpa qui officia deserunt mollit anim id <a href=#>est laborum.</a></div> | |
<script> | |
window.addEventListener('load', sightDemo) | |
window.addEventListener('resize', sightDemo) | |
document.querySelector('.sight').addEventListener('scroll', sightDemo) | |
function sightDemo() { | |
var tag = document.querySelector('#sightDemo') | |
if (!tag) { | |
tag = document.createElement('style') | |
tag.id = 'sightDemo' | |
document.head.appendChild(tag) | |
} | |
tag.innerHTML = ` | |
${inSight('.sight *', ` | |
background: lime; | |
`)} | |
${outOfSight('.sight *', ` | |
visibility: hidden; | |
`)} | |
` | |
} | |
</script> | |
<h2>Non-Empty Values</h2> | |
<select class=non-empty> | |
<option value>Select an option | |
<option value=1>Option 1 | |
<option value=2>Option 2 | |
<option value=3>Option 3 | |
</select> | |
<select class=non-empty> | |
<option value>Select an option | |
<option value=1>Option 1 | |
<option value=2>Option 2 | |
<option value=3>Option 3 | |
</select> | |
<h2>Compare Attribute Values</h2> | |
<div class=compare> | |
<input type=range min=0 max=100 step=1> | |
<div></div> | |
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Last Ever | |
function lastEver(selector, rule) { | |
let styles = '' | |
const tag = document.querySelectorAll(selector) | |
if (tag) { | |
const attr = selector.replace(/\W/g, '') | |
tag[tag.length - 1].setAttribute(`data-last-${attr}`, '') | |
styles += `${selector}[data-last-${attr}] { ${rule} }\n` | |
} | |
return tag ? styles : '' | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Next Sibling | |
function nextSibling(selector, rule) { | |
let styles = '' | |
let count = 0 | |
Array.from(document.querySelectorAll(selector)) | |
.filter(tag => tag.nextElementSibling) | |
.forEach(tag => { | |
const attr = selector.replace(/\W/g, '') | |
tag.nextElementSibling.setAttribute(`data-next-${attr}`, count) | |
styles += `[data-next-${attr}="${count}"] { ${rule} }\n` | |
count++ | |
}) | |
return styles | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Non-empty value | |
function nonEmpty(selector, rule) { | |
let styles = '' | |
let count = 0 | |
document.querySelectorAll(selector).forEach(tag => { | |
let attr = selector.replace(/\W/g, '') | |
if (tag.value && tag.value !== '') { | |
styles += `[data-nonEmpty-${attr}="${count}"] { ${rule} }\n` | |
tag.setAttribute(`data-nonEmpty-${attr}`, count) | |
count++ | |
} else { | |
tag.setAttribute(`data-nonEmpty-${attr}`, '') | |
} | |
}) | |
return styles | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Out of Sight Vertically in Parent | |
function outOfSight(selector, rule) { | |
let styles = '' | |
let count = 0 | |
document.querySelectorAll(selector).forEach(tag => { | |
const attr = selector.replace(/\W/g, '') | |
let aboveTop = tag.offsetTop + tag.offsetHeight < tag.parentElement.scrollTop | |
let underBottom = tag.offsetTop > tag.parentElement.scrollTop + tag.parentElement.offsetHeight | |
if (aboveTop || underBottom) { | |
styles += `[data-outofsight-${attr}="${count}"] { ${rule} }\n` | |
tag.setAttribute(`data-outofsight-${attr}`, count) | |
count++ | |
} else { | |
tag.setAttribute(`data-outofsight-${attr}`, '') | |
} | |
}) | |
return styles | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Horizontal Overflow | |
function overflow(selector, option, stylesheet) { | |
let styles = '' | |
let count = 0 | |
const features = { | |
left: tag => 0 < tag.scrollLeft, | |
right: tag => (tag.scrollLeft + tag.offsetWidth) !== tag.scrollWidth | |
} | |
Array.from(document.querySelectorAll(selector)).forEach(tag => { | |
const attr = (selector+option).replace(/\W/g, '') | |
if (features[option](tag)) { | |
styles += stylesheet.replace(/:self|\$this/g, `[data-overflow-${attr}="${count}"]`) | |
tag.setAttribute(`data-overflow-${attr}`, count) | |
count++ | |
} else { | |
tag.setAttribute(`data-overflow-${attr}`, '') | |
} | |
}) | |
return styles | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Parent Selector | |
function parent(selector, rule) { | |
let styles = '' | |
let count = 0 | |
Array.from(document.querySelectorAll(selector)) | |
.filter(tag => tag.parentElement) | |
.forEach(tag => { | |
const attr = selector.replace(/\W/g, '') | |
tag.parentElement.setAttribute(`data-parent-${attr}`, count) | |
styles += `[data-parent-${attr}="${count}"] { ${rule} }\n` | |
count++ | |
}) | |
return styles | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Previous Sibling | |
function previousSibling(selector, rule) { | |
let styles = '' | |
let count = 0 | |
Array.from(document.querySelectorAll(selector)) | |
.filter(tag => tag.previousElementSibling) | |
.forEach(tag => { | |
const attr = selector.replace(/\W/g, '') | |
tag.previousElementSibling.setAttribute(`data-prev-${attr}`, count) | |
styles += `[data-prev-${attr}="${count}"] { ${rule} }\n` | |
count++ | |
}) | |
return styles | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Protocol Sniffer | |
function protocol(options, stylesheet) { | |
const results = [] | |
for (let test in options) { | |
results.push(location.protocol === `${options[test]}:` ? true : false) | |
} | |
return results.includes(true) ? stylesheet : '' | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Regex Match | |
function regex(selector, regex, rule) { | |
let styles = '' | |
let count = 0 | |
Array.from(document.querySelectorAll(selector)) | |
.filter(tag => regex.test(tag.textContent)) | |
.forEach(tag => { | |
const attr = (selector+regex).replace(/\W/g, '') | |
tag.setAttribute(`data-regex-${attr}`, count) | |
styles += `[data-regex-${attr}="${count}"] { ${rule} }\n` | |
count++ | |
}) | |
return styles | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Scoped Eval | |
function scoped(selector, rule) { | |
let styles = '' | |
let count = 0 | |
document.querySelectorAll(selector).forEach(tag => { | |
const attr = selector.replace(/\W/g, '') | |
const evaluated = | |
rule.replace(/eval\( *((".*?")|('.*?')) *\)/g, (string, match) => | |
new Function(`return ${match.slice(1, -1)}`).call(tag) || '' | |
) | |
tag.setAttribute(`data-scoped-${attr}`, count) | |
styles += `[data-scoped-${attr}="${count}"] { ${evaluated} }\n` | |
count++ | |
}) | |
return styles | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Custom Specificity | |
function specificity(selector, number, rule) { | |
let styles = '' | |
let count = 0 | |
document.querySelectorAll(selector).forEach(tag => { | |
const attr = (selector+number).replace(/\W+/g, '') | |
let partial = `[data-specificity-${attr}="${count}"]` | |
let repeated = partial + partial.repeat(number) | |
tag.setAttribute(`data-specificity-${attr}`, count) | |
styles += `${repeated} { ${rule} }\n` | |
count++ | |
}) | |
return styles | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
${firstEver('.first li', ` | |
background: lime; | |
`)} | |
${previousSibling('.prev .target', ` | |
background: teal; | |
`)} | |
${parent('.parent .target', ` | |
border: 4px dashed red; | |
`)} | |
${ancestor('.ancestor span', 'li', ` | |
background: orange; | |
`)} | |
${nextSibling('.next .target', ` | |
background: dodgerblue; | |
`)} | |
${lastEver('.last li', ` | |
background: hotpink; | |
`)} | |
.auto-expand input, | |
.auto-expand textarea { | |
margin: 1em 0; | |
display: block; | |
} | |
${autoExpand('.auto-expand input', 'width')} | |
${autoExpand('.auto-expand textarea', 'height')} | |
.closest, .closest div { | |
border: 1px solid; | |
padding: 1em; | |
} | |
${closest('.closest .target', '.demo', ` | |
border: 4px dashed red; | |
`)} | |
${elder('.elder .target', ` | |
background: violet; | |
`)} | |
${xpath('//*[@class="xpath"]/*[contains(text(), "test")]', ` | |
background: yellow; | |
`)} | |
${scoped('.scoped textarea', ` | |
background: hsl(eval('this.value.length * 5'), 75%, 75%); | |
`)} | |
${element('.element-query input', el => el.value.length > 5, ` | |
:self { | |
background: lime; | |
} | |
`)} | |
${container('.container-query input', {minCharacters: 6}, ` | |
:self { | |
background: hotpink; | |
} | |
`)} | |
${includes('.includes li', 'target', ` | |
background: blue; | |
`)} | |
${regex('.regex li', /target/, ` | |
background: plum; | |
`)} | |
${eunit('.eunit .ew', ` | |
font-size: 10ew; | |
`)} | |
${eunit('.eunit .eh', ` | |
font-size: 90eh; | |
max-height: 100px; | |
`)} | |
${eunit('.eunit .emin', ` | |
font-size: 90emin; | |
max-height: 100px; | |
`)} | |
${eunit('.eunit .emax', ` | |
font-size: 10emax; | |
`)} | |
.aspect-ratio div { | |
margin: 0 auto; | |
background: lime; | |
} | |
${aspectRatio('.aspect-ratio div', 16/9)} | |
${variables('.variables ', '--color: blue')} | |
${variables('.variables li', ` | |
color: var(--color); | |
background: var(--background); | |
`)} | |
${specificity('.specificity li', 2, ` | |
background: hotpink; | |
`)} | |
${specificity('.specificity li.target', 2, ` | |
background: red; | |
`)} | |
${specificity('.specificity li#target', 1, ` | |
background: blue; | |
`)} | |
.days div { | |
margin: 1em 0; | |
padding: 1em; | |
border: 1px solid black; | |
} | |
${days(['sunday'], ` | |
.days .sunday { | |
background: red; | |
} | |
`)} | |
${days(['monday'], ` | |
.days .monday { | |
background: orange; | |
} | |
`)} | |
${days(['tuesday'], ` | |
.days .tuesday { | |
background: yellow; | |
} | |
`)} | |
${days(['wednesday'], ` | |
.days .wednesday { | |
background: green; | |
} | |
`)} | |
${days(['thursday'], ` | |
.days .thursday { | |
background: blue; | |
} | |
`)} | |
${days(['friday'], ` | |
.days .friday { | |
background: indigo; | |
} | |
`)} | |
${days(['saturday'], ` | |
.days .saturday { | |
background: violet; | |
} | |
`)} | |
/* On weekdays they are red */ | |
${days(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'], ` | |
.days h3:after { | |
content: 'It is a weekday' | |
} | |
`)} | |
/* On weekends they are yellow */ | |
${days(['saturday', 'sunday'], ` | |
.days h3:after { | |
content: 'It is the weekend' | |
} | |
`)} | |
${protocol(['file'],` | |
.protocol:before { | |
content: "You're on FILE://"; | |
} | |
`)} | |
${protocol(['http'],` | |
.protocol:before { | |
content: "You're on HTTP://" | |
} | |
`)} | |
${protocol(['https'],` | |
.protocol:before { | |
content: "You're on HTTPS://" | |
} | |
`)} | |
${protocol(['file'],` | |
.protocol:after { | |
content: " and this site is in DEVELOPMENT"; | |
} | |
`)} | |
${protocol(['http', 'https'],` | |
.protocol:after { | |
content: " and this site is in PRODUCTION"; | |
} | |
`)} | |
${scoped('.tagCount li', ` | |
background: hsl(eval("tagCount('.tagCount li', this) * 50 + 50"), 75%, 75%) | |
`)} | |
.hexa { | |
font-size: 18pt; | |
color: #c00; | |
background: ${hexa('#07f', .3)}; | |
transition: | |
color .2s ease-in-out, | |
background .2s ease-in-out | |
; | |
} | |
.hexa:hover { | |
color: ${hexa('#c00', .3)}; | |
background: #07f; | |
} | |
.has span { | |
background: red; | |
} | |
${has('.has ul', 'li:nth-child(3)', ` | |
background: cyan; | |
`)} | |
${has('.has *', 'span', ` | |
padding-top: 5px; | |
padding-bottom: 5px; | |
border: 4px dashed red; | |
`)} | |
.sight { | |
width: 200px; | |
height: 200px; | |
border: 4px solid purple; | |
overflow: auto; | |
position: relative; | |
} | |
${nonEmpty('.non-empty', ` | |
color: white; | |
background: orange; | |
`)} | |
.compare div { | |
width: 200px; | |
height: 200px; | |
} | |
${compareAttribute('.compare [type="range"]', 'value', num => num < 25, ` | |
:self + div { | |
background: red; | |
} | |
`)} | |
${compareAttribute('.compare [type="range"]', 'value', num => 25 <= num && num < 50, ` | |
:self + div { | |
background: orange; | |
} | |
`)} | |
${compareAttribute('.compare [type="range"]', 'value', num => 50 <= num && num < 75, ` | |
:self + div { | |
background: yellow; | |
} | |
`)} | |
${compareAttribute('.compare [type="range"]', 'value', num => 75 <= num && num <= 100, ` | |
:self + div { | |
background: lime; | |
} | |
`)} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Tag Count | |
function tagCount(selector, tag) { | |
return [].indexOf.call(document.querySelectorAll(selector), tag) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Cascading Frontend Variables | |
function variables(selector, rule) { | |
let styles = '' | |
let count = 0 | |
document.querySelectorAll(selector).forEach(tag => { | |
// Apply variables defined in supplied rule | |
rule.replace(/--([^;]+):(.+)[;}]*/gm, (string, property, value) => { | |
tag.setAttribute(`data-${property}`, value) | |
}) | |
const attr = selector.replace(/\W+/g, '') | |
const evaluated = rule.replace(/var\(--([^)]+)\)/g, (string, match) => { | |
// Check if has data attribute on self | |
if (tag.getAttribute(`data-${match}`) !== null) { | |
return tag.getAttribute(`data-${match}`) | |
// Check if parent has data attribute | |
} else if (tag.closest(`[data-${match}]`) && tag.closest(`[data-${match}]`).getAttribute(`data-${match}`) !== null) { | |
return tag.closest(`[data-${match}]`).getAttribute(`data-${match}`) | |
// Otherwise return global value | |
} else { | |
if (match in window) { | |
return (new Function(`return ${match}`))() || '' | |
} | |
} | |
}) | |
tag.setAttribute(`data-variable-${attr}`, count) | |
styles += `[data-variable-${attr}="${count}"] { ${evaluated} }\n` | |
count++ | |
}) | |
return styles | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Viewport Visibility | |
function viewport(selector, option, rule) { | |
let styles = '' | |
let count = 0 | |
const features = { | |
partly: (tag, rule) => { | |
const top = tag.offsetTop - innerHeight | |
const bottom = top + tag.offsetHeight | |
return (scrollY < tag.offsetTop + tag.offsetHeight) | |
&& (top < scrollY) | |
&& (bottom < scrollY + tag.offsetHeight) | |
? rule | |
: '' | |
}, | |
fully: (tag, rule) => { | |
const top = tag.offsetTop - innerHeight | |
const bottom = top + tag.offsetHeight | |
return (scrollY < tag.offsetTop) | |
&& (top < scrollY) | |
&& (bottom < scrollY) | |
? rule | |
: '' | |
} | |
} | |
document.querySelectorAll(selector).forEach(tag => { | |
const attr = selector.replace(/\W/g, '') | |
const evaluated = features[option](tag, rule) | |
tag.setAttribute(`data-viewport-${attr}`, count) | |
styles += `[data-viewport-${attr}="${count}"] { ${evaluated} }\n` | |
count++ | |
}) | |
return styles | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// XPath Selector | |
function xpath(selector, rule) { | |
let styles = '' | |
let count = 0 | |
const tags = [] | |
const result = document.evaluate( | |
selector, | |
document, | |
null, | |
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, | |
null | |
) | |
for (var i=0; i < result.snapshotLength; i++) { | |
tags.push(result.snapshotItem(i)) | |
} | |
tags.forEach(tag => { | |
const attr = selector.replace(/\W/g, '') | |
tag.setAttribute(`data-xpath-${attr}`, count) | |
styles += `[data-xpath-${attr}="${count}"] { ${rule} }\n` | |
count++ | |
}) | |
return styles | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment