My layout looks almost identical to this codepen.
.parent {
color: white;
padding: 70px;
position: relative;
background-color: #0074d9;
margin-top: 50px;
}
.element {
background-color: lighten(#0074d9, 20);
opacity: .85;
padding: 20px;
color: rgba(255,255,255,.9);
position: fixed;
bottom: 0;
left: 0;
right: 0;
}
The codepen works the right way, so it's been hard to come up with a demonstrable example.
When my cursor is positioned over the fixed "child element" div, I want to be able to scroll the parent but not be able to clickthrough.
The common answer seems to be "pointer-events: none", but that allows click interaction with the page below.
Open to other suggestions or explanations as to why it works in the codepen, but doesn't outside of it.
The solution that worked for me was to use jquery to grab the parent by id and add my deltaY to its scrollTop.
<div
onWheel={(e) => {
const component = $(`#content`);
const contentScrollPosition = component.scrollTop();
component.prop("scrollTop", contentScrollPosition + e.deltaY);
}}
</div>
This allows me to scroll the parent even when my cursor is on the fixed-position div.
Unfortunately, mobile doesn't work well. First, you would need to track the touch event though onTouchStart, End, and Move. Even then, you lose touch scroll momentum which makes it feel too unnatural.
Related
I need to synchronise the scroll of two elements: A content area and a "header".
But when I used the scroll event to change the position of the other element with a CSS transform, there was a notable delay between the user scrolling and the header moving, which is distracting, specially on Chrome.
However, when comparing to other applications that use the same method for synchronising the scroll position, they didn't have it. After many hours trying to find why, I've finally found it: they have an invisible overlay that covers the whole scroll area, which is also not interactive (CSS pointer-events: none)
I've prepared a playground below, where the overlay is visible and doesn't cover the whole area so it can be compared easily.
At least this behaviour is happening in my machine, a Mac with Chrome, and I scroll through the trackpad gesture. There also needs to be some load, so this snippet also adds a 30ms blocking loop every 100ms to simulate one
const content = document.getElementById("content");
const header = document.getElementById("header");
// Add content
const helloes = new Array(100).fill("Hello!").join("<br /><br />");
header.innerHTML = content.innerHTML = helloes;
// Synchronize scroll of content to header by adding `transform`
content.addEventListener("scroll", (evt) => {
header.style.transform = `translateY(${-content.scrollTop}px)`;
});
// Simulates performance hit, either low-end computer or a component doing extra load
setInterval(() => {
let start = Date.now();
while (Date.now() < start + 30);
}, 120);
.main {
position: relative;
display: flex;
}
.main>div {
overflow: auto;
height: 200px;
padding: 10px;
border: 1px solid black;
border-radius: 5px;
white-space: nowrap;
}
#content {
padding-right: 200px;
}
#overlay {
position: absolute;
left: 160px;
top: 50px;
width: 100px;
height: 100px;
box-sizing: border-box;
background: rgba(0, 0, 0, 0.3);
pointer-events: none;
}
<div class="main">
<div>
<div id="header">
</div>
</div>
<div id="content">
</div>
<div id="overlay"></div>
</div>
As it can be quite tricky to reproduce, I've recorded this behaviour in this image
It's quite subtle, but as you can see, when I'm scrolling with the mouse over the scroll area, there's a visible lag. If I'm scrolling instead on top of the uninteractive overlay, the scroll synchronization is perfect.
However, I totally can't understand why this happens - What is the reason adding an overlay and scrolling with the mouse on top of it improves the performance?
I need to make a scrollable div, scroll even if the mouse is upon the content (inside the scrollable div), and not just beside it (Where it is blank). This is what I have so far:
var main = document.getElementById('main-site');
var maxTop = main.parentNode.scrollHeight-main.offsetHeight;
main.parentNode.parentNode.onscroll = function() {
main.style.top = Math.min(this.scrollTop,maxTop) + "px";
}
In Chrome is ok
In IE8+ is ok (i know a hack)
In Safari the content shakes a lot when i scroll, can i fix that? (I want fix this)
Working fiddle -> https://jsfiddle.net/8oj0sge4/6/
var main = document.getElementById('main-site');
var maxTop = main.parentNode.scrollHeight - main.offsetHeight;
main.parentNode.parentNode.onscroll = function() {
main.style.top = Math.min(this.scrollTop, maxTop) + "px";
}
#wrapper {
width: 100%;
height: 1500px;
border: 1px solid red;
padding-top: 380px;
}
#wrapper .container {
border: 1px solid green;
width: 100%;
height: 500px;
overflow: scroll;
}
#wrapper .container-scroll {
height: 1500px;
width: 100%;
border: 1px solid yellow;
position: relative;
}
#wrapper .main {
width: 200px;
height: 500px;
background: black;
overflow: scroll;
position: absolute;
color: white;
left: 50%;
margin-left: -100px;
margin-top: 10px;
}
<div id="wrapper">
<div class="container">
<div class="container-scroll">
<div id="main-site" class="main">
My goals is to make the div container scroll also when the mouse is hover this div in safari, in Google and IE8 i already know how to make work, but safari is shaking a lot!
</div>
</div>
</div>
</div>
Thank you guys.
I hope this demo helps you out to make the div content scroll when mouse hover and when mouse out of the div.
<html>
</head>
<style>
.mydiv
{height: 50px;width: 100px; overflow-y: scroll; }
</style>
<script>
function loadpage()
{ document.getElementById('marquee1').stop(); }
function marqueenow()
{ document.getElementById('marquee1').start(); }
</script>
</head>
<body onload="loadpage()">
<marquee id="marquee1" class="mydiv" onmouseover="marqueenow()" onmouseout="loadpage()" behavior="scroll" direction="up" scrollamount="10">
This is my test content This is my test content This is my test content This is my test content This is my test content This is my test content This is my test
content This is my test content This is my test content This is my test content This is my test content This is my test content This is my test content This is my test content This is my test content This is my test content
</marquee>
</body>
</html>
you just add this js file to get a smooth scrolling effect.
https://github.com/nathco/jQuery.scrollSpeed
live deomo
http://code.nath.co/scrollSpeed
Not 100% sure what you are up to but you can get the fixed position with css "fixed". It will stay where you put it. The following css fixes to the bottom of the page.
.fixed {
bottom: 0;
left: 0;
position: fixed;
right: 0;
top: auto;
}
There is already an answer on scroll position:
How to get scrollbar position with Javascript?
I don't know important is that content, and by this I mean if it needs to stay selectable.
If not a pretty good solution would be to use #wrapper .main{ pointer-events: none; }, meaning that the content will not get any events from mouse and it would go through it to the next element behind it - in your case the scroll would go dirrectly to #wrapper.
Safari does this because every browser has its own scrolling. If you have a fixed header on a phone it acts bouncy and if you do this on a PC it acts normal. Explorer scrolls smooth and Chrome scrolls right to the place without a smooth transition.
The reason why your #main-site is "jiggling" is because the browser keep "repaint" the position of this element.
One Trick to solve this is called Debounce Function, (you may also google it to see other variations.) The basic idea is to delay the scroll event handler to clear out those untriggered callbacks.
In your case, you may do something like this:
main.parentNode.parentNode.onscroll = function(event) {
debounce(offsetting, 10);
}
function offsetting() {
main.style.top = Math.min(main.parentNode.parentNode.scrollTop,maxTop) + "px";
}
function debounce(method, delay) {
clearTimeout(method._tId);
method._tId= setTimeout(function(){
method();
}, delay);
}
If you keep seeing the jiggling issue, you can simply edit the delay parameter (i.e, change 10 to 50). The downside for that is your #main-site element will be 'cut off the top` for a while, depending on your delay settings.
Since your code works perfectly on Chrome and IE, there might be a bug on scrollHeight or offsetHeight attribute on Safari. I recommend you to use getBoundingClientRect for calculating element position since this method is more reliable and accurate.
var maxTop = main.parentNode.getBoundingClientRect().height - main.getBoundingCLientRect().height;
I've found this JS fiddle, which does exactly what I'm looking for. However, I can't seem to figure out how to get it to work when I move the navigation to the side.
var hoverMenu = $('#HiddenMenu'),
hoverSpace = $('#HoverSpace');
hoverSpace.on('mousemove', function(event) {
if(35 - event.clientY < 0) {
hoverMenu.css({top: 35 - event.clientY});
} else {
hoverMenu.css({top: 0});
}
}).on('mouseout', function() {
hoverMenu.css({top: -35});
});
http://jsfiddle.net/PaZHH/1/ <-- this is working example of the clientX/Y event I'm wanting
I can't seem to implement this by using clientX & moving the navigation to the right hand side.
This is where I managed to get too http://jsfiddle.net/PaZHH/102/
Make this changes.
Add absolute positioning to hidden menu:
#HiddenMenu {
background-color: #e00;
position: absolute;
right: -35px;
}
Make the hover space position relative:
#HoverSpace {
position: relative;
background-color: #aeaeae;
overflow: hidden;
width: 45px;
height: 500px;
}
Now, you'll be fine. You can check the result here. It currently works by moving in from right, which is a bit different from you horizontal sample.
Or can someone tell me another way to do this (without using a plugin please)
I have a side nav on the left and some content to the right. Both elements are floated left.
I want the side nav to 'stick' when it gets to 125px from the top of the window, once scrolled down the page.
I feel like I'm close, but I'm definitely not there yet! With the code I have written, the side-nav 'jumps' up to the top of the window once we scroll to the correct window position and then it's going back to the position where I want it to 'stick'.
The other issue, is that once the .stick class is applied, the element is taken out of the document flow (because I'm applying a position: fixed;) and the .content moves over to the left to take it's place.
Here is a codepen: http://codepen.io/MandyMadeThis/pen/DLtsJ
** NOTE: It seems I can't recreate the side-nav 'jumping' to the top of the window on scroll, so I don't what that's all about, but I'd still like to know the correct way to do this without the content moving over to the left.**
If anyone can guide me in the right direction, that would be wonderful. Thank you.
Here is my markdown:
<body>
<nav>navigation content</nav>
<header>header content</header>
<div class="container clearfix">
<div class="side-nav">THIS IS THE THING I WANT TO STICK</div>
<div class="content">This needs to stay to the right of the .side-nav</div>
</body>
Here is the CSS:
.side-nav {
float: left;
position: relative;
top: -17px;
width: 23%;
margin-right: 2%;
}
.content {
width: 70%;
float: left;
}
.stick {
position:fixed;
top:0px;
margin-top: 125px;
}
And here is my jQuery:
var sn = $(".side-nav");
var pos = sn.position();
$(window).scroll(function() {
var windowPos = $(window).scrollTop();
if (windowPos >= pos.top - 125) {
sn.addClass("stick");
} else {
sn.removeClass("stick");
}
});
To fix this you simply need to add a margin-left to .content and remove float. This way the position of the content does not rely on the nav being there.
.content {
width: 70%;
margin-left: 25%;
}
http://codepen.io/anon/pen/jbCix
How can I be able to scroll article while having my mouse cursor over .header while still having .header clickable? If I set z-index: -1 to .header I'm able to scroll while having the cursor over .header, but it's no longer clickable.
Demo
Fiddle
HTML:
<div class="row">
<div class="off-canvas-wrap">
<div class="inner-wrap">
<div class="header">
I should be clickable
</div>
<article class="small-12 columns">
<div style="height:5000px">
</div>
</article>
</div>
</div>
</div>
CSS:
article {
overflow-y: auto;
}
article,
body,
html,
.off-canvas-wrap,
.off-canvas-wrap .inner-wrap,
.row {
height: 100%;
width: 100%;
}
.header {
position: absolute;
width: 400px;
height: 400px;
background: #000;
left: 50%;
top: 50%;
margin-left: -200px;
margin-top: -200px;
z-index: 1;
color: #fff;
text-align: center;
padding-top: 1em;
}
If you want a CSS solution, there is none — this is because of how mouse events are directly related to the visibility of the item to the pointer/cursor: e.g. if you place .header in the back such that it is not accessible (so that scroll events on article can be triggered), it will not register a click event, too.
A JS-based solution would be listening to the mousewheel() event (with this plugin, available as a CDN-hosted plugin, too) and then manually triggering scrolling on the article element. However, this does not replicate the default scrolling behavior on individual OSes, and may appear choppy on OSes that has smoothed scrolling events (like OS X).
Without further ado:
// Cache article's position from top (might change if the page is loaded with a hash, so we cannot declare it as 0)
var fromTop = $('article').scrollTop();
$('.header').mousewheel(function(e,d) {
// Prevent default scrolling behavior, even when .header is overflowing
e.preventDefault();
// Trigger scroll in window
// You can change how much to amplify the 'd', which is the delta (distance registered from the scrollwheel). I have chosen it to multiply it by 10
fromTop = fromTop - d*10;
$(this).next('article').scrollTop(fromTop);
}).click(function() {
// Just testing
alert('Header is clicked on!');
});
Here is the proof-of-concept JSFiddle: http://jsfiddle.net/teddyrised/CK7z8/2/
Warning: In the event that there are multiple .header elements targeting multiple article elements on the same page, you will have to iterate through each .header-article pair and cache the fromTop separately for each pair.