Inherit all css properties from parent div to child div - javascript

My html,
<div id="parent">
<div id="child">
</div>
</div>
css looks like,
#parent{
height:100px;
width:200px;
any-aother-property1:something;
any-aother-property2:something;
any-aother-property3:something;
}
Is there is any way to inherit all the properties to child at once , means can I do like,
$('#child').properties= $('#parent').properties

If you really want to dynamically inherit all CSS properties given to the parent at runtime, you can do the following.
Caution: This overrides the default value for all properties. Generally speaking, it is wise to assume that defaults are correct until proven otherwise. Some properties are inherit by default and some are not for various reasons. So you probably should only override specific properties that need to be changed.
#child {
all: inherit;
}

is this enough?
#parent, #parent > div {
/* your properties */
}

Copy inline:
$('#child').get(0).style = $('#parent').get(0).style;
But better if you find a CSS way as stated in the other answers.
UPDATE:
Get all styles:
$('#child').get(0).style = window.getComputedStyle($('#parent').get(0), null);

As far as I know there is no such a thing but you can always do something like this:
#parent, #child{
height:100px;
width:200px;
any-aother-property1:something;
any-aother-property2:something;
any-aother-property3:something;
}
Add both id's to have the same properties.

I have added a function in my library, custags.js which will help you doing that.
This is the extendcss() which requires the query selectors of the elements or the other methods to function.
You can do this with any type of elements, let it be equals or parent-child.
Here is the working demo:-
const parent = document.querySelector('.parent')
const child = document.querySelector('.child');
Ω('#button').on('click', ()=>{
Ω('document').extendcss(parent, child);
});
.parent{
background-color: teal;
color: yellow;
}
<script src="https://obnoxiousnerd.github.io/custags.js/custags.min.js"></script>
<h1 class = "parent">Parent</h1>
<h1 class = "child">Child</h1>
<button id="button">Give child some css</button>

Since children inherit most of their parent's styles by default, you can focus on clearing the child's styles rather than setting them equal to the parent's.
In Chrome and Opera, you can do so with one line of code:
$('#child')[0].style.all= 'unset';
This works whether child's CSS properties are in a style sheet or are created dynamically.
To clear dynamically-created CSS only, you can do this in all modern browsers:
$('#child')[0].style.cssText= '';
That will restore the style sheet properties.
The cross-browser solution to your problem may be the following:
var cs= getComputedStyle($('#child')[0]);
for (var i=0 ; i<cs.length; i++) {
$('#child').css(cs[i], 'inherit');
}
This iterates through all of child's styles, setting them to be inherited from the parent.
You can test each of these methods in different browsers at this Fiddle:
http://jsfiddle.net/9c3sy2eb/7/

Related

Change CSS direct with javascript

What is currently the best and easiest way to change CSS style using javascript?
I have several elements on the page with class="colorManipul" and in css .colorManipul{filter: grayscale(33%);}
I want to change the value directly in CSS so that it is reflected on all required elements with this class
Thanks for the advice, link, example, ... just anything
You can change the value of a CSS property with JS. To easily target a property you should target a CSS-Variable:
window.addEventListener('DOMContentLoaded', function() {
document.documentElement.style.setProperty('--body-bg', 'blue');
})
:root {
--body-bg: red;
}
body {
background: var(--body-bg);
}
However, this is harder to debug and a cleaner solution is to simply add a class to all elements with a certain class.
document.querySelectorAll('.colorManipul').forEach(el =>
el.classList.add('class-name')
);
If you want to change percentage of grayscale value, you can use css variable and change its value from JS For example:
CSS:
:root {
--grayscale: 33%;
}
.colorManipul {
filter: grayscale(var(--grayscale));
}
JS:
const grayscaleValue = 50;
root.style.setProperty('--grayscale', `${grayscaleValue}%`);

:host-context not working as expected in Lit-Element web component

