30

I want to change the color of a position:fixed menu when scrolling.

enter image description here

My first intention was to use two fixed menus and overflow:hidden but it doesn't work on fixed elements. My second try was using z-index. But it seems impossible.

Maybe someone has an idea?

Null
  • 1,950
  • 9
  • 30
  • 33
bench-o
  • 2,339
  • 5
  • 21
  • 21
  • Check out the [CSS clip property](https://developer.mozilla.org/en-US/docs/Web/CSS/clip). `clip-path` is newer but is not supported by IE. – joews Dec 15 '14 at 13:32
  • Could use a mix of javascript and gradients, by placing invisible objects, you could use script to check if menu is touching the object and then do it frame by frame, though this might give problems on the return back up. You could also make these objects disapear as you pass them and as each one disappears on apears for the return. Id build a whole solution but im not that strong with javascript, But it would be like an animation, could also use a image sprite with each frame instead of gradients. – RyeNyeTheComputerScienceGuy Dec 15 '14 at 13:54
  • Here is an example of the CSS Clip property as mentioned before. Perhaps you could dissect this example and figure out a solution. http://codepen.io/lbebber/pen/xrwja – knocked loose Dec 15 '14 at 14:00
  • Looks good ether, but unfortionally it is using images to work in firefox. – bench-o Dec 15 '14 at 14:05
  • Using you can use collision detection to create an animation that would allow your image to change, as needed http://www.playmycode.com/blog/2011/08/javascript-per-pixel-html5-canvas-image-collision-detection/ – RyeNyeTheComputerScienceGuy Dec 15 '14 at 14:23

4 Answers4

14

What you are looking for is clipping. This allows you to specify a rectangular region where an element is visible.

You can use:

clip: rect(auto, auto, auto, auto);

on the container to emulate overflow: hidden for the position: fixed menu, so you can crop the text as you scroll.

Note that while clip is deprecated, the new clip-path does not work with position: fixed elements, so you are stuck with clip for now.

clip requires absolute or fixed positioning, but you can easily work around that problem by placing a position: absolute element inside a position: relative container, like so:

<div style="position: relative;">
    <div style="position: absolute; clip: rect(auto, auto, auto, auto);">
        <!-- My awesome menu here -->
    </div>
</div>

Here is the demo:

html,
body {
  height: 100%;
  margin: 0;
  padding: 10% 5% 80% 5%;
  background-color: #eee;
  font-family: sans-serif;
}
.container {
  display: table;
  width: 100%;
  height: 100%;
  background-color: #fff;
}
.row {
  display: table-row;
}
.cell {
  display: table-cell;
  position: relative;
}
.cell.small {
  height: 25%;
}
.header,
.content,
.footer {
  position: absolute;
  width: 100%;
  height: 100%;
  padding: 4%;
  box-sizing: border-box;
  clip: rect(auto, auto, auto, auto);
}
.header,
.footer {
  background-color: #F97D9F;
}
.menu {
  position: fixed;
  font-size: 2em;
  top: 10%;
  right: 20%;
}
.white {
  color: #fff;
}
.black {}
<div class="container">
  <div class="row">
    <div class="cell small">
      <div class="header">
        content
        <div class="menu white">MENU</div>
      </div>
    </div>
  </div>
  <div class="row">
    <div class="cell">
      <div class="content">
        content
        <div class="menu black">MENU</div>
      </div>
    </div>
  </div>
  <div class="row">
    <div class="cell small">
      <div class="footer">
        content
        <div class="menu white">MENU</div>
      </div>
    </div>
  </div>
</div>
Antony
  • 14,900
  • 10
  • 46
  • 74
9

The behaviour you are looking for is the same as background-attachement:fixed;.

Although this solution is pretty simple and doesn't rely on JS, from a sematic point of view it shouldn't be recommended.

The point is to use 2 background images with background-attachement: fixed; and to position the link over them for interaction. It will give you the desired behaviour with a smooth color change according to the background color :

DEMO

header, article,footer,body{
    background:#fff url('http://i.imgur.com/oHIZBHL.png') no-repeat;
    background-attachment: fixed;
    background-position:right 160px top 10px;
}
body{
    padding:0 150px 1000px;
    background-color:lightgrey;
}
header,footer{
    background-image:url('http://i.imgur.com/79IWeQK.png');
    background-color:#F97D9F;
    height:125px;
}
article{
    height:500px;
}
nav a{
    position:fixed;
    top:10px; right:160px;
    width:150px; height:50px;
}
<nav><a href="#" title="menu"></a></nav>
<header></header>
<article></article>
<footer></footer>
web-tiki
  • 99,765
  • 32
  • 217
  • 249
2

If you don't want to have to manage duplicate elements to pull off this effect with CSS clip as per Antony's solution, then you can use a couple jQuery plugins:

  • jq-clipthru - This is a superflexible plugin that can probably do everything you want (and a lot more), but it also requires the jQuery UI library. [Demo]

  • Unobscure Text - This is my very lightweight plugin that has a very specific use case, but it's not compatible with jQuery 3. [Demo]

If you require jQuery 3 support and don't care for IE 11 and below, then you can use a solution based on SVG clip-path, like this.

thdoan
  • 18,421
  • 1
  • 62
  • 57
1

EDIT: proven that it does NOT work on Firefox, so probably this is just a Chrome quirk, still interesting behaviour so I'll leave this answer if anyone just wants to build a Chrome only implementation or when Firefox and other browsers come along.

There, I "fixed" it (pun intended :) )

Look at this working fiddle: JSFiddle example of fixed, relative and overflow working together

You can use relative parents with overflow to mimic the effect of the changing color.

Downside: you have to duplicate your menu (which is semantically well, just plain wrong). You could use some basic javascript to do the duplication of the menu though, that would improve it a little. I also only tested this on Chrome, but it seems really basic CSS so I imagine this will work on any browser / device.

Code snippets (the relevant parts)

HTML

<div class="topbar">
    <h1>Whoo pink!</h1>
    <div class="fixed-menu">Fixed!</div>
</div>
<div class="loads-of-content">
    <div class="fixed-menu">Fixed!</div>
</div>

CSS

.topbar {
    position: relative;
    overflow: hidden;
    z-index: 3;
}

.topbar .fixed-menu {
    color: red;
}
.fixed-menu {
    position: fixed;
    top: 20px;
    right: 50px;
}
.loads-of-content {
    position: relative;
    overflow: hidden;
}
Justus Romijn
  • 15,699
  • 5
  • 51
  • 63
  • This actually doesn't work in Firefox. `overflow: hidden;` rarely goes well with `position: fixed;` since that takes the element out of the flow. – Antony Dec 15 '14 at 16:50
  • Oh what a shame. I was actually surprised that it worked in Chrome. Yes, I know that position fixed takes an element out of the flow, but then a relative parent does make some kind of sense (because it works that way with absolute positioned elements as well). – Justus Romijn Dec 15 '14 at 16:53
  • Yeah, back to clipping then: http://stackoverflow.com/questions/12463658/parent-child-with-position-fixed-parent-overflowhidden-bug – Justus Romijn Dec 15 '14 at 17:01
  • 2
    That is exactly the thing I have in [here](http://jsfiddle.net/8jjz7qqz/). An ugly solution with limited browser support. – Antony Dec 15 '14 at 17:05
  • Oh man this looks so great also in Safari. Unfortionally not in Firefox... :( – bench-o Dec 15 '14 at 18:18