Positioning a fixed element inside another fixed element is behaving differently in Chrome / Safari vs Firefox.
This answer explains well the expected behavior for a fixed div inside a relative one, and MDN is pretty clear on this:
Fixed Positioning Do not leave space for the element. Instead, position it at a specified position relative to the screen's viewport and don't move it when scrolled. When printing, position it at that fixed position on every page.
What I don't understand is what Firefox is doing with a fixed div inside a fixed div. What I expect is that the child element moves along with the wrapper on hover.
.wrapper, .header {
position:fixed;
width:320px;
}
.wrapper:hover{
left:0px;
}
.wrapper{
width:320px;
height:100%;
background:white;
overflow:scroll;
left:-200px;
transition: all ease-out .3s;
}
ul {
margin-top:120px;
}
.header {
background:rgba(255,255,255,0.9);
}
body{
background:gray;
<div class="wrapper">
<div class="header">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repudiandae vitae a, itaque commodi, odio et. Excepturi, obcaecati? Architecto repellendus omnis mollitia animi rem quasi at, odit aperiam voluptatibus voluptates earum!
</div>
<ul>
<li>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusantium quam maiores, voluptas facere, iste quis iusto reiciendis delectus, quod blanditiis tempora. Earum voluptatum dicta quae, explicabo placeat at rerum assumenda!
</li>
<li>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusantium quam maiores, voluptas facere, iste quis iusto reiciendis delectus, quod blanditiis tempora. Earum voluptatum dicta quae, explicabo placeat at rerum assumenda!
</li>
<li>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusantium quam maiores, voluptas facere, iste quis iusto reiciendis delectus, quod blanditiis tempora. Earum voluptatum dicta quae, explicabo placeat at rerum assumenda!
</li>
<li>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusantium quam maiores, voluptas facere, iste quis iusto reiciendis delectus, quod blanditiis tempora. Earum voluptatum dicta quae, explicabo placeat at rerum assumenda!
</li>
</ul>
</div>
Any thoughts? I'm looking for a workaround to have consistency across browsers, too.
Edit : more fun?
Add this to glitch it even more on FF :
.header:hover{
height:200px;
}
On hover, it triggers a repaint, then FF recalculate the position of the element.
Tests made with FF 46.0.1, Chrome 54.0.2840.71 and Safari Version 9.1.1 (11601.6.17). Note: I already read this question
To see two workarounds for the behavior you want, scroll down below the horizontal rule.
11/18/16 Update - The CSSWG got back to me and said that it should create a new stacking context:
You're right, this was supposed to be merged into the positioning spec as well - reflected now. Thanks.
New CSS Position specification diff
On the subject of which browser is correct:
fixed position elements should always be placed relative to the viewport, specifically that the position: fixed element's containing block is established "by the viewport" in 10.1.3:
If the element has 'position: fixed', the containing block is established by the viewport [...]
This containing block is formally called the "initial containing block".
9.3.1 also backs this up by saying that, for normal non-paged media (like this),
[...] In the case of handheld, projection, screen, tty, and tv media types, the box is fixed with respect to the viewport and does not move when scrolled.
What's happening in your code is that you are changing the value of the left property of the parent element on hover, and you are expecting the child element to move, too. However, the child element is (properly) not moving.
10.3.7 says
For the purposes of calculating the static position, the containing block of fixed positioned elements is the initial containing block instead of the viewport.
(static position here meaning the position of the element if it were placed in the normal flow).
It also says:
[If] 'left' and 'right' are 'auto' and 'width' is not 'auto', [...] set 'left' to the static position, otherwise set 'right' to the static position. Then solve for 'left' (if 'direction is 'rtl') or 'right' (if 'direction' is 'ltr').
This explains, I believe, why the child position: fixed element is initially set to left: -200px; per where it would be within its parent element if it were position: static.
At this point, it looks like you believe the parent's new left value should move the child element, I'm assuming, either because you expect the new left property to be inherited by the child (which is not how left works), or you expect it to re-flow the document, which doesn't happen on :hover as I recall; the browser only re-paints on :hover, which doesn't change the document flow, but does change the appearance of elements (e.g. background-color, opacity, visibility: hidden; etc).
So... elements on re-paint shouldn't move unless there are pseudo-selectors that change the properties during temporary states (like :hover), or transitions/animations at play.
In this situation, it appears that Chrome and Safari are doing something other than what the spec suggests; they are either causing a full re-flow, or they have set position: fixed elements to inherit left properties from ancestors. This appears to be above the board, if you will, according to the CSS Working Group draft linked by Oriol below. However, it's still non-standard behavior until the spec is updated.
Long-story short, Chrome and Safari are wrong right now, but eventually once the spec is updated, they will be correct, and Firefox will have to update its rendering behavior.
Make the .header div inherit your new left property, since that's how Chrome is doing it and that is the behavior you seek. I also adjusted .header's width just a bit, so that it won't cover the scroll bar on .wrapper:
.wrapper, .header {
position: fixed;
}
.wrapper:hover {
left:0px;
}
.wrapper{
width:320px;
height:100%;
background:white;
overflow:scroll;
left:-200px;
transition: all ease-out .3s;
}
ul {
margin-top:120px;
}
.header {
background:rgba(255,255,255,0.9);
left: inherit;
width: 303px;
}
body{
background:gray;
}
<div class="wrapper">
<div class="header">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Repudiandae vitae a, itaque commodi, odio et. Excepturi, obcaecati? Architecto repellendus omnis mollitia animi rem quasi at, odit aperiam voluptatibus voluptates earum!
</div>
<ul>
<li>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusantium quam maiores, voluptas facere, iste quis iusto reiciendis delectus, quod blanditiis tempora. Earum voluptatum dicta quae, explicabo placeat at rerum assumenda!
</li>
<li>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusantium quam maiores, voluptas facere, iste quis iusto reiciendis delectus, quod blanditiis tempora. Earum voluptatum dicta quae, explicabo placeat at rerum assumenda!
</li>
<li>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusantium quam maiores, voluptas facere, iste quis iusto reiciendis delectus, quod blanditiis tempora. Earum voluptatum dicta quae, explicabo placeat at rerum assumenda!
</li>
<li>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusantium quam maiores, voluptas facere, iste quis iusto reiciendis delectus, quod blanditiis tempora. Earum voluptatum dicta quae, explicabo placeat at rerum assumenda!
</li>
</ul>
</div>
So, I think problem has arisen due to the bug in implementation of left in Firefox.
When hovering .wrapper, .header should get new left value from its parent .wrapper i.e. 0px.
When hovering .wrapper, the left position of .header should be calculated using the left value from its parent .wrapper as no explicit left value is given to .header.
I think its due to bug in Firefox. If you activate :hover pseudo class of .wrapper using Firebug or the default developer tool, the left position of .header is maintained like in Chrome (but in sudden manner).
Tested on Firefox 49.0.2 and Chrome 54.0.2840.71
Related
I have a HTML setup:
<div id="wrapper">
<div id="content">
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Ex corporis perferendis in fugiat cumque. Ipsam modi quia doloremque, animi quisquam quo exercitationem nihil debitis ea corrupti, provident placeat officiis nam.
</div>
</div>
I would like the height of the div with id wrapper to be equal to the height of the div with id content, minus X px (say 4px).
The div with id wrapper must be positioned according to the normal flow of the document. I.e. not absolute or fixed.
Is this possible?
I am asking because I would like to remove the top and bottom spacing of the text, (not padding, border, margin), but keep spacing between the text in case of wrapping.
Essentially; If the text would never wrap, (which it can), it would be equal to setting the line-height to 1 (or the font size).
EDIT: Asked another way, can i set line-height to 1 for only the top half of the first line, and the bottom half of the last line?
If there are any other ways of accomplishing this, I.e css properties like paragraph-height or similar, this is very welcome.
It's difficult to understand precisely what you need here, but if you want the parent to be the same height as its child but minus a few pixels, you could add a negative margin to the child to reduce the parent's height.
#wrapper{
background: red;
display: inline-block;
}
#content {
margin-bottom: -4px;
margin-top: -4px;
}
<div id="wrapper">
<div id="content">
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Ex corporis perferendis in fugiat cumque. Ipsam modi quia doloremque, animi quisquam quo exercitationem nihil debitis ea corrupti, provident placeat officiis nam.
</div>
</div>
In CSS you can use calc() to make calculations.
#content{
height: calc(100% - 4px)
}
So, when you with link internally on the page with eg. <a href="#fragment"> to <div id="fragment">, and click it, it places the target always at the very top of the viewport(as far as the scrollbar will go that is.)
Is there a way to address/modify that location to, for example, the absolute center of your viewport?
You can add a margin using the scroll-margin property. Add scroll-margin to the target element. (here #fragment).
Example:
#fragment {
margin: 1000px 0; /*Just to make the page scrollable*/
scroll-margin: 50vh;
}
Click Me
<div id="fragment">
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Pariatur maxime, accusantium eius in iusto accusamus rerum dignissimos vero minima minus doloribus inventore ipsam distinctio, natus aut velit autem dolorem reprehenderit.
</div>
Explanation:
Here scroll-margin: 50vh;, 50vh will make the element #fragment appear on 50% of the current viewport height when clicked on the link.
👉 Codepen
This question already has an answer here:
Why position:sticky is not working when the element is wrapped inside another one?
(1 answer)
Closed 4 years ago.
I'm making my first website and I want to make the tab bar to stick to the top of the screen and stay on screen when you scroll, but position:sticky doesn't seem to be doing this.
div#tabBar {
position: sticky;
top: 0;
display: flex;
flex-direction: row;
background-color: #29335C;
}
<div>
<div id="tabBar">
<a class="tabLinks">Home</a>
<a class="tabLinks">About Me</a>
</div>
<h1 id="homeFrame">Anna Grace</h1>
<div id="projectList"></div>
</div>
If you want it to the top of the screen, simply switch to position: fixed;
Position fixed is always relative to the upper left corner of the window, which is convinient in your case. Be aware that, because a fixed elenmet has no width, the content will start under/behind it. You might wat to give your body a padding top equal to the height of your header.
Position sticky works differently. It remains as a block/normal element until it's at the given top position, than it switches to fixed behaviour. Think like those advertisements that appear nexto content and stay where they are when you scroll down.
In your case the difference will be minimal, as the header start at 0, so it instanly switches to fixed,it just might behave a little more unpredictable.
it's works. I deleted parent div.
#tabBar{
position: sticky;
top:0;
background-color: #29335C;
display:flex;
flex-direction:row;
}
p {
font-size:36px;
}
<div id="tabBar">
<a class="tabLinks">Home</a>
<a class="tabLinks">About Me</a>
</div>
<div id="projectList"></div>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Minus necessitatibus ad voluptates? Libero harum perspiciatis incidunt voluptatum aliquam magni facere officia debitis? Placeat, saepe dolores praesentium culpa a voluptate quia?</p>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Minus necessitatibus ad voluptates? Libero harum perspiciatis incidunt voluptatum aliquam magni facere officia debitis? Placeat, saepe dolores praesentium culpa a voluptate quia?</p>
<p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Vel, deleniti blanditiis neque id libero, sit consectetur harum optio omnis dolorem quo laborum quaerat doloremque ullam corporis pariatur sunt excepturi maiores!</p>
<p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Non neque error quod nam repellat placeat vitae odit, nulla a deserunt nostrum est nihil sed dicta accusamus molestiae recusandae modi saepe.</p>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Facere eligendi quod accusamus dignissimos minus eum dolorum, commodi enim asperiores dicta nesciunt officiis praesentium quasi voluptas, explicabo sapiente neque atque perferendis!</p>
give outer div some height and the try. thanks
div#tabBar {
position: sticky;
top: 0;
display: flex;
flex-direction: row;
background-color: #29335C;
}
.outer{
height:1000px}
<div class="outer">
<div id="tabBar">
<a class="tabLinks">Home</a>
<a class="tabLinks">About Me</a>
</div>
<h1 id="homeFrame">Anna Grace</h1>
<div id="projectList"></div>
</div>
here is the code: https://jsfiddle.net/zz89emkr/1/
.menu-items {
width: 400px;
text-indent: 5%;
}
<div class="menu-items">
<span>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Necessitatibus quia maiores voluptatum adipisci iusto perferendis earum quasi, accusamus magni temporibus alias consectetur, provident vel quis nesciunt expedita sit nemo aliquam?</span>
<div>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Illo quas esse eius. Quos atque ea necessitatibus labore est error hic. At quae veritatis sit aperiam debitis animi provident dolorum dolore?</div>
<span>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Cumque, eaque atque adipisci, fugiat fuga maiores repellendus voluptas non explicabo odio et ut tenetur sint iusto minima unde. Ex, voluptas sed?</span>
</div>
I know that text-indent will not work on inline element, however, in this example, the first span will get text-indent. What's more, in firefox, the text-indent in the first span element is larger than that in the div element.
Text-indent has effect on the first child span
No, actually the indentation has nothing to do with the span. Being that you have set text-indent on a block element - .menu-items - indentation will be applied before:
1) The first element/node in the container - regardless of whether that element is inline or block or whether the element is text or other content (such as an image) and...
2) All subsequent block level text elements in the container (*)(see below)
.wpr {
text-indent: 5%;
}
<div class="wpr">
This is just a regular text node. However, since it's the <strong>first node</strong> in the container - it is indented (due to text-indent being set on the wrapper div)
<p>This is some block level text - it's indented</p>
<span>Here is some inline level text</span>
<div>Here's some more block level text - again, indented</div>
This text node is <strong>not indented</strong>
</div>
<hr>
<div class="wpr">
<img src="http://via.placeholder.com/350x150" alt="">
<p>Notice that the image above is indented (it's the first element in the container) This block level text - so it's also indented</p>
<span>Here is some inline level text</span>
<div>Here's some more block level text - again, indented</div>
This text node is <strong>not indented</strong>
</div>
What's more, in firefox, the text-indent in the first span element is
larger than that in the div element
Firstly, this discrepancy on firefox can only be reproduced when:
1) The container element has a set width (less than the viewport width)
2) text-indent is set with a percentage value
3) The first element in container is not block level
(All 3 criteria are present in the example fiddle you provided)
This looks like a bug, since firefox renders the indentation (before the first element) as a percentage value of the viewport width instead of a percentage of the container width!
Codepen Demo (Resize in firefox to see this - and keep your eye on the indentation of the first line)
Being that The spec clearly states (bold mine):
Percentages: refers to width of containing block
...I'd say that firefox is doing it wrong.
(*) That's my understanding of what is said in the spec:
Unless otherwise specified by the each-line and/or hanging keywords,
only lines that are the first formatted line [CSS21] of an element
are affected. For example, the first line of an anonymous block box is
only affected if it is the first child of its parent element.
The spec elsewhere elaborates on the meaning of "first formatted line":
The "first formatted line" of an element may occur inside a
block-level descendant in the same flow (i.e., a block-level
descendant that is not positioned and not a float). E.g., the first
line of the DIV in <DIV><P>This line...</P></DIV> is the first line of
the P (assuming that both P and DIV are block-level).
Give display:block to .menu-items span because span default behavior is inline-block.
.menu-items {
width: 400px;
text-indent: 5%;
}
.menu-items span{
display:block;
}
<div class="menu-items">
<span>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Necessitatibus quia maiores voluptatum adipisci iusto perferendis earum quasi, accusamus magni temporibus alias consectetur, provident vel quis nesciunt expedita sit nemo aliquam?</span>
<div>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Illo quas esse eius. Quos atque ea necessitatibus labore est error hic. At quae veritatis sit aperiam debitis animi provident dolorum dolore?</div>
<span>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Cumque, eaque atque adipisci, fugiat fuga maiores repellendus voluptas non explicabo odio et ut tenetur sint iusto minima unde. Ex, voluptas sed?</span>
</div>
If you have every created a dynamic page, you may notice that if you start out with a page height that does not require a scrollbar and then add content dynamically, the scroll bar will appear. When it does this, it "pushes" all of my content to the left the width of the scroll bar and it appears that everything on the page jumps a little.
Is it possible to make the scrollbar act as if it were position absolute so that instead of pushing my content all to the left, it just lays over the content. I do not like the way the content all "jumps" to the left; it looks nasty.
Thanks
you can use overflow: overlay to avoid your content being pushed, what is does is instead of taking your container space it position the scrollbar to top of you content
.overlay {
width: 100px;
height: 100px;
overflow: overlay;
}
.auto {
width: 100px;
height: 100px;
overflow: auto;
}
<h2>scrollbar on content</h2>
<div class="overlay">
<div class="scrollbox-content">Hover me! Lorem ipsum dolor sit amet, consectetur adipisicing elit. Facere velit, repellat voluptas ipsa impedit fugiat voluptatibus. Facilis deleniti, nihil voluptate perspiciatis iure adipisci magni, nisi suscipit aliquam, quam, et excepturi! Lorem
ipsum dolor sit amet, consectetur adipisicing elit. Facere velit, repellat voluptas ipsa impedit fugiat voluptatibus. Facilis deleniti, nihil voluptate perspiciatis iure adipisci magni, nisi suscipit aliquam, quam, et excepturi!</div>
</div>
<h2>scrolbar sharing space with content</h2>
<div class="auto">
<div >Hover me! Lorem ipsum dolor sit amet, consectetur adipisicing elit. Facere velit, repellat voluptas ipsa impedit fugiat voluptatibus. Facilis deleniti, nihil voluptate perspiciatis iure adipisci magni, nisi suscipit aliquam, quam, et excepturi! Lorem
ipsum dolor sit amet, consectetur adipisicing elit. Facere velit, repellat voluptas ipsa impedit fugiat voluptatibus. Facilis deleniti, nihil voluptate perspiciatis iure adipisci magni, nisi suscipit aliquam, quam, et excepturi!</div>
</div>
the div with overlay css placed on top of the overflown div however the div with auto overflow pushes the data.
Using this for container with text is not an ideal solution, but I chooses text so the difference would be clear
You could, but it wouldn't be perfect. Copying the code from this post:
// Used like $('#my-id').hasScrollbar();
jQuery.fn.hasScrollbar = function() {
var scrollHeight = this.get(0).scrollHeight;
//safari's scrollHeight includes padding
if ($.browser.safari)
scrollHeight -= parseInt(this.css('padding-top')) + parseInt(this.css('padding-bottom'));
if (this.height() < scrollHeight)
return true;
else
return false;
}
You could query to see if the scroll bar is present. Before this, however, you'll have a global variable that is the width of the viewport prior to the scrollbar appearing:
var viewportWidth = $(window).width();
And after running the function above, you could compare this viewportWidth with the new viewport width of the window with the scroll bar, and margin-right the body the negative amount of the difference.
What you ask cannot be done.
You could however, force it to appear at all times:
#id {
overflow: scroll;
}
But this is horribly ugly.