Detect When Mouse Enters Specific Area of Document (Not a Div Element) - javascript

I'm trying to figure out how Medium made their bottom action / menu bar slide up when your mouse enters the bottom of the document. The slide up effect is not triggered by moving the mouse over the invisible div (it slides up & down via transform translateY).
Besides, the menu bar is only 44px in height, but its is-visible class gets triggered way before your mouse is near it — but by what? When using Inspect Element, I can't see any hidden divs that could be triggering it..
I've searched for countless of ways, e.g. "show element when mouse enters specific part of document" but all search results involve when the mouse enters or moves over a div element, which is not the solution I'm looking for.
Obviously, you can solve this problem by putting the slide up menu inside a hidden container like I've done here, and then you get the desired result:
(function() {
var actionBar = document.querySelector('.action-bar');
var actionBarWrapper = document.querySelector('.action-bar-detection');
function showDiv() {
actionBar.classList.add('js-is-visible')
}
function hideDiv() {
actionBar.classList.remove('js-is-visible')
}
actionBarWrapper.onmouseover = showDiv;
actionBarWrapper.onmouseout = hideDiv;
})();
* {
margin: 0;
padding: 0;
box-sizing: border-box;
line-height: 1.5;
}
body {
height: 100%;
}
.wrapper {
width: 90%;
max-width: 600px;
margin: 5% auto;
}
.action-bar {
position: absolute;
left: 0;
bottom: 0;
border: 1px solid #252321;
background: #fff;
padding: 16px;
width: 100%;
min-height: 50px;
opacity: 0;
transform: translateY(100%);
transition: all .5s;
z-index: 99;
}
.action-bar-detection {
height: 150px;
width: 100%;
opacity: 1;
position: fixed;
bottom: 0;
left: 0;
z-index: 1;
}
.js-is-visible {
opacity: 1;
transform: translateY(0%);
}
<html>
<head>
<meta charset="UTF-8" />
<title>Document</title>
</head>
<body>
<div class="wrapper">
<p>When mouse enters the hidden action bar element, slides up.</p>
<p>But it's only happening because the action-bar is inside an invisible detection layer class (action-bar-detection) with a height of 150px.</p>
</div>
<div class="action-bar-detection">
<div class="action-bar">
Bottom Menu
</div>
</div>
</body>
</html>
However, this doesn't seem to be what Medium have done, and if this can be done without adding more HTML & CSS, I want to learn how! :-)
I think I'm not phrasing the problem correctly, since I can't find any solutions even remotely close (I've searched A LOT).
Any advice? What should I read up on? :-)

Get height of viewport, track onmousemove, and compare clientY from the mouse event to the viewport height:
(function() {
var actionBar = document.querySelector('.action-bar');
var viewHeight = window.innerHeight - 150;
function toggleDiv(e) {
if (e.clientY >= viewHeight) {
actionBar.classList.add('js-is-visible');
} else {
actionBar.classList.remove('js-is-visible');
}
}
window.onmousemove = toggleDiv;
})();
* {
margin: 0;
padding: 0;
box-sizing: border-box;
line-height: 1.5;
}
body {
height: 100%;
}
.wrapper {
width: 90%;
max-width: 600px;
margin: 5% auto;
}
.action-bar {
position: absolute;
left: 0;
bottom: 0;
border: 1px solid #252321;
background: #fff;
padding: 16px;
width: 100%;
min-height: 50px;
opacity: 0;
transform: translateY(100%);
transition: all .5s;
z-index: 99;
}
.action-bar-detection {
height: 150px;
width: 100%;
opacity: 1;
position: fixed;
bottom: 0;
left: 0;
z-index: 1;
}
.js-is-visible {
opacity: 1;
transform: translateY(0%);
}
<div class="wrapper">
<p>When mouse comes within 150px of the bottom part of the screen, the bar slides up.</p>
<p>When the mouse leaves this defined area of the screen, the bar slides down.</p>
</div>
<div class="action-bar-detection">
<div class="action-bar">
Bottom Menu
</div>
</div>

