I have a css file, where I place all my styles by page.
How I can get the selectors' class/id by searching for a property in css.
For example, I want to change the text color by all pages. I need to get an array with all the selectors that have a property of "color".
How I can do this?
so fun, i have answered a similar question (but for another problem). :)
=> see = https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables
let myGroup = document.querySelector('#my-Group');
let myGroupCSS = getComputedStyle(myGroup);
Bt_change_Color.onclick = function () {
let current_Color = myGroupCSS.getPropertyValue('--color_for_group');
console.log('first color', current_Color); // #305395
myGroup.style.setProperty('--color_for_group', '#95305a');
}
#my-Group {
--color_for_group: #305395;
}
h2 {
color: var(--color_for_group);
}
h4{
color: var(--color_for_group);
}
p {
color: var(--color_for_group);
}
<div id="my-Group">
<h2>title</h2>
<h4>sub tilte</h4>
<p>paragraph</p>
</div>
<button id="Bt_change_Color"> Change Color </button>
Related
I have created this lightbulb exercise that toggles the SRC of the lightbulb, the background color, as well as the color of the text. As an extra challenge, I'm trying to see if I can condense my code using the toggle() method as I have 3 separate functions. Does anyone know if this is possible and how I could accomplish this?
<h1 class="title-text" id="title-text">Click the lightbulb to turn it on or off!</h1>
<img id="lightbulb" onclick="toggleLight();toggleColor();toggleText()" src="/personal-projects/pic_bulbon1.gif">
<script>
let lightbulb = document.getElementById("lightbulb");
let titleText = document.getElementById("title-text");
function toggleLight() {
if (lightbulb.src.match("bulbon1")) {
lightbulb.src = "/personal-projects/pic_bulboff1.gif"
} else {
lightbulb.src = "/personal-projects/pic_bulbon1.gif"
}
}
function toggleColor() {
if (lightbulb.src.match("bulboff1")) {
document.body.style.background = "black";
} else {
document.body.style.background = "#FEDD00";
}
}
function toggleText() {
if (lightbulb.src.match("bulboff1")) {
titleText.style.color = "white";
} else {
titleText.style.color = "black";
}
}
</script>
</body>
</html>
The If loops work fine. I just want to know how I could use toggle if its possible. All the tutorials I find for this type of thing involve jquery.
Using toggle method
You want to first create a class for toggling in your css. Each element you want to toggle should have a default state and a toggled state. Toggling will add/remove a single class.
See snippet below
Snippet
const LightBulb = document.getElementById("lightbulb");
const toggleLight = () => {
LightBulb.classList.toggle("lightBulbOn");
document.body.classList.toggle("bodyLightOn");
};
body {
background-color: black;
}
body,
body .title-text {
color: white;
}
body.bodyLightOn {
background-color: #fedd00;
}
body.bodyLightOn .title-text {
color: black;
}
#lightbulb::before {
content: url("https://upload.wikimedia.org/wikipedia/commons/thumb/b/b4/Gluehlampe_01_KMJ.png/340px-Gluehlampe_01_KMJ.png");
}
.lightBulbOn::before {
content: url("https://cdn.mos.cms.futurecdn.net/HaPnm6P7TZhPQGGUxtDjAg-320-80.jpg") !important;
}
<h1 class="title-text" id="title-text">Click the lightbulb to turn it on or off!</h1>
<div id="lightbulb" class="" onclick="toggleLight();">
</div>
Codepen
Let's say I want to create a custom element which bolds every other character. For example, <staggered-bold>Hello</staggered-bold> would become "Hello, where the H, l, and o are all bolded.
There's no nth-letter CSS selector, so as far as I know the only way to achieve this effect is to wrap each individual character with a span programmatically. To do that, I have an implementation that clones the text content into the Shadow Dom, so that the child content as specified by the user is not changed.
Unfortunately, by doing so, something like <staggered-bold><span class="red">red</span></staggered-bold> no longer works, because by cloning the content into the Shadow Dom, the class CSS declarations for the wrapped span no longer apply.
Here's a proof-of-concept implementation, showcasing that the red and blue text are in fact not red and blue:
customElements.define('staggered-bold', class extends HTMLElement {
constructor() {
super()
this
.attachShadow({ mode: 'open' })
.appendChild(document.getElementById('staggered-bold').content.cloneNode(true))
}
connectedCallback() {
// this is a shadow dom element
const text = this.shadowRoot.getElementById('text')
this.shadowRoot.querySelector('slot').assignedNodes().forEach(node => {
const content = node.textContent.split('').map((char) => {
return `<span class="char">${char}</span>`
}).join('')
const newNode = node.nodeType === Node.TEXT_NODE ? document.createElement('span') : node.cloneNode(true)
newNode.innerHTML = content
text.appendChild(newNode)
})
}
})
.red { color: red; }
.blue { color: blue; }
<p><staggered-bold>Some text</staggered-bold></p>
<p><staggered-bold><span class="red">Red</span> <span class="blue">Blue</span></staggered-bold></p>
<template id="staggered-bold">
<style>
.hide { display: none; }
.char:nth-child(odd) {
font-weight: bold;
}
</style>
<span class="hide"><slot></slot></span>
<span id="text"></span>
</template>
My question is this: what is a good approach to styling each character in a custom element while preserving characteristics provided in the light dom?
One approach I've considered is to manipulate the light dom directly, but I have been avoiding that since I think of the light dom as being in full control of the usage-site (ie. things get complicated very quickly if external JS is manipulating the child of staggered-bold). I'm open to being convinced otherwise, especially there's no real alternative.
I've also considered cloning the content into a named slot so that the original text is preserved, and yet the content continues to live in the light dom. However, I feel like this is still kind of icky for the same reason as the previous paragraph.
You can't have the cake and eat it
Global CSS does NOT style shadowDOM (unless you use CSS properties)
Easier to not use shadowDOM at all.
With an extra safeguard: store the state so the element is properly redrawn on DOM moves.
Note: The setTimeout is always required,
because the connectedCallback fires early on the opening tag;
there is no parsed (innerHTML) DOM yet at that time.
So you have to wait for that DOM to be there.
If you do need a TEMPLATE and shadowDOM, dump the whole .innerHTML to the shadowRoot; but Global CSS still won't style it. Or <slot> it.
Do read: ::slotted CSS selector for nested children in shadowDOM slot
If you go with <slot> consider the slotchange Event
but be aware for an endless loop; changing lightDOM will trigger the slotchange Event again
<staggered-bold>Some text</staggered-bold>
<staggered-bold><span class="red">Red</span> <span class="blue">Blue</span></staggered-bold>
<style>
staggered-bold { display: block; font: 21px Arial }
staggered-bold .char:nth-child(even) { color: blue }
staggered-bold .char:nth-child(odd) { color: red; font-weight: bold }
</style>
<script>
customElements.define('staggered-bold', class extends HTMLElement {
connectedCallback() {
setTimeout(() => { // make sure innerHTML is all parsed
if (this.saved) this.innerHTML = this.saved;
else this.saved = this.innerHTML;
this.stagger();
})
}
stagger(node=this) {
if (node.children.length) {
[...node.children].forEach( n => this.stagger(n) )
} else {
node.innerHTML = node.textContent
.split('')
.map(ch => `<span class="char">${ch}</span>`)
.join('');
}
}
})
document.body.append(document.querySelector("staggered-bold"));//move in DOM
</script>
In the end I attempted a strategy I'm calling the mirror node. The idea is the custom element actually creates an adjacent node within which the split characters are placed.
The original node remains exactly as specified by the user, but is hidden from view
The mirror node actually displays the staggered bold text
The below implementation is incomplete, but gets the idea across:
class StaggeredBoldMirror extends HTMLElement {
constructor() {
super()
}
}
customElements.define('staggered-bold', class extends HTMLElement {
constructor() {
super()
this
.attachShadow({ mode: 'open' })
.appendChild(document.getElementById('staggered-bold').content.cloneNode(true))
}
connectedCallback() {
setTimeout(() => {
const mirror = new StaggeredBoldMirror()
mirror.innerHTML = this.divideIntoCharacters()
this.parentNode.insertBefore(mirror, this)
})
}
divideIntoCharacters = (node = this) => {
return [...node.childNodes].map(n => {
if (n.nodeType === Node.TEXT_NODE) {
return n.textContent
.split('')
.map(ch => `<span class="char">${ch}</span>`)
.join('')
} else {
const nn = n.cloneNode(false)
nn.innerHTML = this.divideIntoCharacters(n)
return nn.outerHTML
}
}).join('')
}
})
customElements.define('staggered-bold-mirror', StaggeredBoldMirror)
.red {
color: red;
}
.blue {
color: blue;
}
staggered-bold-mirror .char:nth-child(odd) {
font-weight: bold;
}
<p><staggered-bold>Some text</staggered-bold></p>
<p><staggered-bold><span class="red">Red</span> <span class="blue">Blue</span></staggered-bold></p>
<template id="staggered-bold">
<style>
.hide { display: none; }
</style>
<span class="hide"><slot></slot></span>
</template>
The vanilla component can be outfitted with a slotchange listener in order to rebuild its mirror whenever its inner content changes. The disconnectedCallback method can also ensure that when one node is removed, the other is too.
Of course, there are downsides to this approach, such has potentially having to also mirror events and the fact that it still manipulates the light dom.
Depending on the use case, either this or Danny's answer works.
Is there a native API which can update the CSS variables scoped under a particular CSS class(or even any other complex CSS selector)predefined in a stylesheet? The question can be generalized for not just CSS variables but other CSS properties as well, i.e whether class specific CSS properties can be updated without targeting a specific HTML element, but by targeting the class definition itself.
Please find below the code snippets which demonstrates an example scenario. You can also find comments in the code to as to what I believe is happening/I am doing on specific lines.
var toggle = true;
function changeColor() {
document.documentElement.style.setProperty('--bg-color', toggle ? 'green' : 'red');
// this works for the "outer" div since there we receive global value(value defined in :root) of --bg-color
toggle = !toggle;
// here I want to also change the scoped value of --bg-color for "inner-primary" and "inner-secondary"
// currently I can do this by doing:
document.querySelectorAll('.inner-primary').forEach(ele => ele.style.setProperty('--bg-color', toggle ? 'blue' : 'yellow'))
document.querySelectorAll('.inner-secondary').forEach(ele => ele.style.setProperty('--bg-color', toggle ? 'yellow' : 'blue' ))
// another way I can see is: we dynamically insert a style tag, but this feels very awkward and can quickly get out of hand on multiple iterations
}
:root {
--bg-color: red;
}
body {
margin: 0;
}
.outer {
width: 100vw;
height: auto;
min-height: 100vh;
text-align: center;
background-color: var(--bg-color); /* receives value from :root */
}
.inner-primary,
.inner-secondary {
width: 100px;
height: 100px;
/* received scoped value from .inner-primary or .inner-secondary defined below*/
background-color: var(--bg-color);
border: 1px solid black;
margin: auto;
margin-bottom: 10px;
}
.inner-secondary {
--bg-color: yellow;
}
.inner-primary {
--bg-color: blue;
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Testing</title>
</head>
<body>
<div class="outer">
<div class="inner-primary"></div>
<div class="inner-secondary"></div>
<div class="inner-primary"></div>
<div class="inner-secondary"></div>
<button onclick="changeColor()">Change Color</button>
</body>
</html>
Please try running this to get a full idea of intended effect. You can click "Change Color" button at the bottom to see the effects in action.
To get the intended overriding for CSS variable --bg-color for classes inner-primary and inner-secondary, I had to use querySelectorAll with the required CSS selector(in this case just a class name) and iteratively set the CSS variable for each individual element found.
By nature of how CSS gets read by the browser, feels like the other solution to this is to dynamically insert a style element tag into the DOM, with the required CSS variable update, scoped under the required class name(or any other required selector)..
But this feels awkward and can quickly get out of hand if we don't implement some system to reuse the same style tag and not insert new ones during each toggle.
Is there any other way to do this? Any native API which can solve this without having to access individual elements or without inserting style tags dynamically..?
As suggested by A Haworth and referring Change CSS of class in Javascript? I was able update changeColor function to use CSSStyleSheet(MDN link) instead. Please find the updated function below, which uses this API:
var toggle = true;
function changeColor() {
document.documentElement.style.setProperty('--bg-color', toggle ? 'green' : 'red');
// solution using document stylesheets
const styleSheet = document.styleSheets[0]
const cssRules = Array.from(styleSheet.cssRules);
const primaryClassIndex = cssRules.findIndex(cssRule => cssRule.selectorText === '.inner-primary');
const secondaryClassIndex = cssRules.findIndex(cssRule => cssRule.selectorText === '.inner-secondary');
//update primary:
styleSheet.deleteRule(primaryClassIndex);
styleSheet.insertRule(`.inner-primary {--bg-color: ${toggle ? 'yellow' : 'blue'};}`, primaryClassIndex)
//update secondary:
styleSheet.deleteRule(secondaryClassIndex);
styleSheet.insertRule(`.inner-secondary {--bg-color: ${toggle ? 'blue' : 'yellow'};}`, secondaryClassIndex)
//toggle
toggle = !toggle;
}
This is still some concern here since it seems like we can only overwrite the entire cssRule(which may also include other CSS properties) for a particular selector, and not just one required property. But this may arguably be better than updating each individual element style or inserting style tags as mentioned in the question.
Can check the full working codepen at => https://codepen.io/yadus/pen/mdWZmXX
I would like to be able to write <div class="inlined">content</div> or so, and have it transformed into some other html, using the content, and as defined by inlined. I would like to do this in pure CSS ie., no javascript.
For instance, that would be a function inlined:
<div data-descr="content"/> -> <div class="container"><div class="topright">content</div></div>
With some implementation "like":
div[data-descr] {
content: <div class="container"><div class="topright">attr(data-descr)</div></div>;
}
If no pure CSS can be used, what would be a less/sass/js solution?
Since your data attribute includes HTML, you can't use the CSS content property for this because your HTML will be treated as a string - it won't get parsed.
You should also avoid adding HTML to data attributes anyway.
You can do it with JavaScript, though, via replaceWith.
Try creating an object for that markup you want to add. You can then reference that to get the HTML
// helper function to handle new element creation
const createNewElement = content => {
const container = document.createElement("div");
container.classList.add("container");
const inner = document.createElement("div");
inner.classList.add("top-right");
inner.innerHTML = dataObject[content];
container.append(inner);
return container;
}
// create an object to get the HTML for a specific describtion attribute
const dataObject = {
content: `<div class="red">red text</div>`,
content2: `<div class="green">green text</div>`,
content3: `<div class="blue">blue text</div>`,
}
// get the elements you want to change
const targetElements = document.querySelectorAll("[data-descr]");
// loop through them and swap them
targetElements.forEach(element => {
const content = element.dataset.descr;
const newElement = createNewElement(content)
element.replaceWith(newElement)
})
.red {
color: red;
}
.green {
color: green;
}
.blue {
color: blue
}
<div data-descr="content"></div>
<div data-descr="content2"></div>
<div data-descr="content3"></div>
As stated by #ErikMartino in this answer: https://stackoverflow.com/a/5865996/8106583
content doesn't support HTML, only text. You should probably use
javascript, jQuery or something like that.
If content did support HTML you could end up in an infinite loop where
content is added inside content.
I'm writing a script that has access to a page's DOM, and I want to know what color would a particular link be if it had been visited. I'm aware that I cannot know whether the link has actually been visited, I don't care about that. I just want to know the color set for a:visited.
You can't get the :visited style with window.getComputedStyle according to the following MDN blog post because of privacy rules. So you can't programmatically tell if a user visited a link or not by checking the color.
But you can do it by walking through the stylesheets and search for a specific selector. Then if the selector is a match return the style.color property of that set style rule. This will not get the computed value but the value as set in the stylesheet.
const getStyleRuleColor = selector => {
for (const { rules } of document.styleSheets) {
for (const { style, selectorText } of rules) {
if (selectorText === selector) {
return style.color;
}
}
}
return null;
}
const selectorElement = 'a:visited';
const selectorId = '#someid:visited';
const elementVisitedColor = getStyleRuleColor(selectorElement);
const idVisitedColor = getStyleRuleColor(selectorId);
console.log(`${selectorElement} :`, elementVisitedColor);
console.log(`${selectorId} :`, idVisitedColor);
a {
color: black;
}
a:visited {
color: red;
}
#someid:visited {
color: green;
}
<a id="someid" href="http://someurl.com">I have a color</a>