Is it possible to unwrap a HTML element using CSS? [duplicate] - html

This question already has answers here:
Is there a way to remove a div but keep its elements?
(2 answers)
Closed 1 year ago.
I have a content management system that generates code like
<div class="container">
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<div class="item">Item 3</div>
</div>
and depending on the query it might also generate
<div class="container">
<div class="section">
<div class="item">Item 1</div>
<div class="item">Item 2</div>
<div class="item">Item 3</div>
</div>
</div>
I want container be a flexbox container, and item be flexbox children in all cases.
Is there a way to unwrap the section element (=make the browser ignore this div layer - so that the item elements will be treated as if they where direct children of container)?
Or is that impossible with pure CSS?

You can do that like this:
.container, .container > .section {
display: flex;
...
}
.container > .item, .container > .section > .item {
...
}
It wouldn’t be a good idea to remove the section element because that is there for a reason. But the css above will take care of both cases.

You can add new rules for case when CMS creates additional tag and then add specificity them to increase chances that correct rule will be applied:
div.container div.section {
display: flex;
}
Read more about specificity here

As far as children elements are concerned, .section is a child element of .container and .item elements are children of .section. There isn't a way to ignore this via CSS.
I'd recommend you copy the styles of .container to .section and just make the necessary adjustments there.
It would help if you shared your current styles.

Related

Can html element be skipped by css?

TL;DR:
Is it possible for css to ignore html element, but not its children?
Such element would be treated by css as if it wasn't there; but its children would be treated normally, i.e. as children of parent of the ignored element.
Details, Motivation:
Let's say we have a nice styled layout, e.g. with display: flex.
<div className="outer"><!-- this one has display: flex (just example) -->
<div className="inner">Foo</div>
<div className="inner">Bar</div>
<div className="inner">Baz</div>
<div className="inner">Foo 2</div>
<div className="inner">Bar 2</div>
<div className="inner">Baz 2</div>
</div>
But then, we need to wrap one group of our inner elements into form, or nav (for semantic or other reasons):
<div className="outer">
<div className="inner">Foo</div>
<div className="inner">Bar</div>
<div className="inner">Baz</div>
<form>
<div className="inner">Foo 2</div>
<div className="inner">Bar 2</div>
<div className="inner">Baz 2</div>
</form>
</div>
Well, of course this breaks our desired layout (e.g. flex), because <form> became the child of outer, and sibling of the first three inners.
Is it possible to make an element, in this case form, to be ignored by css - as if it wasn't there in the html element tree?
If it's not possible, has this feature ever been considered, worked on, rejected... ?
That's exactly what display:contents is designed to do. So:
form { display:contents }
.outer {
display: flex;
justify-content: space-evenly;
}
form {
display: contents;
}
<div class="outer">
<div class="inner">Foo</div>
<div class="inner">Bar</div>
<div class="inner">Baz</div>
<form>
<div class="inner">Foo 2</div>
<div class="inner">Bar 2</div>
<div class="inner">Baz 2</div>
</form>
</div>
just set the form to display: flex
now the form is a direct child... so you can for example set it to flex:1 or so.
and you will get a new "parent" for the form child elements.

Flex child push siblings to the bottom [duplicate]

This question already has answers here:
Force flex item to span full row width
(2 answers)
Closed 1 year ago.
It is posible to have a div parent with display flex and that the first child push the next siblings to the bottom line.
I have this situation and using display flex and the structure can´t be modify.
This could be done with grid with 3 lines of code, but I can´t figgure it out using flex in the parent.
<div class=items-container>
<div class="item1">item 1</div>
<div class="item2">item 2</div>
<div class="item3">item 3</div>
</div>
I left the example in this link:
https://codepen.io/plevindo/pen/porqbXv
Pretty simple, just use flex with flex-wrap on the parent, and the first item set flex-basis to 100%. That will push the siblings to the next line.
.items-container {
display: flex;
flex-wrap: wrap;
}
.item1 {
flex: 1 1 100%;
}
<div class=items-container>
<div class="item1">item 1</div>
<div class="item2">item 2</div>
<div class="item3">item 3</div>
</div>

