问题
I have a grid with some items, and when I click any item I move the content of that item to a modal.
The modal works great, but when I remove the content from the item, a space appears above the item.
I know that a way around this problem could be using a flexbox, which works fine, but I want to understand what is going on here.
Something that could be key here is that each item has 3 children.
I use position: absolute
in two of them, and the other one stays with the default position.
If I use position: absolute
in all the children, the problem is fixed.
What is the difference?
When I click the item the content disappears, so how can the result be different depending on the content?
Here's a JSFiddle showing the problem.
Basically, the structure is the following:
HTML
<div class="context">
<div class="grid">
<div class="item">
<div class="cover"> // has position: absolute
<h1>Title</h1>
</div>
<div class="img-wrapper"> // has position: absolute
<img/>
</div>
<div class="description"> // doesn't have a position defined
<p>Description...</p>
</div>
<div class="item">
// item content
</div>
</div>
</div>
I have a total of 8 items, that are wrapped in two rows of 4 items each.
CSS
The CSS to achieve that is the following:
.grid {
margin-top: 100px;
font-size: 0; // to get all the items together
border: 1px solid red;
}
.item {
width: calc(100% / 4);
height: 100px;
position: relative;
display: inline-flex;
}
.description {
display: flex;
flex-direction: column;
justify-content: space-around;
top: 0;
bottom: 0;
}
.img-wrapper {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
img {
height: 100%;
width: auto;
display: block;
margin: auto;
}
.cover {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
Note: The JSfiddle example has more CSS for styling, but the base is the same, and the problem still appears with the code above.
JavaScript
Finally, the JS to move the content to the modal div is the following:
let $itemNode;
let $itemContent;
$().ready(args => {
// Cache the modal node
let $modal = $('#modal');
$('.item').click(function() {
// First cache the item and content
// to put it back when needed
$itemNode = $(this);
$itemContent = $(this).children();
// Hides the item
$itemNode.css({visibility: 'hidden'});
// Transfer project content to the modal
$itemContent.appendTo($modal);
});
});
回答1:
Solutions
I know that a way around this problem could be using a flexbox...
That is correct. Making the parent of the items a flex container solves the problem:
.grid {
display: flex; /* new */
flex-wrap: wrap; /* new */
margin-top: 100px;
font-size: 0;
border: 1px solid red;
}
revised fiddle
Something that could be key here is that each item has 3 children. I use
position: absolute
in two of them, and the other one stays with the default position. If I useposition: absolute
in all the children, the problem is fixed.
Also, correct. By removing the third div from the normal flow of the document, the problem is resolved.
Explanation
Each item in the grid, before any scripting gets involved and items are removed, is artificially positioned.
In other words, in their initial state, the content of the items (the three child divs) are shifting each item to a place where they would otherwise not be. But when the script removes those child divs, each item moves to where it should be.
As you suspected, the problem lies in the third child div of each grid item (.description
).
If you simply apply visibility: hidden
to the grid item – without removing the child divs – the layout doesn't break.
But with your script, you're not only adding visibility: hidden
, you're also removing the child divs.
The first two divs (.cover
and .img-wrapper
) never cause a problem. They are absolutely positioned, so they're already removed from the normal flow.
However, the third div (.description
) is an in-flow child.
This div contains two p
children ("Description for this item X" and "More info"). When either of these children is removed, that's when layout breaks.
This is because – for a reason I haven't yet determined – this div was suppressing vertical-align: baseline
on the parent, which is an inline-level element and, therefore, gets that vertical-align setting by default.
When the script removes the div, baseline alignment is restored to the parent, shifting it up and, therefore, creating the gap.
EDIT:
As @Kukkuz mentioned an answer, using values other than baseline
for vertical-align
also solves the problem.
回答2:
Use vertical-align: top
to item
and you can get rid of the mystery space when the contents of any item is empty. This happens because the grid
items are inline
(inline-flex).
Note that
vertical-align
applies to all inline-level and table-cell elements and is used to vertically align inline level elements- see the doc here.
Change inline-flex
to inline-block
and you can see about the same behavior. Toggle vertical-align
in the below snippet and check for yourself:
.context {
text-align: center;
height: 400px;
border: 1px solid black;
}
#modal {
display: none
}
.grid {
margin-top: 100px;
font-size: 0;
border: 1px solid red;
}
.item {
width: calc(100% / 4);
height: 100px;
transform-origin: center;
position: relative;
display: inline-flex;
transition: box-shadow .3s, transform .3s;
font-size: 1em;
vertical-align: top;
}
.description {
display: flex;
flex-direction: column;
justify-content: space-around;
top: 0;
bottom: 0;
z-index: 100;
opacity: 0;
background-color: rgba(0, 0, 0, 0.6);
color: white;
padding: 20px;
transition: opacity .3s;
}
.img-wrapper {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: white;
}
img {
height: 100%;
width: auto;
display: block;
margin: auto;
}
.cover {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
z-index: 1000;
cursor: pointer;
opacity: 1;
display: flex;
justify-content: center;
align-items: center;
font-weight: bold;
text-align: center;
color: white;
transition: transform .3s, opacity .3s;
}
.cover-red {
background-color: #ff9f80;
}
.cover-green {
background-color: #66cc66;
}
.cover-blue {
background-color: #809fff;
}
.cover-yellow {
background-color: #ffff80;
}
.cover-mag {
background-color: hsl(338, 95%, 70%);
}
.cover-cyan {
background-color: hsl(214, 100%, 65%);
}
.item:hover {
box-shadow: 0 0 6px 3px rgba(0, 0, 0, 0.3);
z-index: 1100;
transform: scale(1.02);
}
.item:hover .cover {
opacity: 0
}
<div class="context">
<div class="grid">
<div class="item">
</div>
<div class="item">
<div class="cover cover-cyan">
<h1>Title 2</h1>
</div>
<div class="img-wrapper">
<img src="http://rs415.pbsrc.com/albums/pp236/Keefers_/Keffers%20Animals/funny-cats-a10.jpg~c200">
</div>
<div class="description">
<p>Description for item 2</p>
<p>More info</p>
</div>
</div>
<div class="item">
<div class="cover cover-mag">
<h1>Title 3</h1>
</div>
<div class="img-wrapper">
<img src="http://i1.kym-cdn.com/entries/icons/original/000/007/203/nononocat.jpg">
</div>
<div class="description">
<p>Description for item 3</p>
<p>More info</p>
</div>
</div>
<div class="item">
<div class="cover cover-green">
<h1>Title 4</h1>
</div>
<div class="img-wrapper">
<img src="https://media0.giphy.com/media/3o85xoi6nNqJQJ95Qc/200_s.gif">
</div>
<div class="description">
<p>Description for item 4</p>
<p>More info</p>
</div>
</div>
<div class="item">
<div class="cover cover-cyan">
<h1>Title 5</h1>
</div>
<div class="img-wrapper">
<img src="https://media0.giphy.com/media/o0vwzuFwCGAFO/200_s.gif">
</div>
<div class="description">
<p>Description for item 5</p>
<p>More info</p>
</div>
</div>
<div class="item">
<div class="cover cover-green">
<h1>Title 6</h1>
</div>
<div class="img-wrapper">
<img src="http://cdn.thecatsite.com/3/37/200x200px-ZC-379ffbad_anime-cat-cute-cuteness-overload-kitten-Favim_com-350157_large.jpeg">
</div>
<div class="description">
<p>Description for item 6</p>
<p>More info</p>
</div>
</div>
<div class="item">
<div class="cover cover-red">
<h1>Title 7</h1>
</div>
<div class="img-wrapper">
<img src="http://www.officialstarwarscostumes.com/~/media/products/oc/yoda-costumes/pet-yoda-costumes/9935319-yoda-cat-hood-pet-star-wars-costumes-000.ashx?w=200&h=200&bc=ffffff">
</div>
<div class="description">
<p>Description for item 7</p>
<p>More info</p>
</div>
</div>
<div class="item">
<div class="cover cover-mag">
<h1>Title 8</h1>
</div>
<div class="img-wrapper">
<img src="http://adst.org/wp-content/uploads/2014/02/cat-general-120-3-_tplq-200x200.jpg">
</div>
<div class="description">
<p>Description for item 8</p>
<p>More info</p>
</div>
</div>
</div>
<div id="modal">
</div>
</div>
I created a demo with inline-block
showing the same behavior. This is a general behavior of inline elements - see below:
.wrapper {
border: 1px solid;
}
.wrapper > * {
display: inline-block;
border: 1px solid;
height: 100px;
width: 100px;
}
<div class="wrapper">
<div></div>
<div>
<h1>kjk</h1>
</div>
</div>
回答3:
This is happening because of using visibility:hidden
, visibility:hidden
only hides visibility of element, and element still acquires space, you should use display:none;
rather than visibility:hidden
https://jsfiddle.net/67w721zq/1/
来源:https://stackoverflow.com/questions/39791663/mystery-space-created-above-row-when-removing-the-content-inside-of-one-item