I have a header
element with an article
element below it. The article
has overflow: scroll
. Please see my website here.
My goal is to make the header
semi-transparent so that the article
content will show behind it when scrolled. I want the article
overflow to show with the header
.
The main restriction is that the scrollbar should span from the bottom of the header
, not the top.
How can such an effect be done?
A trick is to copy the content. Below is a solution with a blurry and semi-transparent header.
$(function() {
// Clone the content
var $content = $('.content');
var $clone = $('.clone').html($content.html());
// Performance scrolling
var content = $content.get(0);
var clone = $clone.get(0);
content.onscroll = function() {
clone.scrollTop = content.scrollTop;
};
});
div {
width: 400px;
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
}
.content {
top: 50px;
overflow: scroll;
}
.clone {
padding-top: 50px;
overflow: hidden;
-webkit-filter: blur(2px);
}
.header {
height: 50px;
}
.overlay {
background: rgba(240, 240, 240, 0.8);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="content">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus at orci vestibulum, congue urna nec, fringilla dolor. Donec pellentesque odio non dui vehicula, eu gravida odio rhoncus. Sed eleifend eu nisi ullamcorper ultricies. Ut ante velit, facilisis
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus at orci vestibulum, congue urna nec, fringilla dolor. Donec pellentesque odio non dui vehicula, eu gravida odio rhoncus. Sed eleifend eu nisi ullamcorper ultricies. Ut ante velit, facilisis
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus at orci vestibulum, congue urna nec, fringilla dolor. Donec pellentesque odio non dui vehicula, eu gravida odio rhoncus. Sed eleifend eu nisi ullamcorper ultricies. Ut ante velit, facilisis
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus at orci vestibulum, congue urna nec, fringilla dolor. Donec pellentesque odio non dui vehicula, eu gravida odio rhoncus. Sed eleifend eu nisi ullamcorper ultricies. Ut ante velit, facilisis
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus at orci vestibulum, congue urna nec, fringilla dolor. Donec pellentesque odio non dui vehicula, eu gravida odio rhoncus. Sed eleifend eu nisi ullamcorper ultricies. Ut ante velit, facilisis
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus at orci vestibulum, congue urna nec, fringilla dolor. Donec pellentesque odio non dui vehicula, eu gravida odio rhoncus. Sed eleifend eu nisi ullamcorper ultricies. Ut ante velit, facilisis
</div>
<div class="header">
<div class="clone"></div>
<div class="overlay"></div>
</div>
Scrollable elements will always clip inner content by their bounding box. Unless they add something like overflow: visible-scroll
to the specs (I wish!).
There's a workaround for this particular case: a custom Webkit scrollbar (Webkit-only, of course). Simply style the "up" button such that its height and background color matches the header's. This will ensure the scroll bar's track does not extend underneath the header, while keeping styling consistent.
JSFiddle: "Webkit Scrollbar Hack For Translucent Headers"
header {
background: rgba(240, 240, 240, 0.9);
height: 60px;
}
::-webkit-scrollbar-button:start {
background: rgba(240, 240, 240, 0.9);
height: 60px;
}
Be sure to pad the top of your scrollable content area.
#content {
padding-top: 60px;
}
As for the non-Webkit browsers, I can think of no CSS-only solutions.
You could do this by adding padding to your content (that scrolls), the same padding as the height of the header. And then moving the content up and under the header (and making the header transparent). Then creating a custom scrollbar with some javascript and html/css that has an offset from the header.
Update: Just 'scroll'
events, no 'wheel'
or 'mousewheel'
.
CSS and JavaScript?
Here's a way: (jsfiddle).
Tested in Firefox 46 and Chrome 50.
This solution uses an empty dummy element inside a scrollable element, sized to match the height of the content-bearing element (with hidden scrollbar), and some lightweight JavaScript to relay 'scroll'
events between content and dummy.
scroll
events.<div id="wrapper">
<div id="header"></div>
<!-- An empty dummy to carry the scrollbar -->
<div id="scroll-wrap">
<div id="scroll-dummy"></div>
</div>
<!-- Content -->
<div id="content-view">[Actual content.]</div>
</div>
body { overflow: hidden; }
#content-view {
position: absolute;
z-index: 0;
top: 0; /* Place content behind header. */
height: 100vh; /* Fill viewport. */
overflow-y: scroll; /* Hidden scrollbar. */
}
#header {
position: relative; /* Enable z-index. */
z-index: 1; /* Place header above content. */
height: 25vh; /* Top of viewport. */
}
#scroll-wrap {
position: absolute;
z-index: 1; /* Place scrollbar above content. */
top: 25vh; /* Skip header. */
height: 75vh; /* Fill remaining viewport. */
right: 0; /* Avoid non-scroll mouse events. */
overflow-y: scroll; /* Scrollbar! */
}
#scroll-dummy { width: 1px; } /* Firefox won't scroll width:0 elements! */
var $ = document.getElementById.bind(document),
contentView = $('content-view'), scrollDummy = $('scroll-dummy'),
scrollWrap = $('scroll-wrap'), wrapper = $('wrapper'), header=$('header');
scrollWidth = scrollWrap.offsetWidth - scrollWrap.clientWidth;
// Hide content's actual scrollbar
contentView.style.right = -scrollWidth + 'px';
// Copy height of visible content to hidden dummy
function onContentChange() {
window.requestAnimationFrame(function() {
scrollDummy.style.height = contentView.scrollHeight - header.offsetHeight + 'px';
});
}
window.addEventListener('resize', onContentChange);
onContentChange(); // Call whenever the contentView node changes!
// No event-triggering feedback loops
var fromContent = false, fromScroll = false;
// Update scrollbar scroll on content scroll
animate(contentView, 'scroll', function(e){
if (fromScroll) fromScroll = false;
else {
fromContent = true;
scrollWrap.scrollTop = contentView.scrollTop; // Scroll the scrollbar
}
});
// Update content scroll on scrollbar scroll
animate(scrollWrap, 'scroll', function(){
if (fromContent) fromContent=false;
else {
fromScroll = true;
contentView.scrollTop = scrollWrap.scrollTop; // Scroll the content
}
});
// Wrap event handler with throttled rAF
function animate(element, event, func) {
var lock = false;
element.addEventListener(event, function(e) {
if (!lock) {
lock = true;
window.requestAnimationFrame(function(){ func(e); lock = false; });
}
}, false);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With