Using CSS selectors first of type and not first child together

I'm trying to do some styling using purely CSS selectors, my markup looks like this:
<div class="StyledRow">
<div class="Row">Input 1</div>
<div class="Row">Input 2</div>
</div>
<div class="StyledRow">
<div class="Row">Input 1</div>
<div class="Row">Input 2</div>
</div>
<div class="StyledRow">
<div class="Row">Input 1</div>
<div class="Row">Input 2</div>
</div>
The rendered elements are inputs side by side (2) and then stacked on top of one another (so the user can add as many data sets as they wish)
The above is a simplified version but because of additional elements, I need to be a bit more specific with my styles. For example, the current style of StyledRow looks like this:
const StyledRow = styled.div`
display: flex;
align-items: center;
*:not(:first-child) > * {
margin-left: 0.2rem;
}
`;
I am trying to target the second Row in the first StyledRow so that the styles can be different from the rest which use a margin-left: 0.2rem. I had tried to use first-of-type and not-first-child together but with no joy.
Any suggestions would be really appreciated!
If you use
*:not(:first-child) > * {
margin-left: 0.2rem;
}
it will apply that to everything. So if you use
.StyledRow:not(:first-child) > * {
margin-left: 0.2rem;
}
the styling will be applied on every iteration of StyledRow apart from the first one.
If you use:
.StyledRow:nth-child(2) > * {
margin-left: 0.2rem;
}
then that will specifically target the second iteration of StyledRow.

Coloring alternating divs of certain class

My code is as follows:
HTML
<div class="divs">
<div class="row">row 0</div>
<div class="not-row"></div>
<div class="row">row 1</div>
<div class="not-row"></div>
<div class="row">row 2</div>
<div class="not-row"></div>
<div class="row">row 3</div>
<div class="not-row"></div>
</div>
CSS
.row:nth-child(even) {
background: #fff;
}
.row:nth-child(odd) {
background: #eee;
}
This is supposed to paint the background of two of the rows gray and two of the rows white. Unfortunately it paints all of their backgrounds gray. What am I doing wrong?
I tried using nth-of-type instead of nth-child but that didn't change anything.
jsFiddle example
For even just use (as a default)
.row {}
Then override the odd ones with:
.row:nth-child(4n+1) {}
.row {
background: #fff;
}
.row:nth-child(4n+1) {
background: #eee;
}
http://jsfiddle.net/b8ma1hon/3/
More on how nth-child works can be found here:
https://css-tricks.com/how-nth-child-works/
You cannot simply use even/odd in this instance as that is in relation to all child elements, not just the ones with the class row.
Your inclusion of .row in the selector is purely an extra criteria and has no impact on the nth-child selector.
Likewise I could state:
.row:nth-child(1):hover {}
This would restrict selection to an element with a class of row, which is the 2nd child, which is currently in a hovered state.
It wouldn't make sense if this was the 2nd element out of all the hovered elements as you can only hover over one at a time.
I hope that makes sense!
It's also worth noting that your selector is now dependant on the not-row existing, or at least some kind of element existing between the row elements.
If this was to change then your selector would also have to change.
Alternatively you could change your element type for the not-row elements to something else so that you can make use of the nth-of-type selector:
<div class="divs">
<div class="row">row 0</div>
<span class="not-row"></span>
<div class="row">row 1</div>
<span class="not-row"></span>
<div class="row">row 2</div>
<span class="not-row"></span>
<div class="row">row 3</div>
<span class="not-row"></span>
</div>
.row {
background: #fff;
}
.row:nth-of-type(odd) {
background: #eee;
}
http://jsfiddle.net/b8ma1hon/5/

:last-child not working as expected?

