Problem:
I came across some curious Google Chrome default behaviour while building a simple SPA layout with HTML and CSS-Grid.
When it came to animating the individual rows of the grid that I was either dynamically generating content (& items) for or changing existing elements height on :hover
over another element within the same grid, the height of the containing row would grow:
- upwards - if
element:hover
was on the lower half of the screen (the dynamic content sticks out of the viewport with its top); - downwards - if
element:hover
was on the higher half of the screen (the dynamic content being either entirely visible or sticking out of the viewport downwards);.
!important
The above works only if an element
which is hovered over is taken out of its original place in document and put below dynamic element by grid-area
property. In any other case grid-row always expands downwards.
Update, edited 02.05.2017
As Michael_B pointed out in comments section the curious browser behavior is due to Google Chrome feature called scroll anchoring.
Michael_B: In Chrome, the upward / downward direction of expanding boxes is governed by the scroll position in the viewport, not the layout itself. Basically, they bind a DOM element to the current scroll position. The movement of this particular ("anchor") element on the screen will determine an adjustment, if any, to the scroll position.
You can read all about it here!
Redefined problem:
Since there is no way to modify the scroll anchoring other than shutting it down using overflow-anchor: none
, it does not help to enforce desired behavior described in detail in "Expected result" section.
Expected result:
I'd like to force any elements contained within a grid-row to animate vertically upwards no matter the screen position of an element hovered over. In other words, I want grid-row being animated in height to grow from bottom of itself to the top of the page, instead of top to bottom, in all cases, but subject to structure and document flow retaintment.
Research done:
I've done some digging, and it seems, that there are plenty of solutions for rather similar problems but concerning block displayed elements, and none for the CSS-Grid.
Solutions e. g.,
Here are some from stackoverflow:
And also from outside:
Research summary:
All of the above and many others solutions available on-line revolve mostly around setting a CSS rule of position:relative
to the parent container for the block element to be animated, and than declaring a position:absolute
and bottom:0
on the block element being animated.
And in that case, since the block element is now being positioned absolutely from the bottom of its container it is forced to animate in desired direction, for it has nowhere to expand other than vertically increase upwards.
Why doesn't it work for CSS Grid?
In case of standard document flow position:absolute
element is positioned relative to its first positioned (not static, e. g., position:relative
) ancestor, taking it out of the normal document flow. It's also the case for the Grid, what is specific though is that the normal element within the grid stretches by default both horizontally and vertically to fit the whole declared grid area. Contrarily, the positioned element shrinks to fit and adapts its size to the contents or even worse occupy no vertical space at all. Than you could set grid-auto-rows: minmax()
property to try to cover for that. But again, than it goes to its default behavior. It also does not solve the problem, because having other elements within the grid, will become overflown by the positioned one.
You could off course try to put a block element within the grid-row, and position:
it absolute
to the position:
ed relative
ly grid-row and set bottom:0
for the block element, which seems to work well at first. But than you'll notice, that the block element will grow over the previous document elements. It would actually demonstrate the same behavior, if whole layout structure was built on block elements, which is why I didn't like the idea in a first place. It's also the reason why I stated the obvious in the first sentence of this section.
Suggested solution
I believe, that I've realized how to solve that problem conceptually, but I don't have required technical skills to do so. What seems to me as the right approach is to try and override the initial browser behavior when the element:hover
is on the upper side of the screen, and make it act, as it would be on the other half, since the desired outcome is already built-in in the browser but triggered under undesired circumstances.I'm off course open-minded to other propositions.
Update, edited 02.05.2017
Solution invalid due to technical limits.
#container {
display: grid;
grid-auto-rows: minmax(300px, auto) 100vh;
align-content: center;
color: white;
font-size: 24px;
}
#fisrtelement {
display: grid;
background-color: orange;
}
#secondelement {
background-color: greenyellow;
}
.expandable {
height: 100px;
background-color: forestgreen;
}
.content:hover~.expandable {
height: 150px;
}
.content {
min-height: 120px;
}
#item1 {
grid-area: 1/1;
background-color: forestgreen;
}
#item2 {
grid-area: 2/2;
}
#item3 {
grid-area: 3/3;
background-color: forestgreen;
}
<div id="container">
<div id="fisrtelement">
<div id="item1" class="content">Content</div>
<div id="item3" class="content">Content</div>
<div id="item2" class="expandable">Expandable area (on content:hover)</div>
</div>
<div id="secondelement">
</div>
</div>