You could do this by listening to the mousemove event on the document, you will want to invest effort into making this performant as it will be triggered frequently. The most common way to regulate events like this is through throttling.
Once you are hooked into the mousemove event you will need to get the Y coordinate of the cursor and compare that to the height of the window, if it is within a threshold then you can reveal your panel, once it moves out you can proceed to hide it again.
Here is an example showing a basic implementation jsFiddle
// Using underscore for the throttle function though you can implement your own if you wish
document.addEventListener('mousemove', _.throttle(mouseMoveEventAction, 200));
function mouseMoveEventAction(e) {
doPanelStuff(isInsideThreshold(e.clientY));
}
function doPanelStuff(isActive) {
var panelElement = document.querySelector('.panel');
if (isActive) {
panelElement.style.background = 'red';
} else {
panelElement.style.removeProperty('background');
}
}
function isInsideThreshold(cursorY) {
var threshold = 200;
var clientHeight = document.documentElement.clientHeight;
return cursorY > (clientHeight - threshold);
}
html, body {
height: 100%;
}
.container, .content {
height: 100%;
width: 100%;
}
.panel {
height: 50px;
position: absolute;
bottom: 0;
width: 100%;
background: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<div class="container">
<div class="content"></div>
<div class="panel"></div>
</div>

Related

How to Swap Two Divs With Animation

I have a project where I want a div to appear as a large box and three more to appear underneath as smaller boxes and when you click a smaller box, it switches sizes and places with the large box using css transitions to make the movement and size change smooth. Right now I'm attempting to use jQuery and the positioning is not working at all. Here's an example of what I have so far:
https://jsfiddle.net/v3pmhawj/1/
$(function () {
let { left: x1, top: y1 } = $('.full-size-card').offset()
$('.inactive-sheets .card').on('click', function() {
let { left: x2, top: y2 } = $(this).offset()
let curr = $('.full-size-card')
let diffX = x2 - x1
let diffY = y2 - y1
$(this).css({
left: -diffX,
top: -diffY
})
$(this).addClass('full-size-card')
curr.css({
left: diffX,
top: diffY
})
curr.removeClass('full-size-card')
})
})
If anyone has suggestions on ways that involve other libraries or other techniques, I'm all ears. I'd like to be able to move the divs around in the DOM as well but as far as I can tell, you can't css-transition them if you do that since the only way (I know of) is to delete and re-add a copy of the element where you want it in the DOM.
You can create animation effect using transitions only. To achieve this you will have to define width and height of your containers as well as top and left position of bottom elements.
On click, you just have to exchange classes of element that will become small and of element that will become large.
Here is fiddle of an example:
https://jsfiddle.net/fkd3ybwx/210/
HTML
<div class="card-container">
<div class="card large">A</div>
<div class="card small">B</div>
<div class="card small">C</div>
<div class="card small">D</div>
</div>
CSS
.card-container {
position: relative;
}
.card {
transition: all ease 1s;
position: absolute;
font-size: 24px;
border: white 4px solid;
box-sizing: border-box;
cursor: pointer;
}
.small {
width: 100px;
height: 100px;
background: blue;
left: 0;
top: 300px;
}
.small ~ .small {
left: 100px;
background: green;
}
.small ~ .small ~ .small {
left: 200px;
background: yellow;
}
.large {
width: 300px;
height: 300px;
background: red;
left: 0px;
top: 0px;
}
JavaScript
const smallCards = document.querySelectorAll('.card');
smallCards.forEach((smallCard) => {
smallCard.addEventListener('click', (event) => {
const largeCard = document.querySelector('.large');
largeCard.className = "card small";
event.target.className = "card large";
});
});

I want to create a zoom-in-down effect of a background which triggers only on scroll down and goes back on scroll up but does not do viceversa

I want to create my background circle so that it will cover my whole width of container but the problems that I am facing are:
I am using wheel method which I want to change with scroll method but won't able to give a exact scroll position to start scrolling scrollTop is using start of my website but I want to start my scroll after 3rd container div.
In wheel function my background zooms-in with wheel up as well as well down I only want it to work on scroll down.
I want my zoom to zoom out once it reached width of whole screen.
my code:
const zoomElement = document.querySelector(".zoom");
let zoom = 1;
const ZOOM_SPEED = 1;
document.addEventListener("wheel", function(e) {
if (e.deltaY > 0) {
zoomElement.style.transform = `scale(${zoom += ZOOM_SPEED})`;
} else {
zoomElement.style.transform = `scale(${zoom -= ZOOM_SPEED})`;
}
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
overflow-x: hidden;
}
.downarrow {
position: relative;
left: 50%;
font-size: 2rem;
color: #f0f0f0;
}
.zoom {
background: #282828;
width: 100px;
height: 100px;
border-radius: 50%;
position: relative;
left: 47.80%;
}
.container {
width: 100%;
height: 100vh;
}
<div class="container">
<div class="zoom"></div>
<p class="downarrow">↓</p>
</div>
I want to mimic this site downarrow effect:
https://lione.axiomthemes.com/

Pure CSS overlay scrolling

using only css and html, is it possible to scroll away the inner div (overlay red div) completely before scrolling down the rest of the page? Essentially, wondering if overlay scrolling while freezing the behind div is possible in only css? Then once the red div is gone, unfreeze the background scrolling and continue on. Similar to this site here: https://humaan.com/ . Or would some sort of JavaScript need to be used?
.headervideo{background-color:blue; width:100%; height:900px;}
.headerbreak{width:100%; height:300px;}
.headervideo #inner-box {
background-color: red;
height: 90%;
width: 100%;
}
<div class="headervideo">
<div id="inner-box"></div>
</div>
<div class="headerbreak">
<div>
position:sticky can approximate this:
.headervideo {
background: url(https://picsum.photos/id/1064/800/800) center/cover;
height: 100vh;
position: relative;
z-index: 2;
}
.nextsection {
background: url(https://picsum.photos/id/107/800/800) center/cover;
height: 100vh;
margin-top: -100vh;
position: sticky;
top: 0;
}
.container {
height:200vh;
}
body {
margin: 0;
}
<div class="container">
<div class="headervideo"></div>
<div class="nextsection"></div>
</div>
<div style="height:150vh"> more content later </div>
With CSS, you could use the hover event to detect a certain scroll position (e.g. on something just after the red div), but this would not work on touch only devices like mobile phones. It also wouldn't be reliable, as the cursor could be anywhere on the screen.
Using JavaScript to detect scroll position would be necessary. However, you could use the JavaScript only to add a class at different scroll positions and then do the rest with CSS. Here's a simple example:
var red = document.querySelector('#inner-box');
var begin = red.scrollTop;
var end = begin + red.clientHeight;
console.log(begin)
document.body.classList.add('in');
window.addEventListener("scroll", (event) => {
if(this.scrollY < begin) {
document.body.classList.add('before');
document.body.classList.remove('after');
document.body.classList.remove('in');
} else if(end < this.scrollY) {
document.body.classList.remove('before');
document.body.classList.add('after');
document.body.classList.remove('in');
} else {
document.body.classList.remove('before');
document.body.classList.remove('after');
document.body.classList.add('in');
};
});
.headervideo {
background-color: blue;
width: 100%;
height: 900px;
}
.headerbreak {
width: 100%;
height: 300px;
}
.headervideo #inner-box {
background-color: red;
height: 90%;
width: 100%;
}
body {
}
body.before {
background-color: lightgreen;
}
body.in {
background-color: lightpink;
}
body.after {
background-color: lightblue;
}
<body>
<div class="headervideo">
<div id="inner-box"></div>
</div>
<div class="headerbreak">
<div>
</body>

scroll the content inside the div with fixed position using browser/page scroll bar

I have some div's with position:fixed all around the page.
one of that div has little more long content.
my aim is that I want to scroll the content inside that box, using the main browser/page scroll-bar. (its not normal overflow:auto like this)
this is the exact situation
http://s7.postimage.org/d6xl1u9mz/sample.jpg
is any plugin available ?
Without knowledge of your HTML:
<body>
<section id="bodyContent"></section>
<header></header>
<section id="lSide"></section>
<section id="rSide"></section>
</body>
#bodyContent {
box-sizing: border-box;
-moz-box-sizing: border-box;
min-height: 100%;
width: 100%;
padding: 90px 45px 0px 105px;
background-clip: content-box;
background-color: #FFFFFF;
}
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
background-image: url(page_background.jpg);
background-attachment: fixed;
}
header, #lSide, #rSide {
position: fixed;
}
header {
top: 0;
width: 100%;
height: 90px;
background-image: url(page_background.jpg);
background-attachment: fixed;
}
#lSide {
left: 0;
top: 0;
height: 100%;
width: 105px;
padding: 90px 0 0 0;
}
#rSide {
right: 0;
top: 0;
height: 100%;
width: 45px;
padding: 90px 0 0 0;
}
This will force the contents of #bodyContent to sit inside the opening between the various border elements, and it will cause any overflow to trigger a scrollbar on the body element as you desire. JSFiddle
Maybe it's possible. I've created a jsFiddle which does the trick. It's not perfect, but you can develope it further... Also this snippet works only with modern browsers, but is easy to fix for older IEs too. Core code below.
JavaScript:
window.onload = function () {
var content = document.getElementById('contentwrapper'),
dimdiv = document.getElementById('scrollingheight'),
wrapHeight = document.getElementById('fixed').offsetHeight,
scroller = function () {
content.style.top = -(document.documentElement.scrollTop || document.body.scrollTop) + 5 + 'px';
return;
};
dimdiv.style.minHeight = (content.scrollHeight - wrapHeight + 2 * 5) + 'px';
window.addEventListener('scroll', scroller, false);
return;
}
CSS:
#fixed {
position: fixed;
min-width: 300px;
min-height: 200px;
max-height: 200px;
background: #fff;
left: 150px;
top: 200px;
overflow-y: hidden;
border: 1px solid #000;
}
#contentwrapper {
max-width: 290px;
position: absolute;
top: 5px;
left: 5px;
}
#scrollingheight {
position: absolute;
top: 100%;
visibility: hidden;
min-width: 1px;
}
HTML:
<div id="scrollingheight"></div>
<div id="fixed">
<div id="contentwrapper">
content
</div>
</div>
Notice, that all content in the body, but #scrollingheight, must be fixed. Constant 5 is related to #contentwrapper's top value.
AFAIK you cannot do that.
At least not without some wicked JS trickery.
Why? cause you cannot force the browser's default scrollbar height (make it smaller) to embrace some content that is inside a totally different area than the html, body (document).
My suggestion is that you build a custom scrollbar, calculate the height of your nice overflow hidden area, add it to the scrollable ratio calculation.