The issue lies within this CSS and HTML. Here is a link to jsFiddle with the sample code.
HTML
<ul>
<li class"complete">1</li>
<li class"complete">2</li>
<li>3</li>
<li>4</li>
</ul>
CSS
li.complete:last-child {
background-color:yellow;
}
li.complete:last-of-type {
background-color:yellow;
}
Shouldn't either of these lines of CSS target the last li element with the "complete" class?
This query in jQuery doesn't target it either:
$("li.complete:last-child");
But this one does:
$("li.complete").last();
li {
background-color: green;
}
li.complete:first-child {
background-color: white;
}
li.complete:first-of-type {
background-color: red;
}
li.complete:last-of-type {
background-color: blue;
}
li.complete:last-child {
background-color: yellow;
}
<ul>
<li class="complete">1</li>
<li class="complete">2</li>
<li>3</li>
<li>4</li>
</ul>
:last-child will not work if the element is not the VERY LAST element
I think it's crucial to add/emphasize that :last-child will not work if the element is not the VERY LAST element in a container. For whatever reason it took me hours to realize that, and even though Harry's answer is very thorough I couldn't extract that information from "The last-child selector is used to select the last child element of a parent."
Suppose this is my selector: a:last-child {}
This works:
<div>
<a></a>
<a>This will be selected</a>
</div>
This doesn't:
<div>
<a></a>
<a>This will no longer be selected</a>
<div>This is now the last child :'( </div>
</div>
It doesn't because the a element is not the last element inside its parent.
It may be obvious, but it was not for me...
Sidebar: This may seem like a ridiculous gotcha, but the devil's always in the details. :last-of-type may fit your needs in most cases (and feels intuitive) but :last-child definitely serves a purpose. It offers greater specificity (targeting only those elements which are, in-fact, the very last child in a parent). It depends on your use-case.
The last-child selector is used to select the last child element of a parent. It cannot be used to select the last child element with a specific class under a given parent element.
The other part of the compound selector (which is attached before the :last-child) specifies extra conditions which the last child element must satisfy in-order for it to be selected. In the below snippet, you would see how the selected elements differ depending on the rest of the compound selector.
.parent :last-child{ /* this will select all elements which are last child of .parent */
font-weight: bold;
}
.parent div:last-child{ /* this will select the last child of .parent only if it is a div*/
background: crimson;
}
.parent div.child-2:last-child{ /* this will select the last child of .parent only if it is a div and has the class child-2*/
color: beige;
}
<div class='parent'>
<div class='child'>Child</div>
<div class='child'>Child</div>
<div class='child'>Child</div>
<div>Child w/o class</div>
</div>
<div class='parent'>
<div class='child'>Child</div>
<div class='child'>Child</div>
<div class='child'>Child</div>
<div class='child-2'>Child w/o class</div>
</div>
<div class='parent'>
<div class='child'>Child</div>
<div class='child'>Child</div>
<div class='child'>Child</div>
<p>Child w/o class</p>
</div>
To answer your question, the below would style the last child li element with background color as red.
li:last-child{
background-color: red;
}
But the following selector would not work for your markup because the last-child does not have the class='complete' even though it is an li.
li.complete:last-child{
background-color: green;
}
It would have worked if (and only if) the last li in your markup also had class='complete'.
To address your query in the comments:
#Harry I find it rather odd that: .complete:last-of-type does not work, yet .complete:first-of-type does work, regardless of it's position it's parents element. Thanks for your help.
The selector .complete:first-of-type works in the fiddle because it (that is, the element with class='complete') is still the first element of type li within the parent. Try to add <li>0</li> as the first element under the ul and you will find that first-of-type also flops. This is because the first-of-type and last-of-type selectors select the first/last element of each type under the parent.
Refer to the answer posted by BoltClock, in this thread for more details about how the selector works. That is as comprehensive as it gets :)
I encounter similar situation. I would like to have background of the last .item to be yellow in the elements that look like...
<div class="container">
<div class="item">item 1</div>
<div class="item">item 2</div>
<div class="item">item 3</div>
...
<div class="item">item x</div>
<div class="other">I'm here for some reasons</div>
</div>
I use nth-last-child(2) to achieve it.
.item:nth-last-child(2) {
background-color: yellow;
}
It strange to me because nth-last-child of item suppose to be the second of the last item but it works and I got the result as I expect.
I found this helpful trick from CSS Trick