CSS empty psuedo-class not working in nested DIV [duplicate] - javascript

This question already has answers here:
Is there a CSS parent selector?
(33 answers)
What does the "+" (plus sign) CSS selector mean?
(9 answers)
Closed 19 days ago.
If I have two DIVs where I apply CSS using :empty so that if one DIV has content, then the other's background will change color, it works if the DIVs aren't nested. In the fiddle, type anything into the top white box, the other will turn yellow.
http://jsfiddle.net/7fwL26ox/7/
<div id="inner-div" contenteditable=true></div>
<div id="outer-container">
</div>
#outer-container {
padding: 2em 0;
background-color: #CCC;
}
#inner-div{
border: 1px solid #000;
}
#inner-div:not(:empty) + #outer-container{
background-color: yellow;
}
If I nest the DIVs, where the DIV that is empty and changes content, is nested inside the DIV that changes color, it no longer works (input is outlined in the fiddle).
http://jsfiddle.net/vpcwtg7m/1/
<div id="outer-container">
<div id="inner-div" contenteditable=true></div>
</div>
#outer-container {
padding: 2em 0;
background-color: #CCC;
}
#inner-div{
border: 1px solid #000;
}
#inner-div:not(:empty) + #outer-container{
background-color: yellow;
}
I would have expected in the second fiddle, that the background will also change to yellow. Exact same CSS, just the placement of the DIVs has changed. Cheers!

The + in your CSS selector is an adjacent sibling combinator. It targets the next sibling after itself.
Your first example works because #outer-container is the next sibling:
<div id="inner-div" contenteditable=true></div>
<div id="outer-container">
When you rearrange your HTML the CSS is targeting a sibling that no longer exists:
<div id="outer-container">
<div id="inner-div" contenteditable=true></div>
<!-- CSS is looking for your outer-container div here, as the next sibling after inner-div -->
</div>
To style a parent based on the child, you'll have to use the new :has() pseudo class that is still gaining browser support:
#outer-container {
padding: 2em 0;
background-color: #CCC;
}
#inner-div {
border: 1px solid #000;
}
#outer-container:has(#inner-div:not(:empty)) {
background-color: yellow;
}
<div id="outer-container">
<div id="inner-div" contenteditable=true></div>
</div>

Related

Is there a way to select an element based on background color?

