For the following styles, how does one change the value of background color using javascript.
i)Internal Style sheet and/or ii) External Style sheet
I am using the card deck slide show from https://github.com/dynamicdriverepo/carddeckslideshow
div.stackcontainer > div.inner{
background: #D7F9FF; }
One way would be to use a CSS variable (Only available in the latest browsers)
div.stackcontainer > div.inner{ background: var(--inner-bg-color, #D7F9FF); }
Then you can use JS to set the value for --inner-bg-color anywhere from div.inner or above.
document.querySelector('div.stackcontainer').style.setProperty('--inner-bg-color', 'red')
But if you can't change their CSS then you need to adjust the style for that element:
var el = document.querySelector('div.stackcontainer > div.inner');
if (el) {
el.style.backgroundColor = '#FF0000';
}
Be aware that you are now messing with the specificity values that determine what CSS to use. And, if you want to reset to the original then you need to remove the style value from that element.
var el = document.querySelector('div.stackcontainer > div.inner');
if (el) {
el.style.backgroundColor = '';
}
Related
I have multiple divs that when clicked adds a border and scales them up a little. I am looping through all elements using foreach and on click i remove every element's border and scale property except the clicked element, to which i add a border and scale.
My code is completely logical and is supposed to work but for some reason i cant seem to grasp, it only applies the styles to clicked elements but not removing from the rest of the elements (like my code says it should).
JS
document.querySelectorAll('.projcolorpick div').forEach(el => {
el.onclick = (e) => {
el.style.border = "none"
el.style.transform = "scale(1)"
e.target.style.border = "2px solid #fff"
e.target.style.transform = "scale(1.2)"
projcolor = e.target.style.background
}
})
}
give something like this a try... each element needs an id attribute for this to work (the filter part - if there is a unique attribute...)
const list = Array.from(document.querySelectorAll('.projcolorpick div'));
list.forEach(el => {
el.addEventListener('click', (e) => {
//code that affects the element you click on
el.style.border = "2px solid #fff"
el.style.transform = "scale(1.2)"
projcolor = e.target.style.background;
list.filter(x=>x.id!=el.id).forEach(otherEl=>{
//code that affects the other elements you didn't click on
otherEl.style.border = "none"
otherEl.style.transform = "scale(1)"
});
});
});
```
edit:
fixed some typos.
forEach only applies to Arrays unless you configure it otherwise.
querySelectorAll does not return arrays, but array-like objects (NodeLists)
To allow looping over NodeLists, add the following code:
if (window.NodeList && !NodeList.prototype.forEach) {
NodeList.prototype.forEach = Array.prototype.forEach;
}
var nL = document.querySelectorAll('*');
console.log(nL instanceof NodeList); // true
You don't really need an id attribute on each div and I would advocate using class-assignments instead of changing their individual attributes. You can compare the actual DOM elements with each other like c==ev.target, as you can see in my code below:
// === populate the page first ... ============================= START =
const cont=document.getElementById('container');
cont.innerHTML=
[...Array(3)].map(cp=>'<div class="projcolorpick">'+
[...Array(8)].map(d=>{
let hsl= "hsl("+Math.floor(Math.random()*360)+",100%,80%)";
return ' <div style="background-color:'+hsl+'">'+hsl+'</div>'}).join('\n')
+'</div>').join('\n');
// === populate the page first ... =============================== END =
// now, do the action:
cont.onclick=ev=>{
if ( ev.target.parentNode.classList.contains('projcolorpick')
&& ev.target.tagName=='DIV'){
[...ev.target.parentNode.children].forEach(c=>c.classList.toggle('selected',c==ev.target));
ev.target.parentNode.style.backgroundColor=ev.target.textContent;
}
}
.projcolorpick {border: 2px solid #888}
.selected {border: 2px solid #fff; transform:scale(1.2);}
div {margin:6px; padding:4px}
.projcolorpick div {width:200px; height:20px}
<div id="container"></div>
The action happens here:
cont.onclick=ev=>{
if ( ev.target.parentNode.classList.contains('projcolorpick')
&& ev.target.tagName=='DIV'){
[...ev.target.parentNode.children].forEach(c=>c.classList.toggle('selected',c==ev.target));
ev.target.parentNode.style.backgroundColor=ev.target.textContent;
}
}
I use a delegated event-attachment to the parent .container div. The first if statements makes sure that only clicks on .projcolorpick>div elements are processed.
If you want to include more than one generation between them you need to use something like ev.target.closest('.projcolorpick') instead ...
Now, inside the if block two things happen:
Using toggle() on all DOM elements in ev.target.parentNode.children the class "selected" is either
assigned or
removed.
The text found in the clicked div is applied as background-color to the parent .projcolorpick container.
CSS
p:hover { ... }
div { ... }
.something { ... }
#content a:hover { ... }
HTML
<div id="content">
<p>
Test
</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>
Test non-link text
</p>
</div>
<p>another <span>paragraph</span>. A link that is not inside of content</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
There is a set of functions, the so-called "selectors api" https://developer.mozilla.org/en/docs/Web/API/Document/querySelector, https://developer.mozilla.org/en/docs/Web/API/Document/querySelectorAll and similar. But, nearly the only thing you can't do with these functions, is selecting pseudo classes (such as :hover).
I'm afraid you will have to monitor the mouseover and maybe mouseleave events and store the currently hovered element in a separate variable. By using its parentNode property (and its parent's parentNode property), you will have access to the parent chain.
I would suggest something like this:
var hoveredElement = null;
document.addEventListener( "mouseover", function(e){
hoveredElement = e.target;
});
I have managed to get a <ul> to switch display on and off with only a few lines of vanilla JavaScript code but I've run into an issue.
I gave the <li> that switches it on/off a :hover value (gray in this case). I'm keeping the same color on the <li> as the <ul> is collapsed. When the <ul> display is turned off though, I return it to the same value as it had had previously but the :hover value no longer works. Any solutions to this?
This is my JavaScript:
function expandIt(obj) {
obj.nextSibling.style.display = "block";
obj.style.backgroundColor = "gray";};
function reduceIt(obj) {
obj.style.display = "none";
obj.previousSibling.style.backgroundColor = "white";};
This is the HTML:
<ul>
<li onclick="expandIt(this)">ITEM</li>
<ul onclick="reduceIt(this)">
<li>subitem</li>
</ul>
</ul>
You could get this working with the !important modifier in your css. Something like this:
ul li:hover {
background-color:#ccc !important;
}
This overrides inline styles
hover versus inline style
The solution would be to add or a remove a class name with the styling you want. Due to css specificity, anything directly placed in style is going to take precedence over the defined rule inside of your css hover definition. The result is that your hover is never going to register the change of background color.
issues with siblings
Another issue with your code is that nextSibling and previousSibling are going to examine text nodes as well as elements. The trick there is going to be to ensure you are looking at an actual element and not a text node.
Something like this approach will ensure you end up with an element (.nodeType == 1)
function expandIt(obj) {
var next = obj.nextSibling;
while(next && next.nodeType != 1)next = next.nextSibling;
next.style.display = "block";
obj.style.backgroundColor = "gray";
};
function reduceIt(obj) {
obj.style.display = "none";
var prev = obj.previousSibling;
while(prev && prev.nodeType != 1)prev = prev.previousSibling;
prev.style.backgroundColor = "white";
};
Here is a demo of that concept: http://jsfiddle.net/Yu97H/
Now, even with that working example, it is possible to click so that gray is set as the background, but have hover broken. A simple rule such as ul li:hover { background-color: blue; } will exhibit that behavior.
Here is a demo of that behavior: http://jsfiddle.net/Yu97H/1/
I have simple JS function that toggles the DISPLAY of a DIV. The DIV display is set to 'none' by default. If I use an inline style to set the display, it works fine, but if I set the style in the head it only works after I run the function the second time. So it only sees that the display is set to none after the display is set in the function. It doesn't recognize that it is set in the CSS in the head. If I use an inline style, it works fine.
Also, if I change my conditional statement from:
if (OBJ.style.display == 'none')
to
if (OBJ.style.display = 'none')
Use window.getCurrentStyle or element.currentStyle in order to obtain style from the head or body. They're supported by different browsers so here's a cross-browser example:
function getStyle( elem, style ) {
var a = window.getComputedStyle,
b = elem.currentStyle;
if ( a ) return a( elem ).getPropertyValue( style );
else if ( b ) return b[ style ];
}
getStyle( document.getElementById('OBJ'), 'display' )
The style property of an element only contains the inline style, not inherited styles or styles from a style sheet.
You can use the offsetHeight property to check if the element has any size, as it is zero when the element is not visible.
if (OBJ.offsetHeight == 0) ...
Another suggestion is to use css classes:
css:
.hide{
display:none;
}
html:
<div id="mydiv" class="hide">hello world</div>
<button onclick="toggle();">toggle</button>
js:
function toggle(){
var obj = document.getElementById('mydiv');
if(obj.className == 'hide')
obj.className = '';
else
obj.className = 'hide'
}
http://jsfiddle.net/XBhMA/1/
Note this will only work if the element contains only one class. If it contains more, you will need to modify.
My function looks like that
$.fn.animateHighlight = function(highlightColor, originalColor, type, duration) {
var highlightBg = highlightColor || "#FFFF9C";
var animateMs = 500;
var animateVal1 = {};
animateVal1[type] = highlightColor;
var animateVal2 = {};
animateVal2[type] = originalColor;
this.stop().animate(animateVal1, animateMs).delay(duration).animate(animateVal2, animateMs);
};
Calling this like that
$("#variants").animateHighlight("red", "#9c9c9c", "borderColor", 3000);
The problem is,
This is default border color of fieldset
And this is after animation color
I know that animate adds extra style attribute to element. What I wanna do is, return back original fieldset border color (removing style attribute will return back original border color).
Tried to change last line of function to this
this.stop().animate(animateVal1, animateMs).delay(duration).animate(animateVal2, animateMs).removeAttribute('style');
Animate didn't even start.
How can I animate and reset back to original version after flashing?
this.stop().animate(animateVal1, animateMs).delay(duration).animate(animateVal2, animateMs, function() {
this.removeAttribute('style');
});
I think the two method for this.
1.If you used border color in stye attribute for VariantDiv.Default Styles will be lost in style when you remove style attribute. So you should hold first border color.
For Example :
http://jsfiddle.net/tEwa9/
2.if when you don't use style you can do this way.
You can call this code when animate completed.
$(this).attr('style','');
For example:
http://jsfiddle.net/xSYWS/