80

i have some code for example here in html

<html>
 <body>
  <img src='an image source'/>
  <h1>Hi it's test</h1>
  <div id='mydiv'>
    <img src='an image source'/>
    <h1>Hi it's test</h1>
  </div>
 </body>
</html>

if i used the following css code for styling it:

img{
   width:100px;
   height:100px;
}
h1{
   font-size:26px;
   color:red;
}

the question is : How can i prevent and isolate the tags inside the mydiv div tag from styling by the public tags style ?

Kara
  • 6,115
  • 16
  • 50
  • 57
medhatdawoud
  • 1,168
  • 2
  • 12
  • 18

5 Answers5

130

CSS Cascading and Inheritance Level 3 introduces the all shorthand property and the unset keyword, which, together, allow you to achieve this conveniently.

For example, if an author specifies all: initial on an element it will block all inheritance and reset all properties, as if no rules appeared in the author, user, or user-agent levels of the cascade.

This can be useful for the root element of a "widget" included in a page, which does not wish to inherit the styles of the outer page. Note, however, that any "default" style applied to that element (such as, e.g. display: block from the UA style sheet on block elements such as <div>) will also be blown away.

You’ll need to apply all: initial to your div and all: unset to its descendants:

#mydiv {
  all: initial; /* blocking inheritance for all properties */
}
#mydiv * {
  all: unset; /* allowing inheritance within #mydiv */
}

You may want to use a class on your div instead of an id, so that any rules you write to style its descendants won’t have to match or beat the high specificity used in this rule.

To be really safe, you may want to block styles on potential pseudo-element descendants too:

#mydiv::before,
#mydiv::after,
#mydiv *::before,
#mydiv *::after {
  all: unset;
}

Alternatively, for broader browser support, you can manually attempt to do what all does by setting all known CSS properties (don’t forget the prefixed versions):

#mydiv {
  /*
   * using initial for all properties
   * to totally block inheritance
   */
  align-content: initial;
  align-items: initial;
  align-self: initial;
  alignment-baseline: initial;
  animation: initial;
  backface-visibility: initial;
  background: initial;
  ...
}

#mydiv::before,
#mydiv::after,
#mydiv *,
#mydiv *::before,
#mydiv *::after {
  /*
   * using inherit for normally heritable properties,
   * and initial for the others, as unset does
   */
  align-content: initial;
  align-items: initial;
  align-self: initial;
  ...
  color: inherit;
  ...
}

You can encourage browser support for the all shorthand property and track its adoption with these issue links:

Up-to-date browser support information for the all shorthand property is available here.

BrunoLM
  • 97,872
  • 84
  • 296
  • 452
jaredjacobs
  • 5,625
  • 2
  • 27
  • 23
  • Note that it seems that with this technique pseudo-classes like `:hover` are not unset. You can't prevent stuff like this affecting your component : `button:hover { background-color: gray; }` – Thomas L'huillier Oct 07 '17 at 18:06
  • 1
    @ThomasL'huillier That's incorrect: The rule with selector `#mydiv *` beats (has higher specificity) than a `button:hover` rule. If you are not seeing this, then you are probably using lower-specificity selectors for your blocking rules than recommended above (maybe `.mydiv *` instead of `#mydiv *`?). If using an ID selector is not a viable option for you, you can repeat your class selector as many times as may be required to reach sure-win specificity for your page: `.mydiv.mydiv.mydiv.mydiv *`. Hope this helps. – jaredjacobs Oct 09 '17 at 00:46
  • @Zardo, see the paragraph beginning with "Alternatively, for broader browser support...". That technique works in IE/Edge. – jaredjacobs Oct 09 '17 at 00:48
  • 2
    Another thing to note is that if there is `!important` anywhere in the parent hierarchy, the `all` property won't help. – akshay_kashain Aug 05 '18 at 18:15
  • I tried using `all: initial` and it had some weird side effects. All of the divs within `#mydiv` will have a `display` value of `inline` (instead of `block` as one may expect) unless it is explicitly overridden. And this appears to be [expected behavior](https://stackoverflow.com/a/8229026/28324). – Elias Zamaria May 01 '19 at 22:06
19

Old question, but things have changed a little bit since the accepted answer. There is now a CSSWG-recommended keyword called revert which would be better suited than initial and unset to solve this problem, as it resets properties to what they're defined to in the user agent stylesheet, rather than to their initial value (which has no regard for which element they're used on). So for instance, with revert, a div inside #mydiv will have its display set to block as we would expect and not inline (the initial value of display).

