23

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?

9
  • 2
    Not sure if it's doable with the starting point of the scrollbar, as it starts where your DIV starts. Commented May 23, 2016 at 13:51
  • I would recommend using a scroll plugin for this. You can affect the scrollbar usually with css or javascript. Commented May 26, 2016 at 9:04
  • This is currently the case for the scrollbar start point isnt'it ? I mean, from your website link, I can see the scroll bar starting from the bottom of header. You could just do a Javascript (or jQuery) function that vary the opacity of the header according to your Y offset position ? I don't want to claim the bounty, it's only a advice comment, please don't downvote me for the competitive's sake. Commented May 26, 2016 at 9:07
  • @Alex the problem with that is: The <article> never goes under the <header> so the opacity change won't make the <article> part to show through the <header>. The native scrollbar starts from under the header for this particular reason. Commented May 26, 2016 at 9:48
  • 1
    @Randomblue use custom scrollbar plugin for example iScroll Commented May 26, 2016 at 14:48

5 Answers 5

8

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>

Sign up to request clarification or add additional context in comments.

7 Comments

This is a cunning solution! Take note that duplicating the DOM tree may impact rendering performance during page-reflows: when the window is resized, or when elements are changed dynamically. It looks like duosear.ch is a "single-page app", and the content will have to be copied into the clone after each change.
The extra (effective) "padding" introduced by the scrollbar prevents the clone from quite lining up with the content, as-written. (At least, in this fiddle, in Chrome 50). You will need to add some extra styling to right-pad the clone by the scrollbar's width, or the two could fall significantly out of synch. You can calculate the scrollbar width as the difference content.offsetWidth - content.clientWidth.
@this-vidor: Interesting. I'm also on Chrome and the scrollbar's don't affect things for me. Are you Mac or Windows?
I get alignment issues in Chrome 50 and Firefox 46 on Ubuntu 14.04. I'm not sure why alignment wouldn't be an issue, since both the scrollable content and un-scrollable clone have the same width, but one has to fit a scrollbar on its right edge.
There is no problem on Mac. I think it has to do with the way scrollbars are handled on Ubuntu. On Mac, the scrollbars are simple overlays and doesn't affect layout.
|
4

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.

2 Comments

Do you have any idea if we can propose or upvote overflow: visible-scroll somewhere?
Here's the link to the documentation declaring that "scrollable elements will always clip inner content by their bounding box": developer.mozilla.org/en-US/docs/Web/CSS/overflow#scroll
1

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.

1 Comment

This seems to be a comment instead of an answer... I don't know, but you can make a code effort, I guess.
1

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.

Pros:

  1. Uses browsers' native scrollbars and scroll events.
  2. No extra libraries needed
  3. Doesn't weigh down the DOM

Cons:

  1. It's a hack. But that's what we do!

HTML

<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>

CSS

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! */

JavaScript

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);
}

2 Comments

I don't think that it is possible without a hack. The webkit scrollbar option looks good, however only for webkit. Not sure why the scrollbar needs to start at the bottom of the header, but if it's just a display thing, rather than functional, I can suggest using a custom scrollbar like nicescroll: areaaperta.com/nicescroll
@WimMertens I think you put this comment in the wrong place; it belongs on the question, not this solution.
0

I found small hack around , set header as over-flow:scroll and and put margin value to fill the whole area

(possibly negative value for eg: `margin:0px -6%;`),

so header will be fit to your wish.after that use padding to counter the effect of margin

(for example padding:0px 6%;)

so this will remove the effect of margin we put earlier , so the child container still will be scrollable , where it will be visible for whole area as you wish

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.