I've got two Lit-element web components - one is units-list, which contains many units-list-item elements. The units-list-item elements have two different display modes: compact and detailed. Because the list element supports infinite scroll (and thus could contain several thousand units), we need any mechanism that toggles between the two modes to be as performant as possible.
That's why I thought an ideal solution would be to use the :host-context() pseudo-selector in the styles for the units-list-item element, as that way every units-list-item element could switch between the two display modes just by changing the class applied to an ancestor (which would be within the shadow DOM of the units-list element).
To elaborate, here's the relevant markup from the units-list element. Note that the "trigger" classes are being applied to the #list-contents div, which is part of the units-list template.
<div id="list-contents" class="${showDetails ? 'detail-view table' : 'compact-view table'}">
${units.map(unit => html`<units-list-item .unit="${unit}"></units-list-item>`)}
</div>
As you can see, the showDetails flag controls whether the "detail-view" or "compact-view" class is applied to the div containing all of the units-list-item elements. Those classes are definitely being applied correctly.
Here's the full render method from the units-list-item element (unnecessary markup removed):
render() {
const {unit} = this;
// the style token below injects the processed stylesheet contents into the template
return html`
${style}
<div class="row compact">
<!-- compact row markup here -->
</div>
<div class="row detail">
<!-- detail row markup here -->
</div>
`;
}
Then I have the following in the units-list-item element's styles (we're using SCSS, so the single-line comments are not a problem):
// This SHOULD hide the compact version of the row when the
// unit list has a "detail" class applied
:host-context(.detail-view) div.row.compact {
display: none !important;
}
// This SHOULD hide the detail version of the row when the
// unit list has a "compact" class applied
:host-context(.compact-view) div.row.detail {
display: none !important;
}
My understanding of the :host-context selector says that this should work, but Chrome just renders both versions of the row every time, and the Chrome dev tools show that the selectors are never matching with either of the rows.
I know there are several alternatives that would work, but this is the only one I'm aware of that would allow the entire list of units to switch modes by changing a single class on a parent element. Every other solution I've considered would require, at the least, updating the class attribute on every units-list-item element in the list. I'd like to avoid that if possible.
Of course, my primary concern is simply to make this work, if possible, but I'm also curious about a couple of things and can't find any info about them. The two questions I can't seem to find an answer for are
When :host-context is used within an element that is itself part of a shadow DOM, does it consider that parent element's shadow DOM to be the "host context", or does it jump "all the way out" to the document DOM?
If it's the former, will :host-context jump multiple shadow DOM boundaries? Say I have a custom page element that contains a custom list element, which itself contains many custom item elements. If that item element has a :host-context rule, will the browser first scan up the shadow DOM of the list element, then, if matching nothing, scan up the shadow DOM of the page element, and if still matching nothing, then scan up the main document DOM to the <html> tag?
There is no support for :host-context in FireFox or Safari
last update from a month ago is both Mozilla and Apple are not going to implement it.
Looks like it is going to be removed from the spec:
https://github.com/w3c/csswg-drafts/issues/1914
One alternative is to use CSS Properties (those trickle down into
shadowDOM)
JSFiddle: https://jsfiddle.net/WebComponents/hpd6yvxt/
using host-context for Chrome and Edge
using CSS properties for other Browsers
Update Feb 2022
Apple quietly changed their mind? now in Safari TP:
https://caniuse.com/?search=host-context
An example of using css porperties, as Danny Engelman says, to get your goal
customElements.define('list-item', class extends HTMLElement {
constructor() {
const style = document.createElement('style');
const divcompact = document.createElement('div');
divcompact.innerHTML = "compact";
divcompact.className = "compact";
const divdetail = document.createElement('div');
divdetail.innerHTML = "detail";
divdetail.className = "detail";
let shadow = super().attachShadow({
mode: 'open'
});
shadow.append(style, divcompact, divdetail);
style.innerHTML = `
.compact {
background-color: red;
display: var(--display-compact, block);
}
.detail {
background-color: green;
display: var(--display-detail, block);
}
`
}
});
.compact-view {
--display-detail: none;
}
.detail-view {
--display-compact: none;
}
.box {
width: 200px;
height: 50px;
border: solid 1px black;
margin: 5px;
}
<div class="box">
no class applied
<list-item>test</list-item>
</div>
<div class="compact-view box">
compact view
<list-item>test</list-item>
</div>
<div class="detail-view box">
detail view
<list-item>test</list-item>
</div>

document.body.style.marginTop returning blank string in JS