Prevent scrolling when videobox is on

I'm using videobox to embed streams into my site, and I just discovered that when videobox is "on"- i.e. I clicked on a link that brings it up and dims everything around it- I can still scroll down and see the rest of my (non-dimmed) site. This breaks immersion, and I'd like to disable the scrolling, but only for when the videobox is on.
I have no idea where to start though.
You can't do this just with JavaScript, as far as I know, as the onscroll event is not cancelable.
You can achieve this by positioning everything in a container div with a height and width of 100% and disabling overflow on html and body elements, so you actually get the scrollbars on the container div. When your videobox is on, you can turn on an overlay that hides everything behind it (including the scrollbars on the container) and display the videobox on top of it.
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8>
<title>Prevent scrolling</title>
<style>
* { padding: 0; margin: 0; border: 0 }
html, body {
height: 100%;
overflow: hidden;
}
#container {
position: absolute;
height: 100%;
width: 100%;
overflow: auto;
}
#large-div {
background: #aaa;
height: 5000px;
width: 5000px;
}
#overlay {
position: absolute;
background: #fff;
opacity: 0.7;
-moz-opacity: 0.7;
-webkit-opacity: 0.7;
-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=70)";
filter: alpha(opacity=70);
height: 100%;
width: 100%;
z-index: 1000;
display: none;
}
#videobox-container {
position: absolute;
background: #dd8;
width: 600px;
height: 400px;
top: 50%;
left: 50%;
margin: -300px 0 0 -200px;
z-index: 1001;
display: none;
}
</style>
</head>
<body>
<div id="container">
<div id="large-div"></div>
</div>
<div id="overlay"></div>
<div id="videobox-container"></div>
<script>
function showVideoBox() {
// show both overlay and videobox-container
document.getElementById("overlay").style.display = "block";
document.getElementById("videobox-container").style.display = "block";
}
showVideoBox();
</script>
</body>
</html>
(You'll have to fiddle a bit with the positions of your elements, but you get the idea.)
The easy solution is to add the css body{overflow:hidden;} when the video starts playing and after that remove it. Also, can you not put the video box in a div tag and set its position to fixed?
in videobox.js
replace line 80
this.overlay.setStyles({'top': window.getScrollTop()+'px', 'height': window.getHeight()+'px'});
with this:
this.overlay.setStyles({top:-$(window).getScroll().y,height:$(window).getScrollSize().y+$(window).getScroll().y});
Essentially this gets the height of the 'y' scroll and rather than just what the screen is showing.

Categories

Resources