Target :last-child with mixed class siblings? - css

How do I target the last child of a class when there are siblings with another class after it? It would be nice to have a :last-sibling and :last-sibling-of-type.
FIDDLE
<div class="grid">
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div> /* How do I target this if its nth position is unknowable? */
<div class="grid-orphan"></div>
<div class="grid-orphan"></div>
<div class="grid-orphan"></div>
</div>

If you need to get the last .grid-item element out of the document, regardless of what it's inside, then you can't do that in CSS. In CSS, you can only select the first, last, or nth element at one particular level of the hierarchy, you can't select the last element of some type regardless of what it's nested in.
Here is one way to get the last div inside of .grid
.grid div:last-of-type
Here's another way to get the last child of some outer div:
div :last-child
BUT The thing you probably need is some js:
You could do this the jquery approach (like below), or just do getElementsByClassNameand then set the last element in the list in the same manner.
function getlastchild() {
var items = $(".grid-item");
items.last().css("background-color", "red");
//OR even as one-liner: $(".grid-item").last().css("background-color", "red");
}
.grid {
width: 200px;
}
.grid div:last-of-type {
color: white;
}
div:last-child {
background-color: grey;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="grid">
<div class="grid-item">1</div>
<div class="grid-item">2</div>
<div class="grid-item">3</div>
<div class="grid-item">4</div>
<div class="grid-orphan">5</div>
<div class="grid-orphan">6</div>
<div class="grid-orphan">7</div>
</div>
<button onclick="getlastchild()">Press me to get last grid-item</button>

I see 4 possibilities here:
Case 1:
You already know the number of grid-orphan items, so you can use nth-last-child.
.grid-item:nth-last-child(4) {
background: blue;
}
Case 2:
You are an adventurer and this is not for production: use the newest version of nth-child and nth-last-child:
.grid-item:nth-last-child(1 of .grid-item) {
background: blue;
}
However, it only works on Safari for now (12.99%).
See specs
Case 3:
Use JavaScript.
const items = document.querySelectorAll('.grid-item')
const selectedItem = [].slice.call(items).pop();
selectedItem.style.background = 'blue';
Case 4:
Just add an extra class.
<div class="grid-item grid-item--special"></div>
.grid-item.grid-item--special {
background: blue;
}

You can use either :nth-last-child or :nth-last-of-type if you are able to put all your .grid-orphan elements inside another div.
HTML
<div class="grid">
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-orphans">
<div class="grid-orphan"></div>
<div class="grid-orphan"></div>
<div class="grid-orphan"></div>
</div>
</div>
SCSS
.grid {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
.grid-item {
background: gray;
border: 1px solid white;
position: relative;
display: flex;
flex-direction: column;
height: 100px;
width: calc(25% - 20px);
min-width: 360px;
flex-grow: 2;
justify-content: flex-start;
cursor: pointer;
&:nth-last-child(2) {
background: navy;
}
}
.grid-orphan {
height: 0;
border: 0;
display: flex;
flex-direction: column;
width: calc(25% - 20px);
min-width: 360px;
flex-grow: 2;
justify-content: flex-start;
cursor: pointer;
}
}
Jsfiddle available here.
Otherwise, a CSS-only approach would be limited to only Apple's browsers, that is Safari desktop and on iOS, since they are the only ones that implement the selector list argument, which allows you to narrow down the elements by a class selector. For seeing this just change &:nth-last-child(2) with &:nth-last-child(1 of .grid-item)

All those answers are great. Thank you. I ended up just making the orphans <span>'s and then .grid-item:last-child worked fine. I knew I could do that but was wondering if it could be done (easily) when the orphans were the same element type as the grid items.

Related

Exclude a class and all of its children

I am loading global CSS styles but I need them to not affect one part of the page and all of its subcomponents. There are some old information but is there a solution now when :not() is a Level 4 selector?
Codepen example that is not working: https://codepen.io/LaCertosus/pen/PoaYeRj
I have a HTML structure that is not predefined, I do not know how many and what elements are around and inside the "red" element.
Visual example
<div class="parent">
<div class="_filler">
<div class="_filler">
<div class="block">
Should be red
</div>
</div>
</div>
<div>
<div class="child-lime">
<div class="block">
Should be lime
</div>
<div class="_filler">
<div class="block">
Should be lime
</div>
</div>
</div>
</div>
</div>
.parent:not(.child-lime) {
.block {
background: red;
}
}
/* Block is an example, in reality we don't know the class name */
.block {
height: 100px;
width: 100px;
background: lime;
border: 1px solid #000;
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
}
I have tried different combinations with :not() selector but with no luck. It works when I don't need to include all children.
add this into CSS file
.parent > ._filler .block {
background: red;
}
.child-lime .block {
background: lime;
}
remove this from CSS file
.parent:not(.child-lime) {
.block {
background: red;
}
}
Your question seems to be missing some details, but here's what gets you close (assuming you can't touch the underlying HTML)
.parent {
div:not(.child-lime .block) {
background: red;
}
.block {...}
}
There's an un-classed div element that turns red...but since your comments seem to require not touching the underlying HTML and using the :not pseudo, that's probably as close as you can get.