It was my understanding that [some elem].style.maginTop would return a string with the element's top margin.
Instead, I'm always getting a blank string. I want to use this with the body, but I also tried on a div, and that didn't work either.
console.log(document.body.style.marginTop); // logs ""
console.log(typeof(document.body.style.marginTop)); // logs "String"
var elem = document.getElementById("testDiv");
console.log(elem.style.marginTop); // logs ""
body {
margin-top:100px;
}
#testDiv {
margin-top:50px;
}
hi!
<div id="testDiv">test</div>
I'm not sure what I'm doing wrong... Does anybody have a non-jQuery solution to this?
The HTMLElement.style only returns inline styles:
The HTMLElement.style property returns a CSSStyleDeclaration object that represents the element's style attribute.
To access the styles from stylesheets use Window.getComputedStyle(element):
The Window.getComputedStyle() method gives the values of all the CSS properties of an element after applying the active stylesheets and resolving any basic computation those values may contain.
var elem = document.getElementById("testDiv");
var style = window.getComputedStyle(elem);
//output
document.body.innerHTML = style.marginTop;
body {
margin-top:100px;
}
#testDiv {
margin-top:50px;
}
hi!
<div id="testDiv">test</div>
You can use getComputedStyle and getPropertyValue to get top margin.
console.log(document.body.style.marginTop); // logs ""
console.log(typeof(document.body.style.marginTop)); // logs "String"
var elem = document.getElementById("testDiv");
//console.log(elem.style.marginTop); // logs ""
console.log(getComputedStyle(elem).getPropertyValue("margin-top"));
alert(getComputedStyle(elem).getPropertyValue("margin-top"));
body {
margin-top:100px;
}
#testDiv {
margin-top:50px;
}
hi!
<div id="testDiv">test</div>
Tested and working.
I don't know why, but I got it working by implicitly assigning the margin-top by JS first. I know why my answer works (it's been over 2 years so I better know why). By setting the values of div#test and body to 50px and
100px like this:
document.getElementById("testDiv").style.marginTop = '50px';
document.body.style.marginTop = '100px';
I'm actually setting the CSS property/value of the elements inline:
<body style='margin-top: 100px'>
<div id='testDiv' style='margin-top: 50px'>test</div>
</body>
Whenever the .style property is used, the CSS property/value that follows it is always inline. One important thing to remember about inline CSS styles is that they have a higher priority than the other 2 means of CSS Declaration: external stylesheets (ex. <link href="file.css"...) and inline stylesheet (ex. <style>...</style>). The only way to override an inline style is to use !important (unless of course the inline style has !important as well.)
So if the.style property is used to read a property/value of an element, it'll only return the inline style value if it actually exists which in OP's case it never did and in my case it did because I used .style to assign the property/values. While my solution is correct, the answers by Nicolo and Mr. Karlsson are better since you'll get the values from all CSS stylesheets.
document.getElementById("testDiv").style.marginTop = '50px';
document.body.style.marginTop = '100px';
console.log(document.getElementById("testDiv").style.marginTop);
console.log(document.body.style.marginTop);
body {
margin-top: 100px;
}
#testDiv {
margin-top: 50px;
}
hi!
<div id="testDiv">test</div>
Hm, I tried it as well with the same result as you did. A quick google search turned up Using JavaScript to read html / body tag margin-top which uses style.getPropertyValue() to return the information you're looking for.
instead of using core javascript, let you use js library jQuery. Then use this syntaxt to get its value.:
console.log($('body').css('margin-top'));
for pure javascript use this
var element = document.body,
style = window.getComputedStyle(element),
margin_top = style.getPropertyValue('margin-top');
console.log(margin_top);
http://jsfiddle.net/hAw53/726/

Stop inheritance of a specific CSS class