You'd have to do this:

#mydiv,
#mydiv::before,
#mydiv::after,
#mydiv *
#mydiv *::before,
#mydiv *::after {
  all: revert;
}

At the time of writing, revert is supported in Edge, Firefox, Chrome, and Safari, but not IE or Opera.

There is also something else to take into consideration regarding the accepted answer. If you want to style anything inside #mydiv, you need to do so with a selector that is at least as specific as the one you used to unset or revert everything, otherwise it will be overridden by that rule, even if it comes after it in the CSS.

So you'd need to do something like this (note the #mydiv which boost the specificity of the rules):

#mydiv p {
  margin-top: 20px;
}

#mydiv .bg-black {
  background-color: black;
}

// etc.
luiscla27
  • 4,956
  • 37
  • 49
benface
  • 687
  • 9
  • 19
  • This will be the best solution, once available. [Firefox](https://bugzilla.mozilla.org/show_bug.cgi?id=1215878#c19) now has a draft patch being considered. You can vote the feature up. – Well Actually Mar 03 '19 at 21:20
  • Extremely well-written answer. – qxotk Dec 30 '20 at 15:31
10

You cannot technically, that's how CSS is suppose to work. If there is any style defined for div tag in your style sheet it will be applied to all div elements.

Few things that you can try is don't style with tag name instead give class name and give style declaration to class. so that you can make sure where all styles will go.

OR. if you want some specific Div tag to not have the style while other Divs to have. you can always reset it give some different class name or id and reset the style declarations

Kamal
  • 1,122
  • 11
  • 18
4

iframe is also a decent solution for isolation of styles if it doesn't complicate other business logic. Plus this can be done in pure JavaScript and may work in older browsers too.

const HTMLToIsolate = `...` // get your div tag as HTML string
const parentContainerRef = document.body; // or something else
const newIframe = document.createElement('iframe');
// set height & width to 100% and remove borders for newIframe

parentContainerRef.appendChild(newIframe)
newIframe.contentWindow.document.open('text/html', 'replace');
newIframe.contentWindow.document.write(HTMLToIsolate);
newIframe.contentWindow.document.close();

// bonus to remove scroll bars by setting proper iframe's height
newIframe.addEventListener('load', function onIFrameLoad(e) {
    const iframeBody = this.contentDocument.querySelector("body");
    this.style.height = `${iframeBody.scrollHeight}px`;
});

This worked perfectly for me when I had to embed a complex HTML file into my webpage fetched remotely without MixedContent warning and without parent HTML styles overriding the embedded HTML. Thanks to https://benfrain.com/sandbox-local-htmlcss-code-snippets-inside-iframe-style-guidespattern-libraries/ for this excellent idea!

Though #mydiv * { all: unset } CSS trick, as suggested in the accepted solution here works, it ended being a complex browser operation as there were many DOM nodes on my page.

Nitin
  • 7,187
  • 6
  • 31
  • 36
2

One thing that might be helpful is the CSS direct child selector, which is available in all browsers including IE7+. That lets you apply styling that doesn't cascade down into children. For example in your code you could use this CSS:

body > img {
  width:100px;
  height:100px;
}
body > h1 {
  font-size:26px;
  color:red;
}

And that CSS would only apply to elements directly on the BODY element.

jpsimons
  • 27,382
  • 3
  • 35
  • 45
  • Nice point, answer could improve if it used the OP's
    tag in the example, rather than . E.g. #mydiv > img { ...} #mydiv > h1 {...}
    – qxotk Dec 30 '20 at 15:33