CSS selector to target element where sibling does not have a specific class

I'm working on a slider that has previous button, content and next button as sibling nodes. I'm hiding disabled buttons and I need to adjust the margin for the content
I'm having difficulties with the selectors. I want to target the container to adjust the margin based on if the button is disabled or not. My initial idea was to do this with flex but it's an old
I've been trying something like
.content {
&:not(+ .button-disabled) {
margin-left: 50px;
}
}
but it seems I'm not allowed to have a +inside :not(). Is there any other way I can target this?
You can define a class to the parent container of the three siblings, like
<div class="disabled-button">
<div class="prev"></div>
<div class="cont"></div>
<div class="next"></div>
</div>
And define CSS for them, like:
.wrapper {
width: 80%;
height: 30px;
display: inline-flex;
}
.wrapper > div {
width: 33%;
height: 100%;
border: 1px solid black;
background-color: green;
}
.wrapper.disabled-button .prev {
margin-left: 50px;
}
<div class="disabled-button wrapper">
<div class="prev"></div>
<div class="cont"></div>
<div class="next"></div>
</div>
<div class="wrapper">
<div class="prev"></div>
<div class="cont"></div>
<div class="next"></div>
</div>

Multiple siblings, place some in same row & fill extra space

I have no control of the html. I have a parent with multiple children.Only some of them must be in the same row, while the rest of them stay unaffected and one of them must take up all the extra space. Content is auto generated and % is not an option.
Other options except inline to place on the same row to avoid the problem are welcome as well.
.parent {
background: red;
}
.same-row-child {
background: green;
display: inline-flex;
}
<div class="parent">
<div class="other-child">A</div>
<div class="same-row-child">B</div>
<div class="same-row-child">C</div>
</div>
To sum up: Α in the first line unaffected.
B+C in the same line with B taking up all the extra space.
If the idea is to use flex, then it should be the parent the flex box:
https://css-tricks.com/snippets/css/a-guide-to-flexbox/
display:flex; display:inline-flex; It enables a flex context for all its direct children.
.parent {
background: red;
display: flex;
flex-wrap: wrap;
}
.other-child {
width: 100%;
}
.same-row-child {
background: green;
}
.parent :last-child {
flex: 1;
margin-left:2px;
}
<div class="parent">
<div class="other-child">A</div>
<div class="same-row-child">B</div>
<div class="same-row-child">C</div>
</div>
looks like not the option you would use See next option
The oldish way is float and overflow, and the one to float is the one that comes first and is supposed to shrink on itself.
see https://css-tricks.com/all-about-floats/
Aside from the simple example of wrapping text around images, floats can be used to create entire web layouts.
.parent {
background: red;
}
.other-child {}
.same-row-child {
float: left;
background: green;
margin-right: 2px;
}
.parent :last-child {
float: none;
overflow: hidden;
margin: 0;
}
<div class="parent">
<div class="other-child">A</div>
<div class="same-row-child">B</div>
<div class="same-row-child">C</div>
</div>

Is there a way of writing CSS that lets you re-organize the order of how HTML components appear?