I have to modify an existing HTML5 app that has two different types of themes.
For example, I have something like this:
<body class="theme1 theme2">
<div id="div1">I'm happy with both themes</div>
<div id="div2">I just want theme 2</div>
</body>
This example is overly simplified. I know that I could just apply theme2 to div2. But the point is that those themes classes are in the body and I cannot "easily" change that.
I naively thought that I could just do something like this in JS:
getElementById('div2').classList.remove('theme1');
But this does not seem to work. I think that this only work on classes directly applied to the element. I would prefer a "pure CSS" solution anyway.
The info that I find seems related to preventing inheritance of specific properties. In my case, I want to prevent inheritance of any property under the theme1 class for div2 and all its children.
Properties are inherited, classes are not.
The only way to stop a property being inherited is to assign a value to that property for the given element.
The code doesn't work because 'theme1' is not a class of div 'div2'.
I don't know of a way to selectively remove the inheritance as you asked. The closest solution I can think of is to use nesting to create a more specific selector and override the undesired styles. Simple example:
.theme2 {
font-style: italic;
}
.theme2 #div2 {
font-style: normal;
}
This would "remove" the italic style from div2. You would have to redefine all the undesired styles. From a maintenance standpoint this may or may not be an acceptable solution. But it does solve the problem.
No, that's not how CSS works. If properties are getting applied to a descendent because of a class on an ancestor, and you cannot change that class or the properties, then you have to override the properties (via a more specific selector) that you don't want inherited.
e.g.
body.theme2 div { background: #000; }
#div2 { background: transparent; }
Let's start with some common.js:
//<![CDATA[
var doc = document, bod = doc.body, IE = parseFloat(navigator.appVersion.split('MSIE')[1]);
bod.className = 'js';
function gteIE(version, className){
if(IE >= version)bod.className = className;
}
function E(e){
return doc.getElementById(e);
}
function removeParentClassName(childId, removeClassName){
var cn = new RegExp('(\s+)?'+removeClassName, 'i'), pn = E(childId).parentNode;
pn.className = pn.className.replace(cn, '');
}
//]]>
Now on your current page:
removeParentClassName('div2', 'theme1');
One way you could do this with pure CSS would be to set up you CSS rules like so
.theme1 .div1,
.theme2 .div1,
.theme1 .div2,
.theme2 .div2 {font-style:normal;} /* apply theme1 and theme2 styles here */
.theme1.theme2 .div2 {font-style:italic;} /* apply theme2 only rules here */

Grouping changes of different element's styles

Is it possible in Javascript to set different element's styles at once, in such way that only one reflow is triggered? For example, is it possible to set at once the color style for different elements as in the below code snippet, in a way that just one reflow is triggered instead of three reflows?
document.getElementById("elem1").style.color = '#000';
document.getElementById("elem2").style.color = '#fff';
document.getElementById("elem3").style.color = '#abc';
I am familiar with techniques (as explained here) that minimize reflows/repaints such as using document fragments or using css classes instead of manipulating css styles through javascript, but I don't see how they can be applied on this case.
EDIT: the three elements on the example are siblings but there might exist, or not, other sibling elements between them, meaning that we cannot assume that they are defined necessarily by that order in the html structure. For example, its possible that we have a structure like this:
<div id="parent">
<div id="elem1">elem1</div>
<div id="elem2">elem2</div>
<div id="elem4">elem4</div>
<div id="elem3">elem3</div>
</div>
Much appreciated for any help!
Cheers
As far as I am aware the is no way to set the class of multiple elements at once. However, the browser may actually batch these changes for you anyway. Providing you don't read styles as well as writing them I believe this should hold true.
This article provides some insight into how reflow and repaint are triggered http://www.phpied.com/rendering-repaint-reflowrelayout-restyle/
You can prepare class like this :
.color1{
color : #000;
}
.color2{
color : #abc;
}
And set to your elements like this :
document.getElementById("elem1").className = document.getElementById("elem1").className + " color1";
document.getElementById("elem2").className = document.getElementById("elem2").className + " color2";
Depending on your element structure. For example assuming those elements are sibling DIVs, you can define CSS as:
div.myclass {
color:#000
}
div.myclass + div {
color:#fff
}
div.myclass + div + div {
color:#abc
}
Then a single JS command:
document.getElementById("elem1").className = "myclass";
Would set color for all 3: http://jsfiddle.net/PjZ77/1/
If it makes sense in your case, use css classes and swap the container class.
HTML structure could be :
<div id="container1">
<div id="elem1" class="clsA">A</div>
<div id="elem2" class="clsB">B</div>
<div id="elem3" class="clsC">C</div>
</div>
and in CSS:
#container1 .clsA { color: #000; }
#container1 .clsB { color: #111; }
#container1 .clsC { color: #222; }
#container1.mystate .clsA { color: #DDD; }
#container1.mystate .clsB { color: #EEE; }
#container1.mystate .clsC { color: #FFF; }
You can set document.getElementById("container1").className with mystate class (or empty class, or any class name that makes sense you defined in the css.
Class change occurs for only one element (the container), so the elem(n) child items will be refreshed at the same moment.

Categories

Resources