admin管理员组文章数量:1178545
I have a DOM situation that looks like this:
A is an ancestor of B, which is in turn an ancestor of C
Initially, A has styles that inherit to B and C
I wish to temporarily highlight B by giving it a highlighted class
...however...
I want to "escape" the highlighting on C so it changes as little as possible
It seems this is not possible within the cascading paradigm of CSS. The only way to "un-apply" a style is to apply an overriding style. My problem is that the highlight code is in a plugin that wants to play well with an arbitrary page's existing CSS...my selectors are like this:
/* / */
.highlighted.class1 {
background-color: ...
background-image: ...
...
}
.highlighted.class2 {
...
}
/* ... */
.highlighted.classN {
...
}
Background is a tricky one...because although it is not an inherited CSS property, changing an ancestor's background can alter a descendant's appearance (if they have transparent backgrounds). So it's an example of something that causes the kind of disruption I'm trying to negate. :-/
Are there any instances of how people have suppressed the inheritance of changes in this fashion? Perhaps using tricks such as caching computed styles? I'm looking for any default technique that can be at least a little smarter than a function-level hook that says "hey, the plugin highlighted this node...do what you need to visually compensate."
UPDATE I have created a basic JsFiddle of this scenario to help make the discussion clearer:
/
I have a DOM situation that looks like this:
A is an ancestor of B, which is in turn an ancestor of C
Initially, A has styles that inherit to B and C
I wish to temporarily highlight B by giving it a highlighted class
...however...
I want to "escape" the highlighting on C so it changes as little as possible
It seems this is not possible within the cascading paradigm of CSS. The only way to "un-apply" a style is to apply an overriding style. My problem is that the highlight code is in a plugin that wants to play well with an arbitrary page's existing CSS...my selectors are like this:
/* http://www.maxdesign.com.au/articles/multiple-classes/ */
.highlighted.class1 {
background-color: ...
background-image: ...
...
}
.highlighted.class2 {
...
}
/* ... */
.highlighted.classN {
...
}
Background is a tricky one...because although it is not an inherited CSS property, changing an ancestor's background can alter a descendant's appearance (if they have transparent backgrounds). So it's an example of something that causes the kind of disruption I'm trying to negate. :-/
Are there any instances of how people have suppressed the inheritance of changes in this fashion? Perhaps using tricks such as caching computed styles? I'm looking for any default technique that can be at least a little smarter than a function-level hook that says "hey, the plugin highlighted this node...do what you need to visually compensate."
UPDATE I have created a basic JsFiddle of this scenario to help make the discussion clearer:
http://jsfiddle.net/HostileFork/7ku3g/
Share Improve this question edited May 23, 2017 at 10:24 CommunityBot 11 silver badge asked Nov 10, 2011 at 17:26 HostileFork says dont trust SEHostileFork says dont trust SE 33.6k13 gold badges102 silver badges173 bronze badges 15 | Show 10 more comments9 Answers
Reset to default 4 +150If I understand correctly, you want to cut a "hole" in the background of an ancestor.
The only way I can think of is using <canvas>
to cut the hole and then use the resulting image as the background of the ancestor.
Here is my code: http://jsfiddle.net/7ku3g/24/
I used the JS Fiddle logo as the watermark because you cannot retrieve contents of a canvas with cross-origin resources drawn to it.
Update: You would also want to listen to resize
events to update the background as the window size changes.
As thirtydot already pointed out, the background is not an inherited property but it rather just shines true as the child elements are transparent by default. So really the only way to make them mimic the behavior you want is to set them to the background(-image/color) that is either applied to parent (= div#a
) of the highlight element or even below that. In this simple example that is:
.highlight div {
background-color: white;
}
But in your real website this might be less easy to do.
"Background is a tricky one because it effectively acts inherited when it is not. But it's still an example of something that causes the kind of disruption I'm trying to negate. :-/"
Incorrect. A, being the highest level element (the oldest ancestor) has a background. When subsequent elements are descended from A (e.g. B and C), they have a background of "transparent", which makes them appear to have the inherited background (but is why your tiled backgrounds show up as contiguous).
We could, as proposed by @bozdoz use a specified value for the sub element in the situation that the highlight is applied, as such:
.highlight.bClass #c {
background-image: url('http://hostilefork.com/shared/stackoverflow/parchment.jpg');
background-repeat: repeat;
color: initial;
}
This would give the illusion of the original background peering through, and, given your example, appears nearly flawless to the naked eye. This however has the unfortunate side effect of being entirely non-dynamic. For an example, we might instead create a new class:
.preventHighlight {
background-image: url('http://hostilefork.com/shared/stackoverflow/parchment.jpg');
background-repeat: repeat;
color: initial;
}
And, in addition to this, add a second line to your javascript:
$('#toggleHighlight').click(function(){
$('#b').toggleClass('highlight');
$('#c').toggleClass('preventHighlight');
});
Not bad. But what if it were, for instance, #x
that we didn't want highlighted? I propose we give #toggleHighlight
a new attribute, name
. In the html, try:
<button id="toggleHighlight" name="x">Toggle Highlight</button>
Where name="x"
is set to the ID of the element to which we wish to apply the preventHighlight
class. Thus, we are able to write the function more akin to:
$('#toggleHighlight').click(function(){
$('#b').toggleClass('highlight');
$('#'+$('#toggleHighlight').attr('name')).toggleClass('preventHighlight');
});
You could continue this on, instead creating a function taking two parameters, such as tglHighlight(idToHighlight, idToPrevent) were you so inclined.
edit:
I appear to have misunderstood @bozdoz and done essentially the same thing, without noticing the visible shift in background. I would propose then a slight alternative which includes a correction for the offset of the background. This is dependent solely upon a minor change to my proposed javascript:
$('#toggleHighlight').click(function(){
$('#b').toggleClass('highlight');
$('#'+$('#toggleHighlight').attr('name')).toggleClass('preventHighlight');
$('.preventHighlight').css('background-position', function(index) {
var ch_pos = $('.preventHighlight').position();
var a_pos = $('#a').position();
var x = a_pos.left-ch_pos.left;
var y = a_pos.top-ch_pos.top;
console.log( x+"px "+y+"px" );
return x+"px "+y+"px";
});
});
I have a solution that helps with non-background related styles. You can see the fiddle http://jsfiddle.net/7ku3g/83/ which is only a roughed concept. Some key points: 1) it is imperative that the prepended styleControl
element is added to the inner most containing element. For example, you had the .highlight
class added to the .bClass
div but there was another full wrapping element, the blockquote
within that. 2) You would want to remove the styleControl
element upon removal of the highlight class.
In general, the idea is to add an invisible styling element first in the inner most wrapper, then use the general sibling selector ~
and :not
selector to style further down elements. Again, this does not solve your background issues, but does seem to be a way to help solve true inheriting properties since those styles are NOT applied to the wrapper div, but to the newly created first sibling in the group and the properties applied based off that sibling only to those they need to be applied to.
One issue with this technique is any "naked text" (see http://jsfiddle.net/7ku3g/84/) does not then get styled. So it is not a whole solution to your issue. EDIT: For such cases, grabbing and adding a carefully crafted span
around those naked nodes might work. Something similar to: Style a certain character in a string.
CSS
.highlight.bClass {
background-image: url('http://hostilefork.com/shared/stackoverflow/cc-by-nc-nd.png');
background-repeat: repeat;
}
.highlight.bClass .styleControl {
width: 0;
height: 0;
position: absolute;
}
.highlight.bClass .styleControl ~ *:not(.cClass) {
color: red;
}
UPDATE: Some further explanation based on HostileFork's comments. First, my solution here only matters if there is a child element that you want to prevent from inheriting styles (actual inheritable styles) you are adding. Second, multiple may need to be added depending on how deep things are nested. Here's some pseudocode html to discuss:
<div> (containing licensed material)
<childA1> (this is the wrapping blockquote in your example; could be anything)
<scriptAddedStyleElementB0>
<childB1>content<endchildB1> (apply styles)
<childB2>content<endchildB2> (don't apply styles)
<childB3> (don't apply styles because a child is not having styles applied)
<scriptAddedStyleElementC0>
<childC1> (apply styles)
<!-- noScriptAddedStyleElementD0 -->
<childD1>content<endchildD1>
<childD2>content<endchildD2>
<endchildC1>
<childC2>content<endchildC2> (don't apply styles)
<endchildB3>
<end-childA1>
<end-div1>
You are targeting the outermost div
to apply the highlight
class to but certain children you don't want affected. Note how A1 and B3 don't contain any content (text/images) themselves, they just contain other elements. Those are "wrappers" in my use of the term (they are just used to group their children, though they may have styling themselves).
To answer your question of "when the delving needs to stop," it is when you reach a point where you no longer care about styles inheriting. In the example above, 2 styling elements would need to be added, one at B0 and one at C0 but not at D0. This is because the general sibling selector ~
only applies to elements that have the same parent and that follow that sibling. So B0 is going to apply the styles to B1 but not B2 (non-licensed) or B3 (contains a non-licensed). Element C0 is going to apply the styles to C1 (and its licensed children D1 and D2) but avoid styling C2. The content of B2 and C2 would still be inheriting the original styles from the wrapping div (C2 by way of B3), while B1, C1, D1, D2 would all get the licensing styling.
The difficulties in this are: 1) earlier versions of IE (7 and 8) do not recognize some of this css, specifically the :not
selector. That might potentially be worked around. 2) my previous note about "naked text" or content. If B3 looked like:
<childB3>
<scriptAddedStyleElementC0>
Some unwrapped text
<childC1>...<endchildC1>
Some other unwrapped text and an <img> tag
<childC2>...<endchildC2>
More unwrapped text
<endchildB3>
If B3 unwrapped text was needing to be styled for licensing (turned red in your example) then you would need to locate all the text nodes via javascript and wrap them in span
elements to apply the styling, because if you apply the style to B3, then it will inherit to C2 which you don't want.
There is no doubt that what you are looking for is difficult to achieve. It would have been impossible via CSS prior to the general sibling selector coming about, and it may get easier with CSS4 standards that are being discussed (being able to style parent elements from children might be leveraged for this situation).
Manage all CSS changes in javascript so that they're easily reversible. As you said, it's not possible in pure CSS, but you can cache the styles via javascript.
In steps:
- define what kind of elements will be ignored inside the highlight
- check if you have a background-image in any of the parents
- if you do, save the image and that element's
offset()
- grab the descendant elements
- save their current CSS styles (before the highlight)
- apply the highlight styles to the target
- re-apply the saved styles for the descendants
- if there was a parent with a background image, calculate and offset the image accordingly
This keeps all styles for the inner elements intact, and accounts for background images that "fall through" transparent backgrounds.
Here's a test: http://jsfiddle.net/W4Yfm/2/
This leaves a messy sea of style attributes behind though, you might want to clean it up. There are probably unnacounted edge cases too.
To be most reliable you should use a technique like @Jan pointed out (draw the highlight on canvas), but you won't have support in IE < 9, and it's probably too slow for mobile.
You could add .highlight .cClass
to the top two css groups, and set color:initial
on .highlight .cClass
, like so:
.aClass, .highlight .cClass {
background-image: url('http://hostilefork.com/shared/stackoverflow/parchment.jpg');
background-repeat: repeat;
}
.aClass, .bClass, .cClass, .xClass, .highlight .cClass {
border : 2px solid #000;
margin: 20px;
padding: 20px;
}
.highlight .cClass {
color: initial;
}
The only problem, in this example, is that the background-position changes. But it's the best I've come up with.
Check it out: http://jsfiddle.net/t63m7/
This will work in your particular example case in jsFiddle. You would have to play around with background-position in your real code perhaps.
Trick in this case was to add the following CSS:
.highlight.bClass div {
background-image: url('http://hostilefork.com/shared/stackoverflow/parchment.jpg');
background-repeat: repeat;
background-position: -84px 0;
}
I would set the background of .cClass to be the same as .aClass with css, but If you want them to match up you will have to change the background position style as well.
Hmm, a lot of suggestions but my first thought which I have not tested would be to give the the same tags as whatever highlight you're doing to B to the C class and make it !important.
So:
.cClass div {
background-image: none !important;
}
本文标签:
版权声明:本文标题:javascript - Workaround for lack of CSS feature to "suppress inherited styles" (and backgrounds?) - Stack Over 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1738080191a2060279.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
background
is not an inheritable property. – thirtydot Commented Nov 10, 2011 at 17:40.class1
,.classN
, etc., are they classes that are intended to reflect classes from "arbitrary" pages' styles, or are they purely related to your plugin styling--that is, are you adding the class.highlighted
to some page class.class1
or are you adding both classes.highlighted
and.class1
to elements purely for your plugin purposes? – ScottS Commented Nov 11, 2011 at 2:20