I am working on a responsive site in which the mobile/tablet view differs from the desktop view in the way it re-orders the DIVs.
Is there a way to write maintainable CSS that let's you re-organize the order of how HTML DIVs appear?
For example, the code below controls the order of how DIVs would appear on a desktop device:
<div class="container">
<div class="row1">
<div class="col1A">Sample content</div>
<div class="col2A">Sample content</div>
<div class="col3A">Sample content</div>
</div>
<div class="row2">
<div class="col1B">Sample content</div>
<div class="col2B">Sample content</div>
<div class="col3B">Sample content</div>
</div>
</div>
However, for mobile/tablet view, I want to display the DIVs in different order using CSS, like the example below:
Show row2, col2B
Then row1, col1A
Then row1, col3A
Then row2, col1B
Is this possible using CSS ?
As a proof-of-concept, you can use the flex CSS property to reorder how elements are visually rendered.
In your example, I had to keep the child elements within a single container
and then I could control the order using the order property.
If you want to hide some items on the small screen view, use display: none on the specific items.
Note: For a wide screen, you would need some CSS rules to get the items to look like two rows. (Please specify what you need.)
If you combine this with media queries, you can get a workable solution.
.container {
display: flex;
flex: center;
flex-direction: column;
align-items: center;
border: 1px dotted blue;
}
.container div {
text-align: center;
padding: 10px;
border: 1px dotted gray;
width: auto;
}
.col1A {
order: 2;
}
.col2A {
display: none;
}
.col3A {
order: 3;
}
.col1B {
order: 4;
}
.col2B {
order: 1;
}
.col3B {
display: none;
}
<div class="container">
<div class="row1 col1A">Sample content 1A</div>
<div class="row1 col2A">Sample content 2A</div>
<div class="row1 col3A">Sample content 3A</div>
<div class="row2 col1B">Sample content 1B</div>
<div class="row2 col2B">Sample content 2B</div>
<div class="row2 col3B">Sample content 3B</div>
</div>
If you want to simulate two rows of three elements, you can still use flex with some adjustments. The following may be helpful.
.container {
display: flex;
flex: center;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
border: 1px dotted blue;
padding: 20px 0;
}
.container .row1 {
margin-bottom: 20px;
}
.container div {
text-align: center;
padding: 10px;
border: 1px dotted gray;
flex-basis: calc(33% - 20px);
}
.col1B {
background-color: yellow;
}
<div class="container">
<div class="row1 col1A">Sample content 1A</div>
<div class="row1 col2A">Sample content 2A</div>
<div class="row1 col3A">Sample content 3A</div>
<div class="row2 col1B">Sample content 1B</div>
<div class="row2 col2B">Sample content 2B</div>
<div class="row2 col3B">Sample content 3B</div>
</div>
Set a screen size for mobile device detection in the css and add the following
#media screen and (max-width: SIZE) {
.row2{
display: flex; flex-flow: column;
}
.col1B{
order: 1;
}
.col2B{
order: 2;
}
.col3B{
order: 3;
}
}
And then add the classes to the DIVs
<div class="row2">
<div class="col1B">Sample content</div>
<div class="col2B">Sample content</div>
<div class="col3B">Sample content</div>
</div>
Change order: 1/2/3; to your needs.

Can 2 columns of nested divs be merged into a single column and then re-ordered using CSS?

