I have a layout that contains a fixed
div (#navigation
) that has buttons. The layout also includes scrollable content (.card
).
#navigation
currently has a green background for demo purposes. Like so:
#navigation { position: fixed; top: 20px; left: 50%; transform: translate(-50%, 0%); background: green; padding: 25px; }
<div id="navigation"><button id="btn1">Button</button> <button id="btn2">Button</button> <button id="btn3">Button</button> <button id="btn4">Button</button> </div>
I would like to hide the any part of any .card
element as soon as it goes behind the green background. So, I use z-index stacking order and it works well. Like so:
#card-wrapper { width: 250px; margin: 100px auto; } .card { height: 200px; width: 200px; background: #131418; margin: 1em auto; display: inline-block } #navigation { position: fixed; top: 20px; left: 50%; transform: translate(-50%, 0%); z-index: 1; background: green; padding: 25px; } #main { text-align: center; }
<div id="main"> <div id="navigation"><button id="btn1">Button</button> <button id="btn2">Button</button> <button id="btn3">Button</button> <button id="btn4">Button</button> </div> <div id="card-wrapper"> <div class="card"></div> <div class="card"></div> <div class="card"></div> <div class="card"></div> <div class="card"></div> <div class="card"></div> </div> </div>
However, I would also like not to use the green background in production. This means that #navigation
should not have a background and only the buttons inside should be visible.
So my question is how do I hide the top-side overflow from #card-wrapper
as soon as it reaches the hypothetical green background?
#card-wrapper { width: 250px; margin: 100px auto; } .card { height: 200px; width: 200px; background: #131418; margin: 1em auto; display: inline-block } #navigation { position: fixed; top: 20px; left: 50%; transform: translate(-50%, 0%); z-index: 1; padding: 25px; border: 1px solid; background: transparent } #main { text-align: center; } body { margin: 0 auto; background: url(http://svgur.com/i/42T.svg); background-attachment:fixed; background-size:cover; }
<div id="main"> <div id="navigation"><button id="btn1">Button</button> <button id="btn2">Button</button> <button id="btn3">Button</button> <button id="btn4">Button</button> </div> <div id="card-wrapper"> <div class="card"></div> <div class="card"></div> <div class="card"></div> <div class="card"></div> <div class="card"></div> <div class="card"></div> </div> </div>
Note, the body
element has an SVG background, I cannot add any background to #navigation as it would look bad.
I am open to all solutions CSS/JS/jQuery as long as they do not involve hard-coded values
6 Answers
Answers 1
Try to set same background for #navigation also, with same background-position (see example below)
#card-wrapper { width: 250px; margin: 100px auto; } .card { height: 200px; width: 200px; background: #131418; margin: 1em auto; display: inline-block } #navigation { position: fixed; top: 0; left: 0; right: 0; z-index: 1; padding: 40px 25px 25px 25px; background: #ffffff url(http://svgur.com/i/42T.svg); background-attachment: fixed; background-size: cover; background-position: 0 0; } #main { text-align: center; } body { margin: 0 auto; background: #ffffff url(http://svgur.com/i/42T.svg); background-attachment: fixed; background-size: cover; background-position: 0 0; }
<div id="main"> <div id="navigation"> <button id="btn1">Button</button> <button id="btn2">Button</button> <button id="btn3">Button</button> <button id="btn4">Button</button> </div> <div id="card-wrapper"> <div class="card"></div> <div class="card"></div> <div class="card"></div> <div class="card"></div> <div class="card"></div> <div class="card"></div> </div> </div>
Answers 2
A JavaScript Solution
The spirit of the solution:
Tested on CH62, FF57 and IE11
While kastriotcunaku's answer is good if you don't mind the formatting, This solution maintain same parameters as those given, here I'll attempt to give the #navigation
a background that looks and is positioned as the body's to give the illusion of transparency using JavaScript to make it responsive.
Background-size:cover
on the #navigation
div will make it looks denser. So I'll try to emulate cover
by stretching the background to cover the whole body and still appearing only on the navigation element by :
navigationElement.style.backgroundSize=(document.body.clientHeight/imgH)*imgW+"px";
/* imgW image width, imgH original image height. A trick to maintain the width/height ratio of the SVG. You can programmatically
get size of the image
For some reason I've needed that even on the body for IE11 who doesn't seem to handle "cover" properly */
For the background positioning however, there are browser differences. Surprisingly enough IE11 is the most straightforward
n=document.getElementById('navigation'); var isChrome = !!window.chrome && !!window.chrome.webstore; var isIE = /*@cc_on!@*/false || !!document.documentMode; var imgH=647.7; var imgW= 2351.2; function resizeHandler(){ r=n.getBoundingClientRect(); n.style.backgroundSize=(innerHeight/imgH)*imgW+"px"; if (isChrome){ n.style.backgroundPosition=r.width/2+"px 0px" } else if (isIE){ n.style.backgroundPosition=0 document.body.style.backgroundSize=(innerHeight/imgH)*imgW+"px"; } else{ n.style.backgroundPosition=-r.x+"px -20px" } } addEventListener("resize",resizeHandler); document.addEventListener ("DOMContentLoaded",resizeHandler);
#card-wrapper { width: 250px; margin: 100px auto; } .card { height: 200px; width: 200px; background: #131418; margin: 1em auto; display: inline-block } #navigation { position: fixed; top: 20px; left: 50%; transform: translate(-50%, 0%); z-index: 1; padding: 25px; border: 0.1px dotted; background: #ffffff url(http://svgur.com/i/42T.svg); background-attachment: fixed; } #main { text-align: center; } body { margin: 0 auto; background:#ffffff url(http://svgur.com/i/42T.svg); background-attachment:fixed; background-size:cover; background-position:0; }
<div id="main"> <div id="navigation"> <button id="btn1">Button</button> <button id="btn2">Button</button> <button id="btn3">Button</button> <button id="btn4">Button</button> </div> <div id="card-wrapper"> <div class="card"></div> <div class="card"></div> <div class="card"></div> <div class="card"></div> <div class="card"></div> <div class="card"></div> </div> </div>
Answers 3
I really, really, really, really wanted to find a solution using some new CSS features like clip
or backdrop-filter
. In the end, I found a solution but it definitely comes up short, and isn't as elegant as kastriotcunaku's solution.
The solution below uses the experimental backdrop-filter
property to make the content behind a #screen
element have opacity(0)
. Unfortunately, there are downsides to this:
- Requires a few lines of JavaScript to set the dimensions of the
#screen
element to match the#navigation
element - this could be avoided if the width/height/position of the#screen
element was known in advance - Will only work in browsers supporting the
backdrop-filter
property - mainly Safari, or in Chrome with the Experimental Web Platform features flag enabled (which you can set by going to chrome://flags/#enable-experimental-web-platform-features in Chrome)
The end result is:
If you're using a browser with support for backdrop-filter
you can view it in action below:
var screen = document.getElementById("screen"); var navigation = document.getElementById("navigation"); var navigationStyle = window.getComputedStyle(navigation, null); screen.style.height = navigationStyle.getPropertyValue('height'); screen.style.width = navigationStyle.getPropertyValue('width'); screen.style.border = navigationStyle.getPropertyValue('border'); screen.style.borderColor = "transparent";
#card-wrapper { width: 250px; margin: 100px auto; } .card { height: 200px; width: 200px; background: #131418; margin: 1em auto; display: inline-block } #screen, #navigation { position: fixed; top: 20px; left: 50%; transform: translate(-50%, 0%); padding: 25px; } #screen { backdrop-filter: opacity(0%); -webkit-backdrop-filter: opacity(0%); } #navigation { z-index: 10; border: 1px solid; } #main { text-align: center; } body { margin: 0 auto; } .bkg { position: fixed; z-index: 10; top: 0; left: 0; width: 100%; height: 100%; background: url(http://svgur.com/i/42T.svg); background-attachment:fixed; background-size:cover; }
<div id="main"> <div id="screen"></div> <div class="bkg"></div> <div id="navigation"> <button id="btn1" onclick="alert()">Button</button> <button id="btn2">Button</button> <button id="btn3">Button</button> <button id="btn4">Button</button> </div> <div id="card-wrapper"> <div class="card"></div> <div class="card"></div> <div class="card"></div> <div class="card"></div> <div class="card"></div> <div class="card"></div> </div> </div>
If the width/height of the #screen
and #navigation
are fixed we do away with the JavaScript.
#card-wrapper { width: 250px; margin: 100px auto; } .card { height: 200px; width: 200px; background: #131418; margin: 1em auto; display: inline-block } #screen, #navigation { position: fixed; top: 20px; left: 50%; transform: translate(-50%, 0%); padding: 25px; width: 280px; height: 71px; box-sizing: border-box; } #screen { backdrop-filter: opacity(0%); -webkit-backdrop-filter: opacity(0%); } #navigation { z-index: 10; border: 1px solid; } #main { text-align: center; } body { margin: 0 auto; } .bkg { position: fixed; z-index: 10; top: 0; left: 0; width: 100%; height: 100%; background: url(http://svgur.com/i/42T.svg); background-attachment:fixed; background-size:cover; }
<div id="main"> <div id="screen"></div> <div class="bkg"></div> <div id="navigation"> <button id="btn1" onclick="alert()">Button</button> <button id="btn2">Button</button> <button id="btn3">Button</button> <button id="btn4">Button</button> </div> <div id="card-wrapper"> <div class="card"></div> <div class="card"></div> <div class="card"></div> <div class="card"></div> <div class="card"></div> <div class="card"></div> </div> </div>
Answers 4
You can use your navigation as fixed navigation and only allow scroll to your content area. For that you have to add position: absolute;
to #card-wrapper
and remove display:inline-block
from .card
.
#card-wrapper { position: absolute; top: 100px; bottom: 0; left: 0; right: 0; overflow: auto; } .card { height: 200px; width: 200px; background: #131418; margin: 1em auto; clear:both; } #navigation { position: fixed; top: 20px; left: 50%; transform: translate(-50%, 0%); z-index: 1; padding: 25px; border: 1px solid; background: transparent } #main { text-align: center; } body { margin: 0 auto; background: url(http://svgur.com/i/42T.svg); background-attachment:fixed; background-size:cover; }
<div id="main"> <div id="navigation"><button id="btn1">Button</button> <button id="btn2">Button</button> <button id="btn3">Button</button> <button id="btn4">Button</button> </div> <div id="card-wrapper"> <div class="card"></div> <div class="card"></div> <div class="card"></div> <div class="card"></div> <div class="card"></div> <div class="card"></div> </div> </div>
Answers 5
After a nice bit of trial and errors, I've got this solution that I think it's the best so far
#card-wrapper { width: 250px; margin: 100px auto; } .card { height: 200px; width: 200px; background: #131418; margin: 1em auto; display: inline-block } #navigation { position: fixed; top: 20px; left: 25%; right:25%; z-index: 1; padding: 25px; border-top: 0.1px dotted; border-bottom: 0.1px dotted; background: white url(http://svgur.com/i/42T.svg) top left/cover no-repeat fixed; } #main { text-align: center; } body { margin: 0 auto; background: white url(http://svgur.com/i/42T.svg) top left/cover no-repeat fixed; }
<div id="main"> <div id="navigation"> <button id="btn1">Button</button> <button id="btn2">Button</button> <button id="btn3">Button</button> <button id="btn4">Button</button> </div> <div id="card-wrapper"> <div class="card"></div> <div class="card"></div> <div class="card"></div> <div class="card"></div> <div class="card"></div> <div class="card"></div> </div> </div>
Answers 6
If you don't mind to have fixed value for the height
of the buttons - then your task can be solved through background-clip
style. In example below I've also added click handlers for cards and buttons to demonstrate that cards are still clickable.
$('.card').on('click',function(){ $(this).addClass('clicked'); }) $('button').on('click',function(){ $('.card').removeClass('clicked'); })
#card-wrapper { width: 250px; margin: 100px auto; } .card { height: 200px; width: 200px; background: #131418; margin: 1em auto; display: inline-block } #navigation { background-image: url(http://svgur.com/i/42T.svg); background-attachment: fixed; background-size: cover; background-clip: content-box; border: 0 none; padding: 0; top: 0; left: 0; bottom: 0; right: 0; position: fixed; z-index: 1; box-sizing: border-box; /** calc() calculation values: 25px - .buttons-list padding 20px - top offset of #navigation (it is also padding-top value) 30px - button height; */ padding: 20px 0 calc(100vh - 25px * 2 - 20px - 30px); pointer-events: none; } button { height: 30px } #main { text-align: center; } body { margin: 0 auto; background: url(http://svgur.com/i/42T.svg); background-attachment:fixed; background-size:cover; } .buttons-list { padding: 25px; pointer-events: initial; } /* Just for click events demonstration */ .card.clicked{ background-color: green; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <div id="main"> <div id="navigation"> <div class="buttons-list"> <button id="btn1">Button</button> <button id="btn2">Button</button> <button id="btn3">Button</button> <button id="btn4">Button</button> </div> </div> <div id="card-wrapper"> <div class="card"></div> <div class="card"></div> <div class="card"></div> <div class="card"></div> <div class="card"></div> <div class="card"></div> </div> </div>
0 comments:
Post a Comment