I want to change the background-color of a child of a div with a certain background color and was wondering if this could be done with CSS. I'll explain the case below.
something like:
.container[background-color=some color] .content {
background-color: some other color
}
My guess is that it can't be done because you would then be able to do something along the lines of:
.div {
background-color: some color
}
.div[background-color=some color] {
background-color: some other color
}
Which would create some kind of circularity where the div would be selected, set to the other color, then not be selected anymore and fall back on the original definition, but then be selected again because it has that original color.
I don't think the :has, :is and :where selectors work this way either but I was hoping there was some way this could be done while avoiding doing it in Javascript.
The Case
Divs are created dynamically based on errors in user input. These divs may or may not contain a certain type of error for which another div is needed
// simple errors
<div class="errors">
<p>Error 1</p>
<p>Error 2</p>
<p>Error 3</p>
<p>Error 4</p>
</div>
// more complex errors
<div class="errors">
<p>Error 1</p>
<div class="complex-error">
<p>Complex Error 1</p>
</div>
</div>
I give the errors div a background color with odd/even
.errors {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: auto;
border-radius: 4px;
margin: 8px 0px;
}
.errors:nth-child(odd) {
background-color: #dee2e6;
}
.errors:nth-child(even) {
background-color: #f8f9fa;
border: 2px solid #dee2e6;
}
I was wondering whether or not the elements with class errors could be selected based on background color to then select it's child.
I'll post my own solution as the answer.
The best way to do it without using JavaScript would be with classes. You can add the first background-color with a class that you add to the children that need to have that background color. Then, you change the background-color of only the children that have that class. And if you need to you can add and remove the class dynamically with JavaScript (as you are not providing an example of the whole app I don't fully understand the behaviour you are looking for).
div.black {
background-color: black;
}
div.white {
background-color: white;
}
div.black {
background-color: #5A5A5A;
}
This is the way you could do it. Separate the class out to style the background color then use the class selector .class1.class2 to apply the rule to only the parent. Use the descendent class combinator to apply the background colour to the child.
Here's an example
.parent {
border: 1px solid black;
padding: 1rem;
font-size: 1.5rem;
margin-top: 1rem;
}
.yellow-backgroud {
background-color: yellow;
}
.red-background {
background-color: red;
}
/* this selects all child divs where the parent has the parent class AND the yellow-background class */
.parent.yellow-background div {
background-color: green;
}
/* this selects all child divs where the parent has the parent class AND the red-background class */
.parent.red-background div {
background-color: orange;
}
<div class='parent yellow-background'>
Parent
<div>
This is a the child of the yellow background parent
</div>
</div>
<div class='parent red-background'>
Parent
<div>
This is a the child of the red background parent
</div>
</div>
<div class='parent'>
Parent
<div>
This is a the child with no parent background color set.
</div>
</div>
The odd/even selector can already be used to select those elements.
.errors:nth-child(odd) {
background-color: #dee2e6;
}
.errors:nth-child(odd) .complex-error {
background-color: #f8f9fa;
}
.errors:nth-child(even) {
background-color: #f8f9fa;
border: 2px solid #dee2e6;
}
.errors:nth-child(even) .complex-error {
background-color: #dee2e6;
}
There was no reason to select them after the fact when you can just select them at the place where they are defined.

How to guarantee an element will have a particular height

Say that I have this element on my page:
<div style="height: 1em;"> </div>
I want to use JavaScript to measure the height of the div to figure out how many px are equivalent to 1em for that element.
So if I did:
document.querySelector('div').getBoundingClientRect()
Then I might get 16.
But what if users can inject arbitrary styles onto this webpage? What if they do something like:
div { border: 1px solid black; }
Then I would get 18, because of the unexpected border applied to all div elements.
To avoid this, I could add a laundry list of styles to the div to remove potential "unexpected styles:"
<div style="border: 0; margin: 0; padding: 0; height: 1em;"> </div>
But is that list of styles comprehensive? If not, what other styles do I need? Or is there a better way to make this calculation?
Set the font-style: 1em !important; on the element, and get the font size in px using Window#getComputedStyle:
var fontSize = window.getComputedStyle(div).fontSize;
console.log(fontSize);
<div id="div" style="font-size: 1em;"></div>
My previous not bullet proof answer:
This fails if the user uses borders and/or paddings which height is greater than 16.
You can use box-sizing: border-box on the element. With this box sizing, the borders and the paddings don't increase the dimensions of the element. The content area is the original width/height minus any paddings and borders.
console.log(div.getBoundingClientRect().height);
div {
padding: 3px;
border: 2px solid black;
}
<div id="div" style="height: 1em; box-sizing: border-box;">

How to change sprite image when focus input form? [duplicate]

The plus sign selector (+) is for selecting the next adjacent sibling.
Is there an equivalent for the previous sibling?
No, there is no "previous sibling" selector.
On a related note, ~ is for general successor sibling (meaning the element comes after this one, but not necessarily immediately after) and is a CSS3 selector. + is for next sibling and is CSS2.1.
See Adjacent sibling combinator from Selectors Level 3 and 5.7 Adjacent sibling selectors from Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification.
I found a way to style all previous siblings (opposite of ~) that may work depending on what you need.
Let's say you have a list of links and when hovering on one, all the previous ones should turn red. You can do it like this:
/* default link color is blue */
.parent a {
color: blue;
}
/* prev siblings should be red */
.parent:hover a {
color: red;
}
.parent a:hover,
.parent a:hover ~ a {
color: blue;
}
<div class="parent">
link
link
link
link
link
</div>
Selectors level 4 proposes :has() (previously the subject indicator !) which will, one day, allow you to select a previous sibling with:
previous:has(+ next) {}
or (for a general previous sibling rather than adjacent one):
previous:has(~ next) {}
At the time of writing :has{} is supported by most but not all major browsers. Support is improving.
Over the years this answer has attracted dozens of "It's still not supported" comments (now deleted). Please don't add any more. There's a link to an regularly updated browser support chart in the answer.
Consider the order property of flex and grid layouts.
I'll focus on flexbox in the examples below, but the same concepts apply to Grid.
With flexbox, a previous sibling selector can be simulated.
In particular, the flex order property can move elements around the screen.
Here's an example:
You want element A to turn red when element B is hovered.
<ul>
<li>A</li>
<li>B</li>
</ul>
STEPS
Make the ul a flex container.
ul { display: flex; }
Reverse the order of siblings in the mark-up.
<ul>
<li>B</li>
<li>A</li>
</ul>
Use a sibling selector to target Element A (~ or + will do) .
li:hover + li { background-color: red; }
Use the flex order property to restore the order of siblings on the visual display.
li:last-child { order: -1; }
...and voilà! A previous sibling selector is born (or at least simulated).
Here's the full code:
ul {
display: flex;
}
li:hover + li {
background-color: red;
}
li:last-child {
order: -1;
}
/* non-essential decorative styles */
li {
height: 200px;
width: 200px;
background-color: aqua;
margin: 5px;
list-style-type: none;
cursor: pointer;
}
<ul>
<li>B</li>
<li>A</li>
</ul>
From the flexbox spec:
5.4. Display Order: the order property
Flex items are, by default, displayed and laid out in the same order as they appear in the source document. The
order property can be used to change this ordering.
The order property controls the order in which flex items appear within the flex container, by assigning them to ordinal groups. It takes a single <integer> value, which specifies which ordinal group the flex item
belongs to.
The initial order value for all flex items is 0.
Also see order in the CSS Grid Layout spec.
Examples of "previous sibling selectors" created with the flex order property.
.container { display: flex; }
.box5 { order: 1; }
.box5:hover + .box4 { background-color: orangered; font-size: 1.5em; }
.box6 { order: -4; }
.box7 { order: -3; }
.box8 { order: -2; }
.box9 { order: -1; }
.box9:hover ~ :not(.box12):nth-child(-1n+5) { background-color: orangered;
font-size: 1.5em; }
.box12 { order: 2; }
.box12:hover ~ :nth-last-child(-1n+2) { background-color: orangered;
font-size: 1.5em; }
.box21 { order: 1; }
.box21:hover ~ .box { background-color: orangered; font-size: 1.5em; }
/* non-essential decorative styles */
.container {
padding: 5px;
background-color: #888;
}
.box {
height: 50px;
width: 75px;
margin: 5px;
background-color: lightgreen;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
cursor: pointer;
}
<p>
Using the flex <code>order</code> property to construct a previous sibling selector
</p>
<div class="container">
<div class="box box1"><span>1</span></div>
<div class="box box2"><span>2</span></div>
<div class="box box3"><span>3</span></div>
<div class="box box5"><span>HOVER ME</span></div>
<div class="box box4"><span>4</span></div>
</div>
<br>
<div class="container">
<div class="box box9"><span>HOVER ME</span></div>
<div class="box box12"><span>HOVER ME</span></div>
<div class="box box6"><span>6</span></div>
<div class="box box7"><span>7</span></div>
<div class="box box8"><span>8</span></div>
<div class="box box10"><span>10</span></div>
<div class="box box11"><span>11</span></div>
</div>
<br>
<div class="container">
<div class="box box21"><span>HOVER ME</span></div>
<div class="box box13"><span>13</span></div>
<div class="box box14"><span>14</span></div>
<div class="box box15"><span>15</span></div>
<div class="box box16"><span>16</span></div>
<div class="box box17"><span>17</span></div>
<div class="box box18"><span>18</span></div>
<div class="box box19"><span>19</span></div>
<div class="box box20"><span>20</span></div>
</div>
jsFiddle
A Side Note – Two Outdated Beliefs about CSS
Flexbox is shattering long-held beliefs about CSS.
One such belief is that a previous sibling selector is not possible in CSS.
To say this belief is widespread would be an understatement. Here's a sampling of related questions on Stack Overflow alone:
Select the preceding sibling of an element in CSS using selectors
CSS: select previous sibling
CSS select previous sibling
Previous adjacent selector in CSS
Select previous siblings on hover
CSS selector to get preceding sibling
Change color of sibling elements on hover using CSS
How to select the previous sibling using selenium css syntax
CSS Selector for selecting an element that comes BEFORE another element?
How to add styling to active input's previous sibling using CSS only
CSS selector for next and previous elements
How to affect other elements when a div is hovered
As described above, this belief is not entirely true. A previous sibling selector can be simulated in CSS using the flex order property.
The z-index Myth
Another long-standing belief has been that z-index works only on positioned elements.
In fact, the most current version of the spec – the W3C Editor's Draft – still asserts this to be true:
9.9.1 Specifying the stack level: the z-index
property
z-index
Value: auto | | inherit
Initial: auto
Applies to: positioned elements
Inherited: no
Percentages: N/A
Media: visual
Computed value: as specified
(emphasis added)
In reality, however, this information is obsolete and inaccurate.
Elements that are flex items or grid items can create stacking contexts even when position is static.
4.3. Flex Item Z-Ordering
Flex items paint exactly the same as inline blocks, except that order-modified document order is used in place of raw
document order, and z-index values other than auto create a stacking context even if position is static.
5.4. Z-axis Ordering: the z-index property
The painting order of grid items is exactly the same as inline blocks, except that order-modified document order is
used in place of raw document order, and z-index values other than auto create a stacking context even if
position is static.
Here's a demonstration of z-index working on non-positioned flex items: https://jsfiddle.net/m0wddwxs/
I had the same question, but then I had a "duh" moment. Instead of writing
x ~ y
write
y ~ x
Obviously this matches "x" instead of "y", but it answers the "is there a match?" question, and simple DOM traversal may get you to the right element more efficiently than looping in javascript.
I realize that the original question was a CSS question so this answer is probably completely irrelevant, but other Javascript users may stumble on the question via search like I did.
There's not "previous selector", but you can use the combination of :not and ~ ("after selector"). No reverse order, no javascript.
.parent a{
color: blue
}
.parent a.active{
color: red
}
.parent a:not(.parent a.active ~ a){
color: red
}
<div class="parent">
link
link
link
link
link
</div>
I think my approach is more straight-forward than "style all divs, than remove styling for after divs", or using javascript, or using reverse order.
Three tricks:
basically, reversing the HTML order of your elements in HTML,
and using the ~ Next siblings operator:
1. Using CSS Flex and row-reverse
.reverse {
display: inline-flex;
flex-direction: row-reverse;
}
.reverse span:hover ~ span { /* On SPAN hover target its "previous" elements */
background:gold;
}
Hover a SPAN and see the previous elements being styled!<br>
<div class="reverse">
<!-- Reverse the order of inner elements -->
<span>5</span>
<span>4</span>
<span>3</span>
<span>2</span>
<span>1</span>
</div>
2. Using Flex with direction: RTL
.reverse {
display: inline-flex;
direction: rtl;
}
.reverse span:hover ~ span { /* On SPAN hover target its "previous" elements */
background: red;
}
Hover a SPAN and see the previous elements being styled!<br>
<div class="reverse">
<!-- Reverse the order of inner elements -->
<span>5</span>
<span>4</span>
<span>3</span>
<span>2</span>
<span>1</span>
</div>
3. Using float right
.reverse {
display: inline-block;
}
.reverse span{
float: right;
}
.reverse span:hover ~ span { /* On SPAN hover target its "previous" elements */
background: red;
}
Hover a SPAN and see the previous elements being styled!<br>
<div class="reverse">
<!-- Reverse the order of inner elements -->
<span>5</span>
<span>4</span>
<span>3</span>
<span>2</span>
<span>1</span>
</div>
+ is for the next sibling. Is there an equivalent for the previous
sibling?
You can use the two axe selectors: ! and ?
There are 2 subsequent sibling selectors in conventional CSS:
+ is the immediate subsequent sibling selector
~ is the any subsequent sibling selector
In conventional CSS, there is no previous sibling selector.
However, in the axe CSS post-processor library, there are 2 previous sibling selectors:
? is the immediate previous sibling selector (opposite of +)
! is the any previous sibling selector (opposite of ~)
Working Example:
In the example below:
.any-subsequent:hover ~ div selects any subsequent div
.immediate-subsequent:hover + div selects the immediate subsequent div
.any-previous:hover ! div selects any previous div
.immediate-previous:hover ? div selects the immediate previous div
div {
display: inline-block;
width: 60px;
height: 100px;
color: rgb(255, 255, 255);
background-color: rgb(255, 0, 0);
text-align: center;
vertical-align: top;
cursor: pointer;
opacity: 0;
transition: opacity 0.6s ease-out;
}
code {
display: block;
margin: 4px;
font-size: 24px;
line-height: 24px;
background-color: rgba(0, 0, 0, 0.5);
}
div:nth-of-type(-n+4) {
background-color: rgb(0, 0, 255);
}
div:nth-of-type(n+3):nth-of-type(-n+6) {
opacity: 1;
}
.any-subsequent:hover ~ div,
.immediate-subsequent:hover + div,
.any-previous:hover ! div,
.immediate-previous:hover ? div {
opacity: 1;
}
<h2>Hover over any of the blocks below</h2>
<div></div>
<div></div>
<div class="immediate-previous">Hover for <code>?</code> selector</div>
<div class="any-previous">Hover for <code>!</code> selector</div>
<div class="any-subsequent">Hover for <code>~</code> selector</div>
<div class="immediate-subsequent">Hover for <code>+</code> selector</div>
<div></div>
<div></div>
<script src="https://rouninmedia.github.io/axe/axe.js"></script>
Another flexbox solution
You can use inverse the order of elements in HTML. Then besides using order as in Michael_B's answer you can use flex-direction: row-reverse; or flex-direction: column-reverse; depending on your layout.
Working sample:
.flex {
display: flex;
flex-direction: row-reverse;
/* Align content at the "reversed" end i.e. beginning */
justify-content: flex-end;
}
/* On hover target its "previous" elements */
.flex-item:hover ~ .flex-item {
background-color: lime;
}
/* styles just for demo */
.flex-item {
background-color: orange;
color: white;
padding: 20px;
font-size: 3rem;
border-radius: 50%;
}
<div class="flex">
<div class="flex-item">5</div>
<div class="flex-item">4</div>
<div class="flex-item">3</div>
<div class="flex-item">2</div>
<div class="flex-item">1</div>
</div>
There is no official way to do that at the moment but you can use a little trick to achieve this ! Remember that it is experimental and it has some limitation ...
(check this link if you worries about navigator compatibility )
What you can do is use a CSS3 selector : the pseudo classe called nth-child()
#list>* {
display: inline-block;
padding: 20px 28px;
margin-right: 5px;
border: 1px solid #bbb;
background: #ddd;
color: #444;
margin: 0.4em 0;
}
#list :nth-child(-n+4) {
color: #600b90;
border: 1px dashed red;
background: orange;
}
<p>The oranges elements are the previous sibling li selected using li:nth-child(-n+4)</p>
<div id="list">
<span>1</span><!-- this will be selected -->
<p>2</p><!-- this will be selected -->
<p>3</p><!-- this will be selected -->
<div>4</div><!-- this will be selected -->
<div>5</div>
<p>6</p>
<p>7</p>
<p>8</p>
<p>9</p>
</div>
Limitations
You can't select previous elements based on the classes of the next elements
This is the same for pseudo classes
You could use double negation
SELECTOR:not([SELECTOR]FILTER):not([SELECTOR]FILTER + SELECTOR) { ... }
Replace SELECTOR with either the TAG or .CLASS ( Using #ID is probably too specific ).
Replace FILTER with some other :PSUEDO-SELECTOR (I've only tried :hover) or .CLASS (More for toggling through Javascript).
Since the typical usage will probably rely upon hovering (See example that follows)
/* Effect only limited when hovering */
TAG.CLASS:not(TAG.CLASS:hover):not(TAG.CLASS:hover + TAG.CLASS) {}
/* Effect only applied when hovering */
PARENT.CLASS:hover > CHILD.CLASS:not(CHILD.CLASS:hover):not(CHILD.CLASS:hover + CHILD.CLASS) {}
/* Solution */
div.parent:hover > div.child:not(:hover):not(:hover ~ .child) {
background-color:red;
border-radius:1.5em;
}
div.parent:hover > div.child:not(:hover):not(:hover ~ .child) > div {
background-color:yellow;
}
/* Make pretty (kinda) */
div.parent {
width:9em;
height:9em;
/* Layout */
display:grid;
grid-template-columns : auto auto auto;
grid-template-rows : auto auto auto;
}
div.child {
/* Dimensions */
height:3em;
width:3em;
/* Layout */
position:relative;
/* Cursor */
cursor: pointer;
/* Presentation */
border: 1px black solid;
border-radius:1.5em;
}
.star {
/* Dimensions */
width: 2.5em;
height: 2.5em;
/* Placement */
position:absolute;
top: 50%;
left: 50%;
transform:translate(-50%,-50%);
/* Geometry */
-webkit-clip-path: polygon(
50% 0%,
63% 38%,
100% 38%,
69% 59%,
82% 100%,
50% 75%,
18% 100%,
31% 59%,
0% 38%,
37% 38%
);
clip-path: polygon(
50% 0%,
63% 38%,
100% 38%,
69% 59%,
82% 100%,
50% 75%,
18% 100%,
31% 59%,
0% 38%,
37% 38%
);
/* Presentation */
background-color: lightgrey;
}
div.child:hover {
/* Presentation */
background-color:yellow;
border-radius:1.5em;
}
div.child:hover > div.star {
/* Presentation */
background-color:red;
}
<div class="parent">
<div class="child" href="#"><div class="star"></div></div>
<div class="child" href="#"><div class="star"></div></div>
<div class="child" href="#"><div class="star"></div></div>
<div class="child" href="#"><div class="star"></div></div>
<div class="child" href="#"><div class="star"></div></div>
<div class="child" href="#"><div class="star"></div></div>
<div class="child" href="#"><div class="star"></div></div>
<div class="child" href="#"><div class="star"></div></div>
<div class="child" href="#"><div class="star"></div></div>
</div>
Overriding the styles of next siblings on hover, so that it looks like only previous siblings have styles added on hover.
ul li {
color: red;
}
ul:hover li {
color: blue;
}
ul:hover li:hover ~ li{
color: red;
}
<ul>
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
</ul>
If you know the exact position an :nth-child()-based exclusion of all following siblings would work.
ul li:not(:nth-child(n+3))
Which would select all lis before the 3rd (e.g. 1st and 2nd). But, in my opinion this looks ugly and has a very tight usecase.
You also could select the nth-child right-to-left:
ul li:nth-child(-n+2)
Which does the same.
No. It is not possible via CSS. It takes the "Cascade" to heart ;-).
However, if you are able to add JavaScript to your page, a little bit of jQuery could get you to your end goal.
You can use jQuery's find to perform a "look-ahead" on your target element/class/id, then backtrack to select your target.
Then you use jQuery to re-write the DOM (CSS) for your element.
Based on this answer by Mike Brant,
the following jQuery snippet could help.
$('p + ul').prev('p')
This first selects all <ul>s that immediately follow a <p>.
Then it "backtracks" to select all the previous <p>s from that set of <ul>s.
Effectively, "previous sibling" has been selected via jQuery.
Now, use the .css function to pass in your CSS new values for that element.
In my case I was looking to find a way to select a DIV with the id #full-width, but ONLY if it had a (indirect) descendant DIV with the class of .companies.
I had control of all the HTML under .companies, but could not alter any of the HTML above it.
And the cascade goes only 1 direction: down.
Thus I could select ALL #full-widths.
Or I could select .companies that only followed a #full-width.
But I could not select only #full-widths that proceeded .companies.
And, again, I was unable to add .companies any higher up in the HTML. That part of the HTML was written externally, and wrapped our code.
But with jQuery, I can select the required #full-widths, then assign the appropriate style:
$("#full-width").find(".companies").parents("#full-width").css( "width", "300px" );
This finds all #full-width .companies, and selects just those .companies, similar to how selectors are used to target specific elements in standard in CSS.
Then it uses .parents to "backtrack" and select ALL parents of .companies,
but filters those results to keep only #fill-width elements, so that in the end,
it only selects a #full-width element if it has a .companies class descendant.
Finally, it assigns a new CSS (width) value to the resulting element.
$(".parent").find(".change-parent").parents(".parent").css( "background-color", "darkred");
div {
background-color: lightblue;
width: 120px;
height: 40px;
border: 1px solid gray;
padding: 5px;
}
.wrapper {
background-color: blue;
width: 250px;
height: 165px;
}
.parent {
background-color: green;
width: 200px;
height: 70px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<html>
<div class="wrapper">
<div class="parent">
"parent" turns red
<div class="change-parent">
descendant: "change-parent"
</div>
</div>
<div class="parent">
"parent" stays green
<div class="nope">
descendant: "nope"
</div>
</div>
</div>
Target <b>"<span style="color:darkgreen">parent</span>"</b> to turn <span style="color:red">red</span>.<br>
<b>Only</b> if it <b>has</b> a descendant of "change-parent".<br>
<br>
(reverse cascade, look ahead, parent un-descendant)
</html>
jQuery Reference Docs:
$() or jQuery(): DOM element.
.find: Get the descendants of each element in the current set of matched elements, filtered by a selector, jQuery object, or element.
.parents: Get the immediately preceding sibling of each element in the set of matched elements. If a selector is provided, it retrieves the previous sibling only if it matches that selector (filters the results to only include the listed elements/selectors).
.css: Set one or more CSS properties for the set of matched elements.
My requirement was to select currently hovered item's previous and next two siblings with the help of #Quentin 's answer I selected previous siblings.
.child{
width: 25px;
height: 25px;
}
.child:hover {
background:blue;
}
.child:has( + .child:hover) {
background: yellow;
}
.child:has(+ .child + .child:hover){
background:green;
}
.child:hover + .child {
background: red;
}
.child:hover + .child + .child {
background: magenta;
}
<ul class="parent">
<li class="child"></li>
<li class="child"></li>
<li class="child"></li>
<li class="child"></li>
<li class="child"></li>
<li class="child"></li>
</ul>
To select all previous siblings
.child {
width: 25px;
height: 25px;
}
.child:hover {
background: blue;
}
.child:has(~ .child:hover) {
background: red;
}
<ul class="parent">
<li class="child"></li>
<li class="child"></li>
<li class="child"></li>
<li class="child"></li>
<li class="child"></li>
<li class="child"></li>
</ul>
Depending on your exact objective, there is a way to achieve the usefulness of a parent selector without using one (even if one were to exist)...
Say we have:
<div>
<ul>
<li><a>Pants</a></li>
<li><a>Socks</a></li>
<ul>
<li><a>White socks</a></li>
<li><a>Blue socks</a></li>
</ul>
</ul>
</div>
What can we do to make the Socks block (including sock colours) stand out visually using spacing?
What would be nice but doesn't exist:
ul li ul:parent {
margin-top: 15px;
margin-bottom: 15px;
}
What does exist:
li > a {
margin-top: 15px;
display: block;
}
li > a:only-child {
margin-top: 0px;
}
This sets all anchor links to have 15px margin on the top and resets it back to 0 for those with no UL elements (or other tags) inside LIs.
/* Add a style to all the children, then undo the style to the target
and sibling children of your target. */
ul>li {
color: red;
}
ul>li.target,
ul>li.target~li {
color: inherit;
}
<ul>
<li>before</li>
<li class="target">target</li>
<li>after</li>
<li>after</li>
</ul>
There is no "previous" sibling selector unfortunately, but you can possibly still get the same effect by using positioning (e.g. float right). It depends on what you are trying to do.
In my case, I wanted a primarily CSS 5-star rating system. I would need to color (or swap the icon of) the previous stars. By floating each element right, I am essentially getting the same effect (the html for the stars thus must be written 'backwards').
I'm using FontAwesome in this example and swapping between the unicodes of fa-star-o and fa-star
http://fortawesome.github.io/Font-Awesome/
CSS:
.fa {
display: inline-block;
font-family: FontAwesome;
font-style: normal;
font-weight: normal;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* set all stars to 'empty star' */
.stars-container {
display: inline-block;
}
/* set all stars to 'empty star' */
.stars-container .star {
float: right;
display: inline-block;
padding: 2px;
color: orange;
cursor: pointer;
}
.stars-container .star:before {
content: "\f006"; /* fontAwesome empty star code */
}
/* set hovered star to 'filled star' */
.star:hover:before{
content: "\f005"; /* fontAwesome filled star code */
}
/* set all stars after hovered to'filled star'
** it will appear that it selects all after due to positioning */
.star:hover ~ .star:before {
content: "\f005"; /* fontAwesome filled star code */
}
HTML:
(40)
JSFiddle: http://jsfiddle.net/andrewleyva/88j0105g/
I needed a solution to select the previous sibling tr. I came up with this solution using React and Styled-components. This is not my exact solution (This is from memory, hours later). I know there is a flaw in the setHighlighterRow function.
OnMouseOver a row will set the row index to state, and rerender the previous row with a new background color
class ReactClass extends Component {
constructor() {
this.state = {
highlightRowIndex: null
}
}
setHighlightedRow = (index) => {
const highlightRowIndex = index === null ? null : index - 1;
this.setState({highlightRowIndex});
}
render() {
return (
<Table>
<Tbody>
{arr.map((row, index) => {
const isHighlighted = index === this.state.highlightRowIndex
return {
<Trow
isHighlighted={isHighlighted}
onMouseOver={() => this.setHighlightedRow(index)}
onMouseOut={() => this.setHighlightedRow(null)}
>
...
</Trow>
}
})}
</Tbody>
</Table>
)
}
}
const Trow = styled.tr`
& td {
background-color: ${p => p.isHighlighted ? 'red' : 'white'};
}
&:hover {
background-color: red;
}
`;
There isn't, and there is.
If you must place the label before the input, just place the label after the input and keep both the label & the input inside a div, and style the div as following :
.input-box {
display: flex;
flex-direction: column-reverse;
}
<div class="input-box">
<input
id="email"
class="form-item"
/>
<label for="email" class="form-item-header">
E-Mail*
</label>
</div>
Now you can apply the standard next sibling styling options available in css, and it will appear like you are using a previous sibling styling.
I've found the easiest solution. It might only apply based on what you're doing.
Let's say you want to hover on "sibling_2" to change "sibling_1" in the example below:
<div class='parent'>
<div class='sibling_1'></div>
<div class='sibling_2'></div>
</div>
Since there's no previous element selector you can simply switch 'sibling_1' and 'sibling_2' around and apply so they look the same.
.parent {
display: flex;
flex-direction: row-reverse;
}
Now you can select them like that.
.sibling_1:hover ~ .sibling_2 {
#your CSS
}
You can use :has() as following.
.thePrevious:has(+ .theNextSibling)
I used this for fixing overlapping bootstrap modals as follows. Any previous modals will be hidden if there are multiple.
.modal.show.modal--open:has(~ .modal.show.modal--open){
opacity: 0;
}
There is no such selector, but in the DOM API has a pretty read-only property
Node.previousSibling
I fixed this problem by putting my elements in a flexbox and then using flex-direction: column-reverse.
Then I had to invert my elements in the HTML manually (put them in reverse order), and it looked normal and it worked!
<div style="display: flex; flex-direction: column-reverse">
<a class="element2">Element 2</a>
<a class="element1">Element 1</a>
</div>
...
<style>
.element2:hover + element1 {
...
}
</style>
I had a similar problem and found out that all problem of this nature can be solved as follows:
give all your items a style.
give your selected item a style.
give next items a style using + or ~.
and this way you'll be able to style your current, previous items(all items overridden with current and next items) and your next items.
example:
/* all items (will be styled as previous) */
li {
color: blue;
}
/* the item i want to distinguish */
li.milk {
color: red;
}
/* next items */
li ~ li {
color: green;
}
<ul>
<li>Tea</li>
<li class="milk">Milk</li>
<li>Juice</li>
<li>others</li>
</ul>
Hope it helps someone.
here is the link for a similar question
CSS select all previous siblings for a star rating
So I post my solution using bits of everyones responses and anyone can use it as reference and possibliy recommend improvements.
// Just to check input value
// Consts
const starRadios = document.querySelectorAll('input[name="rating"]');
// EventListeners
starRadios.forEach((radio) => radio.addEventListener('change', getStarRadioValue));
// Get star radio value
function getStarRadioValue(event) {
alert(event.target.value)
// Do something with it
};
.star-rating {
font-size: 1.5rem;
unicode-bidi: bidi-override;
direction: rtl;
text-align: left;
}
.star-rating.editable label:hover {
cursor: pointer;
}
.star-rating.editable .icon-star:hover,
.star-rating.editable .icon-star:hover ~ .icon-star {
background-color: #fb2727 !important;
}
.icon-star {
position: relative;
background-color: #72747d;
width: 32px;
height: 32px;
display: inline-block;
transition: background-color 0.3s ease;
}
.icon-star.filled {
background-color: #fb2727;
}
.icon-star > label {
display: inline-block;
width: 100%;
height: 100%;
left: 0;
top: 0;
position: absolute;
}
.icon-star > label > input[type="radio"] {
position: absolute;
top: 0;
left: 0;
transform: translateY(50%) translateX(50%);
display: none;
}
<div class="star-rating editable">
<span class="icon-star">
<label>
<input type="radio" name="rating" value="5" />
</label>
</span>
<span class="icon-star">
<label>
<input type="radio" name="rating" value="4" />
</label>
</span>
<span class="icon-star">
<label>
<input type="radio" name="rating" value="3" />
</label>
</span>
<span class="icon-star">
<label>
<input type="radio" name="rating" value="2" />
</label>
</span>
<span class="icon-star">
<label>
<input type="radio" name="rating" value="1" />
</label>
</span>
</div>
For my use case was needed to change previous element style on focus and hover only having 2 items in parent element. to do so used :focus-within and :hover pseudo-classes.
like so selecting whenever focus/hover event occurs
.root-element:hover .element-to-style { background-color: red;}
.root-element:focus-within .element-to-style { background-color: green;}
<div class="root-element">
<span class="element-to-style"> TextFocused</span>
<input type="text" placeholder="type To Style"/>
</div>
There is actually no selector that can select the previous sibling in css. But it is possible to use certain tricks.
For example, if you want to change the style of the previous element when you hover over any element, you can use this:
<!DOCTYPE html>
<html lang="en">
<head>
<style>
.element:has(+ .next-element:hover){
/* here your style for .element */
}
</style>
</head>
<body>
<div class="container">
<div class="element"></div>
<div class="next-element"></div>
</div>
</body>
</html>
In this situation if you hover over .next-element the style of .element will change as you defined above
I had this same problem, while I was trying to change prepend icon fill color on input focus, my code looked something like this:
<template #append>
<b-input-group-text><strong class="text-danger">!</strong></b-input-group-text>
</template>
<b-form-input id="password_confirmation" v-model="form.password_confirmation" type="password" placeholder="Repeat password" autocomplete="new-password" />
The problem was that I'm using a vue-bootstrap slot to inject the prepend, so even if i change the location still get rendered after the input
Well my solution was to swipe their location, and add custom prepend and used ~ symbol, as css doesn't support previous sibling.
<div class="form-input-prepend">
<svg-vue icon="common.lock" />
</div>
<b-form-input id="password_confirmation" v-model="form.password_confirmation" type="password" placeholder="Repeat password" autocomplete="new-password" />
Scss style
.form-control:focus ~ .form-input-prepend {
svg path {
fill: $accent;
}
}
So just try to change its position, and if necessary use css order or position: absolute; to achieve what you want, and to avoid using javascript for this kind of needs.
Though there is no previous CSS selector. I've found a quick and easy method to select one yourself.
Here is the HTML markup:
<div class="parent">
<div class="child-1"></div>
<div class="child-2"></div>
</div>
In your JavaScript simply do:
document.querySelector(".child-2").parentElement.querySelector(".child-1")
This will first select the parent div and later on select the child-1 div from the child-2 div.
If you are using jQuery you can simply do:
$(".child-2").prev()

I need these buttons to control which div is showing or "on top"

I have these buttons on the side of my page, and a main content area taking up the better part of the page.
What I am trying to do is get the button I click to change the main content to a div containing the corresponding information. This is very hard to find, perhaps because I am searching by the wrong terms, and I have covered a good portion of stackoverflow without much luck.
I have though about absolutely positioning the divs and using a script to change the z-index of the the divs to the highest amount using a "=+1" type situation, but I could see that getting messy.
I have considered adapting a script I have that replaces part of an image file name in order to change a main picture on a page to a larger version of the image corresponding to a thumb name, though this script targets file names so it isn't going well.
I have also tried something along the lines of:
"id of button" onclick function = "main content class" change id to "corresponding div"
only in javascript talk, and this isn't working at all so I can only assume that I am either looking at it wrong or I have some messed up in the code.
$('#tabhead1').click(function() {
document.getElementByClassName("maintab").id = "tabs1";
});
This is driving me crazy and I would really appreciate some ideas. I tried to leave it free formed so that noone gets hung up on anyone solution.
**** Just to clarify, I have 5 divs id'd at #tabhead1, #tabhead2, #tabhead3, etc. and 5 content divs classed as .maintab, and id'd as tabs1, tabs2, tabs3, etc. I need the first content div to show automatically, and for that div to change based on the button clicked. at the moment all content divs are set to display: none; except the first one.
For each button, add a data attribute related to the corresponding <div>
for example
<button id="tabhead1" data-content="tabs1" >first Tab</button>
apply a common class for the tabs, for example .tab
Then you can do the following
$('button').click(function(){
var contentId = $(this).data('content'); // get the id of corresponding tab
$('.tab').hide(); // hide all tabs
$('#'+contentId).show(); //show the corresponding tab
});
You are using getElementbyClassName which does not exists. Use:
document.getElementsByClassName("maintab")[0].id = "tabs1";
// Get all elements to match classname + get first element from array
And for the rest, I don't know why you want to add id with JS? Why not just add them to your HTML?
Try this
$('#tabhead1').click(function() {
// get element with class 'maintab' and replace its content with that of another tab
$(".maintab").html($(".tabs1").html());
});
To expand a little on the demo I posted in the comments earlier:
This uses a method very similar to #tilwin-joy, so I guess we were of like mindedness. There are a couple of small differences that I would point out:
jQuery:
$('button').on('click', function () {
var button = $(this);
var target = button.data('target');
button.prop('disabled', true).siblings().prop('disabled', false);
$(target).show('slow').siblings().hide();
});
This uses siblings to hide the other content (one less pass at the DOM).
I suggest just setting your data value with the id hash in the markup, I think it's a bit clearer to read and follow (IMHO) in both the script and markup.
This script also sets the current button to be disabled when clicked. The benefit of this is that you can use the disabled property to style up your buttons, and even if you don't style them it gives a visual cue to the user as to which tab content is currently displayed. Check out the demo to see how this can be used for styling purposes.
HTML: (I stripped some of the unneeded ids from what you described as your markup).
<div class="tabhead">
<button data-target="#tabs1" disabled="true">Content 1</button>
<button data-target="#tabs2">Content 2</button>
<button data-target="#tabs3">Content 3</button>
<button data-target="#tabs4">Content 4</button>
<button data-target="#tabs5">Content 5</button>
</div>
<div class="maintab">
<div id="tabs1">
<img src="http://placehold.it/350/e8117f/fff&text=Image+1" alt="Image 1" />
<p>This is the content of tabs1.</p>
</div>
<div id="tabs2">
<img src="http://placehold.it/350/9acd32/fff&text=Image+2" alt="Image 2" />
<p>This is the content of tabs2.</p>
</div>
<div id="tabs3">
<img src="http://placehold.it/350/9400d3/fff&text=Image+3" alt="Image 3" />
<p>This is the content of tabs3.</p>
</div>
<div id="tabs4">
<img src="http://placehold.it/350/ffd700/fff&text=Image+4" alt="Image 4" />
<p>This is the content of tabs4.</p>
</div>
<div id="tabs5">
<img src="http://placehold.it/350/1e90ff/fff&text=Image+5" alt="Image 5" />
<p>This is the content of tabs5.</p>
</div>
</div>
CSS: Not needed - just to give you an idea of how you can style the elements to look like tabs.
/*This sets all but the first tab to hidden when the page is loaded*/
.maintab>div:not(:first-child) {
display: none;
}
/*The rest is just to style the elements to look like tabs*/
body {
background-color: #eaeaea;
}
.maintab, .tabhead {
text-align: center;
margin:0 20px;
font-family: sans-serif;
}
.maintab {
border: 1px solid #1e90ff;
border-top: none;
padding-top: 20px;
background-color: #fff;
}
.tabhead {
border-bottom: 1px solid #1e90ff;
position: relative;
margin-top: 20px;
}
button {
background-color: #ccc;
padding: 10px;
border: 1px solid #999;
border-bottom: none;
-webkit-border-top-left-radius: 4px;
-webkit-border-top-right-radius: 4px;
-moz-border-radius-topleft: 4px;
-moz-border-radius-topright: 4px;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
color: #999;
font-size: 14px;
cursor: pointer;
position: relative;
top: 2px;
}
button:disabled {
background-color: #fff;
border-color: #1e90ff;
color: #1e90ff;
top: 3px;
padding-top: 11px;
cursor: not-allowed;
z-index: 10;
}

How to apply :hover to an element

I want to apply the hover state remotely, by hovering on a different object. But I want to name the object that has its hover activated rather than have it be by DOM relationship to the item being hovered over.
<style>
img:hover {border: thin red solid;}
</style>
<li id="hover-over-me">Dogs</li>
<img src="dog.jpg" />
I haven't found a javascript or jquery method that allows you to apply the hover pseudo-class effect to an element remotely (ie independently of it actually being hovered). Is there a way to do this?
http://sandbox.phpcode.eu/g/3304b
<style>
img:hover,img.hovered {border: 5px red solid;}
</style>
<ul>
<li id="hover-over-me">Dogs</li>
</ul>
<img src="http://4.bp.blogspot.com/_7VyEDKFMA2U/TOX9ZL7KRPI/AAAAAAAAACM/-XSoYjePBPk/s1600/cute-puppy-dog-wallpapers.jpg" />
<script>
$("li").mouseenter(function(){
$("img").addClass('hovered');
});
$("li").mouseout(function(){
$("img").removeClass('hovered');
});
</script>
If you mean specifically the CSS :hover pseudo selector, then one element can only trigger it on another insofar as a relationship can be established in CSS:
http://jsfiddle.net/tFSWt/
example as siblings:
<span>Sibling </span><img src = "http://dummyimage.com/120x90/f00/fff.png&text=my+image" />
img:hover { border: 2px dashed blue; }
span:hover + img { border: 2px dashed blue; }
example as ancestor/descendant:
<span>Parent
<img src = "http://dummyimage.com/120x90/f00/fff.png&text=my+image" />
</span>
img:hover { border: 2px dashed blue; }
span:hover img { border: 2px dashed blue; }
Otherwise you'll need to rely on JavaScript to select the related element and set the style on either by inline styling, or by adding a class that provides the appropriate style.

Categories