So, I'm pretty certain it's not possible to do the following, but thought I'd ask anyway!
I've got 2 columns of content on my website design. Both have a wrapper div, floated, so they sit side by side. This looks fine on the desktop layout, and on the mobile (responsive) layout they currently both fill out to 100% width, and stack on top of each other.
What I'd really like to do is change the order of the nested divs inside each floated wrapper on the mobile layout so that, essentially, the two columns merge into one single column and the nested divs ordering changes as below:
DESKTOP
1 5
2 6
3 7
4 8
MOBILE
1
5
2
3
6
7
8
4
Hope this is clear enough! I know I can use flexboxes to change the order on the mobile layout, but as far as I can get is to change the order only within each individual wrapper div. Have also tried floating the nested divs in various ways on the desktop layout, but to no avail.
EDIT:
Apologies, I should have pasted my code, either:
<div id="container">
<div class="wrapper-left">
<div class="divInside div1">1</div>
<div class="divInside div2">2</div>
<div class="divInside div3">3</div>
<div class="divInside div4">4</div>
</div>
<div class="wrapper-right">
<div class="divInside div5">5</div>
<div class="divInside div6">6</div>
<div class="divInside div7">7</div>
<div class="divInside div8">8</div>
</div>
</div>
or
<div id="container">
<div class="wrapper">
<div class="divInside div1">1</div>
<div class="divInside div2">2</div>
<div class="divInside div3">3</div>
<div class="divInside div4">4</div>
<div class="divInside div5">5</div>
<div class="divInside div6">6</div>
<div class="divInside div7">7</div>
<div class="divInside div8">8</div>
</div>
</div>
Also here's an image to illustrate what I'm after. Sincere apologies, it's my first question here!
After the feedback on the comments, I change my answer using flexbox and order property as you point in your question that you have tried before. You do not need two wrappers to get it, instead.
html,body{
width: 100%;
height: 100%;
margin: 0;
}
#container{
width: 100%;
height: 100%;
}
.wrapper{
display: flex;
flex-wrap: wrap;
width: 100%;
height: 100%;
}
.red{
background-color: red;
}
.yellow{
background-color: yellow;
}
.divInside{
border: 1px solid;
height: 25%;
width: 50%;
flex-basis: 49%;
}
.div1{
order: 1;
}
.div2{
order: 3;
}
.div3{
order: 5;
}
.div4{
order: 7;
}
.div5{
order: 2;
}
.div6{
order: 4;
}
.div7{
order: 6;
}
.div8{
order: 8;
}
#media screen and (max-width: 400px){
.wrapper{
width: 100%;
}
.divInside{
flex-basis: 100%;
}
.div3{
order: 4;
}
.div4{
order: 8;
}
.div8{
order: 7;
}
}
<div id="container">
<div class="wrapper">
<div class="divInside div1">1</div>
<div class="divInside div2">2</div>
<div class="divInside div3">3</div>
<div class="divInside div4">4</div>
<div class="divInside div5">5</div>
<div class="divInside div6">6</div>
<div class="divInside div7">7</div>
<div class="divInside div8">8</div>
</div>
</div>
It would be simple to solve this if you set the HTML to a css table structure (display:table-row; and display:table-cell;) setting the desired values for the Desktop format.
Then, using media queries you just set these display properties on the desktop size, and when it goes to mobile size it will stack one on top of the other:
.main > div > div {
border: 1px solid silver;
}
#media (min-width: 400px) {
.main {
display: table;
width: 100%;
}
.main > div {
display: table-row;
}
.main > div > div {
display: table-cell;
}
}
<div class='main'>
<div>
<div>1</div>
<div>5</div>
</div>
<div>
<div>2</div>
<div>6</div>
</div>
<div>
<div>3</div>
<div>7</div>
</div>
<div>
<div>4</div>
<div>8</div>
</div>
</div>
Note: Firefox ONLY Solution
There is an experimental CSS display property of contents which will allow us to have our required structure but unbundle the respective wrappers as required.
I record it here for information purposes pending adoption by other browsers.
MDN Reference
These elements don't produce a specific box by themselves. They are replaced by their pseudo-box and their child boxes.
Codepen Demo
html,
body {
width: 100%;
height: 100%;
margin: 0;
}
#container {
width: 100%;
height: 100%;
display: flex;
}
.wrapper {
width: 50%;
height: 100%;
display: flex;
flex-direction: column;
}
.red > .divInside {
background-color: red;
}
.yellow > .divInside {
background-color: yellow;
}
.divInside {
border: 1px solid;
height: 25%;
width: 100%;
order: 0;
}
.red .divInside:nth-child(1) {} .red .divInside:nth-child(2) {
order: 3;
}
.red .divInside:nth-child(3) {
order: 5
}
.red .divInside:nth-child(4) {
order: 8
}
.yellow .divInside:nth-child(1) {
order: 2;
}
.yellow .divInside:nth-child(2) {
order: 5
}
.yellow .divInside:nth-child(3) {
order: 6
}
.yellow .divInside:nth-child(4) {
order: 7
}
#media screen and (max-width: 760px) {
.wrapper {
display: contents;
}
#container {
flex-direction: column;
}
}
<div id="container">
<div class="wrapper red">
<div class="divInside">1</div>
<div class="divInside">2</div>
<div class="divInside">3</div>
<div class="divInside">4</div>
</div>
<div class="wrapper yellow">
<div class="divInside">5</div>
<div class="divInside">6</div>
<div class="divInside">7</div>
<div class="divInside">8</div>
</div>
</div>

Resources