Detect when mouse leaves via top of page with jquery - javascript

This Jquery problem has been bugging me for a while now. I developed a script, with one function detecting when the mouse leaves via the top of the page. Here is the code:
$(document).bind("mouseleave", function(e)
{
console.log(e.pageY);
if (e.pageY <= 1)
{
now = new Date();
for (i=0; i < times.length; i++)
{
if (now.getTime() > times[i][0] && now.getTime() < times[i][1])
{
$.fn.colorbox({iframe:true, width:650, height:600, href: "work.html", open: true});
}
}
}
});
This works perfectly for me in all browsers. For some reason it works randomly in Chrome and seemingly not at all in Firefox for a friend that tested the site. In my browser (firefox 3.5.3), e.pageY is logged in the console box as a number near 0, however in my friends browser (also firefox 3.5.3) the lowest value is around 240. I have no idea why this is happening considering identical browsers. Does anyone have a clue as to how to debug this, or another more reliable method to detect when the mouse goes out of the webpage via the top? I hope this makes sense.

The problem appears if your window scrolls down, add a bunch of <br/>s to your page and scroll down one line and you'll see it.
So instead of looking to see if e.pageY <=1, subtract out the scrollTop:
if (e.pageY - $(window).scrollTop() <= 1)
{
// do something
}

I used another technic, almost works for all browsers. The trick is using $("body") or $(window).
$(window) do not work for IE, but $("body") works partially for FF as the body might not fill the whole window.
Here's the full page code:
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script><script>
var mouseX = 0;
var mouseY = 0;
var theFrame;
$(function() {
theFrame = $("body"); //$(window) for non-IE
theFrame.mousemove( function(e) {
//horizontal distance from edge
mouseX = Math.min(theFrame.width() - e.pageX, e.pageX);
//vertical distance from top
mouseY = e.pageY;
$("#mx").html(mouseX);
$("#my").html(mouseY);
});
theFrame.mouseout(function() {
if(mouseY<=mouseX)
$("#in_out").html("out-top");
else
$("#in_out").html("out");
});
theFrame.mouseover(function() {
$("#in_out").html("in");
});
});
</script>
</head>
<body>
<span id="in_out"></span>
<br />Hor: <span id="mx"></span>
<br />Ver: <span id="my"></span>
</body>
</html>

$(document).on('mouseleave', leaveFromTop);
function leaveFromTop(e){
if( e.clientY < 0 ) // less than 60px is close enough to the top
alert('y u leave from the top?');
}
This doesn't work well on older IE version, because those versions don't report the mouse position as should, but it's good enough.

Here is a vanilla JS solution if you just want something light weight that doesn't need to work in EI
/**
* Trigger an event when the cursor leaves the top of the window
* #param {*} threshold how close does it need to be to the top
* #param {*} cb callback function to trigger
*/
function onExit (threshold, cb) {
threshold = threshold || 60
var hasExited = false
document.addEventListener('mouseout', function (e) {
if (e.clientY < threshold && e.movementY < 0 && !hasExited) {
hasExited = true
cb(e)
}
})
}
Example Usage:
onExit(20, function() {
console.log('Mouse has left the top of the window!')
}

In order to detect mouseleave without taking in account the scroll bar and the autcomplete field or inspect :
document.addEventListener("mouseleave", function(event){
if(event.clientY <= 0 || event.clientX <= 0 || (event.clientX >= window.innerWidth || event.clientY >= window.innerHeight))
{
console.log("I'm out");
}
});
Conditions explanations:
event.clientY <= 0 is when the mouse leave from the top
event.clientX <= 0 is when the mouse leave from the left
event.clientX >= window.innerWidth is when the mouse leave from the right
event.clientY >= window.innerHeight is when the mouse leave from the bottom
Just keep
event.clientY <= 0
If you only want to detect exit on top

Related

How to detect mouseleave when mouse button is being held and cursor is out the viewport?

I need to detect when an user move out the mouse outside the view-port (example mouse is on browser address bar) even when the mouse button is being held.
As you can see from the code below, I am able to detect it using mouseout and mouseleave but when keeping the mouse button hold and moving out of the view-port these events are not fired.
Any idea how to solve this issue?
I target FF and Chrome latest version.
http://jsbin.com/gesehoneri/edit?html,output
document.addEventListener('mouseout', function () {
console.log('mouseout');
})
document.addEventListener('mouseleave', function () {
console.log('mouseleave');
})
Try this:
document.addEventListener('mousemove', function(e) {
var top = e.pageY;
var right = document.body.clientWidth - e.pageX;
var bottom = document.body.clientHeight - e.pageY;
var left = e.pageX;
if (top < 10 || right < 10 || bottom < 10 || left < 10) {
console.log('Mouse is out the viewport!');
}
});
body,
html {
height: 100%;
}
With this code, if you press the button inside the window, hold it and move the mouse outside the window, it logs the text. Does this help you?
function myFunctioName(e){
if(e.pageY < 0 || e.pageY > window.innerHeight) {
console.log("outside window vertical");
};
if(e.pageX < 0 || e.pageX > window.innerWidth) {
console.log("outside window horizontal");
};
}
window.addEventListener("mousemove", myFunctioName);
window.addEventListener("mousedown", myFunctioName);
Updated for use without JQuery and included both directions.

Animate element on scroll

I would like to animate a div when user scrolls the page.
For that, i implemented this code:
var slide = jQuery(".apresentacao-spc-01");
var opening = false;
var closing = false;
var pos = jQuery(window).scrollTop();
jQuery(window).scroll(function() {
var pos = jQuery(window).scrollTop();
console.log(pos);
if (pos > 100) {
if (!opening) {
opening = true; closing = false;
slide.stop().animate({
'opacity': 1,
'margin-left': '0px'
}, 700, function() {
opening = false;
});
}
} else {
if (!closing) {
closing = true; opening = false;
slide.stop().animate({
'opacity': 0,
'margin-left': '-1000px'
}, 500, function() {
closing = false;
});
}
}
});
The issue is:
Using "if (pos > 100) {", if the user resolution is big enough to show the element before he needs to scroll, he won't see the element unless he begins to scroll the page.
My question is:
How can I get a scroll animation that will be executed when the element is visible?
I mean: If the element is visible on page load, the animation automatically starts... If the element is not visible on page load, the animation waits the scroll reach the element to start...
Thanks.
There a few different things you could do. My first thought was to query the height of the viewport with something like this:
var viewportWidth = document.documentElement.clientWidth
, viewportHeight = document.documentElement.clientHeight
And then trigger the animation if it is taller than the distance the element is down.
A more dynamic solution would be to use a function that checks to see if the element is in viewport the automatically, that way you wouldn't need to worry about adjusting the height if you changed stuff on your page:
function isElementInViewport (el) {
var rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
);
}
credit to this response.
There is a use guide and further information in the link provided.
Good luck!

Automatic Scrolling Suddenly Stops

I've written the following script with the simple purpose of scrolling to the right when the user hovers over the right side of the screen and scrolling to the left when the user hovers over the left side of the screen. It works fine except that if you leave the mouse in the same spot for too long, then scrolling will stop before reaching the end. It begins scrolling again if you subsequently move the mouse. I can't understand why this is happening, since the code initiates an infinite timed loop which checks mouse position and scrolls accordingly. Its as if the mouse position stops being reported if the mouse is inactive for too long. Any ideas?
var mouseX = 0;
var scrollX = 0;
var timer;
$(document).ready(function() {
// Record the mouse position if the mouse is moved
$(document).mousemove(function(e) {
mouseX = e.pageX;
});
// Record the scroll position if the page is scrolled
$(document).scroll(function() {
scrollX = $(window).scrollLeft();
});
// Initiate the scrolling loop
scroll();
});
function scroll() {
// If the user is hovering over the right side of the window
if ((mouseX - scrollX) > 0.75*$(window).width()) {
scrollX += 1;
$(window).scrollLeft(scrollX);
}
// If the user is hovering over the left side of the window
if ((mouseX - scrollX) < (0.25*$(window).width())) {
scrollX -= 1;
$(window).scrollLeft(scrollX);
}
// Repeat in 5 ms
timer = window.setTimeout('scroll()', 5);
}
I don't know exactly what's wrong with your code, but why don't you use jQuery's animation?
It's more reliable than writing your own.
//inside $(document).ready():
var which = 0;
$('body').mousemove(function(e) {
var w_width = $(window).innerWidth();
var prc = (e.pageX - $(window).scrollLeft())/w_width;
var next_which = prc < 0.25 ? -1 : (prc > 0.75 ? 1 : 0);
if (next_which == which)
return;
which = next_which;
$('html,body').stop(true);
if (which != 0)
$('html,body').animate({scrollLeft: (which > 0 ? $(document).innerWidth()-w_width : 0)}, 2000);
}).mouseleave(function() {
$('html,body').stop(true);
which = 0;
});
​ ​
See fiddle
jQuery's mousemove() event fails to fire when e.pageX > $(window).width() (or thereabouts). Looks like a jQuery bug to me. That could be impeding your progress!

iScroll Scrolling Past Bottom?

You can easily see the problem on the first page here: http://m.vancouverislandlife.com/
Scroll down (slide up) and allow the content to leave the page, and it doesn't bounce back and is lost forever. However, on pages whose content does overflow the page and is therefore supposed to be scrollable, the scrolling works correctly (see Accomodations > b&b's and scroll down for an example of this).
I noticed that on my computer, the scrolling on the first page is always stuck at -899px. I can't find anybody else who's experienced this problem and no matter what I try, I just can't fix it! Help!
(It's not exactly urgent, however, as the target audience of iPhones and iPod Touches aren't affected by this since they have so little screen room.)
Okay, new problem. To solve the iScroll issue, I just created a custom script. However, it's not working correctly on the actual device. On desktop browsers, it works just fine. On mobile, it occasionally jumps back to the top and won't recognize some touches. This is probably because of the way I cancelled the default event and had to resort to a bit of a hack. How can I fix this? (Yup - simple problem for a +500 bounty. Not bad, huh?)
Here's the script, and the website is at the usual place:
function Scroller(content) {
function range(variable, min, max) {
if(variable < min) return min > max ? max : min;
if(variable > max) return max;
return variable;
}
function getFirstElementChild(element) {
element = element.firstChild;
while(element && element.nodeType !== 1) {
element = element.nextSibling;
}
return element;
}
var isScrolling = false;
var mouseY = 0;
var cScroll = 0;
var momentum = 0;
if("createTouch" in document) {
content.addEventListener('touchstart', function(evt) {
isScrolling = true;
mouseY = evt.pageY;
evt.preventDefault();
}, false);
content.addEventListener('touchmove', function(evt) {
if(isScrolling) {
evt = evt.touches[0];
var dY = evt.pageY - mouseY;
mouseY = evt.pageY;
cScroll += dY;
momentum = range(momentum + dY * Scroller.ACCELERATION, -Scroller.MAX_MOMENTUM, Scroller.MAX_MOMENTUM);
var firstElementChild = getFirstElementChild(content);
content.style.WebkitTransform = 'translateY(' + range(cScroll, -(firstElementChild.scrollHeight - content.offsetHeight), 0).toString() + 'px)';
}
}, false);
window.addEventListener('touchend', function(evt) {
isScrolling = false;
}, false);
} else {
content.addEventListener('mousedown', function(evt) {
isScrolling = true;
mouseY = evt.pageY;
}, false);
content.addEventListener('mousemove', function(evt) {
if(isScrolling) {
var dY = evt.pageY - mouseY;
mouseY = evt.pageY;
cScroll += dY;
momentum = range(momentum + dY * Scroller.ACCELERATION, -Scroller.MAX_MOMENTUM, Scroller.MAX_MOMENTUM);
var firstElementChild = getFirstElementChild(content);
content.style.WebkitTransform = 'translateY(' + range(cScroll, -(firstElementChild.scrollHeight - content.offsetHeight), 0).toString() + 'px)';
}
}, false);
window.addEventListener('mouseup', function(evt) {
isScrolling = false;
}, false);
}
function scrollToTop() {
cScroll = 0;
content.style.WebkitTransform = '';
}
function performAnimations() {
if(!isScrolling) {
var firstElementChild = getFirstElementChild(content);
cScroll = range(cScroll + momentum, -(firstElementChild.scrollHeight - content.offsetHeight), 0);
content.style.WebkitTransform = 'translateY(' + range(cScroll, -(firstElementChild.scrollHeight - content.offsetHeight), 0).toString() + 'px)';
momentum *= Scroller.FRICTION;
}
}
return {
scrollToTop: scrollToTop,
animationId: setInterval(performAnimations, 33)
}
}
Scroller.MAX_MOMENTUM = 100;
Scroller.ACCELERATION = 1;
Scroller.FRICTION = 0.8;
I think Andrew was on the right track with regards to setting the height of the #wrapper div. As he pointed out that,
that.maxScrollY = that.wrapperH - that.scrollerH;
Normally, this would work. But now that you've changed your #content to position: fixed, the wrapper element is no longer "wrapping" your content, thus that.wrapperH has a value of 0, things break.
Disclaimer: I did not go through the entire script so I may be wrong here
When manually setting a height to #wrapper, say 500px, it becomes,
that.maxScrollY = 500 - that.scrollerH;
The folly here is that when there's a lot of content and the window is small, that.scrollerH is relatively close in value to 500, say 700px. The difference of the two would be 200px, so you can only scroll 200 pixels, thus giving the appearance that it is frozen. This boils down to how you set that maxScrollY value.
Solution (for Chrome browser at least):
Since #wrapper effectively contains no content, we cannot use it in the calculations. Now we are left with the only thing that we can reliably get these dimensions from, #content. In this particular case, it appears that using the content element's scrollHeight yield what we want. This is most likely the one that has the expected behavior,
that.maxScrollY = that.scrollerH - that.scroller.scrollHeight;
scrollerH is the offsetHeight, which is roughly the height of what you see in the window. scroller.scrollHeight is the height that's considered scrollable. When the content does not exceed the length of the page, they are roughly equivalent to one another. That means no scroll. When there are a lot of content, the difference of these two values is the amount of scroll you need.
There is still a minor bug, and this looks like it's already there. When you have a lot of content, the last few elements are covered up by the bar when scrolled to the bottom. To fix this, you can set an offset such as,
that.maxScrollY = that.scrollerH - that.scroller.scrollHeight - 75;
The number 75 arbitrary. It's probably best if it's the height of the bar itself with 2 or 3 pixels for a bit of padding. Good luck!
Edit:
I forgot to mention last night, but here are the two sample pages that I used in trying to debug this problem.
Long page
Short page
This may be a CSS issue. In your stylesheet (mobile.css line 22), try removing position:fixed from #content.
That should allow the document to scroll normally (vertical scrollbar on a computer, "slideable" on a mobile browser).
Elements with position:fixed exit the normal flow of the document, their positioning is relative to the browser window. This is probably why you're having issues with scrolling. Fixed positioning is generally for elements which should always remain in the same place, even when the page is scrolled (ie. a notification bar "pinned" at the top of a page).
No definite solution, but more a direction I'd go for:
#wrapper and #content's overflow:hidden paired #content's postion:fixed and seem to be the cause of the issue.
If position: fixed is removed from #content, scrolling is possible but the "blank" divs are wrongly layered (tested in Firefox 5).
Your wrapper div seems to have a height of 0. So all the calculations are negative, setting it's height to the window height will correct the scroll issue. When I manually set the wrappers height via firebug and chromes debug bar the scroll functions as it should.
You #content div seems to have its size change on resize, probably a better idea to have the #wrapper div have its size change and then have #content inherit the size.
[Edit]
You don't believe me so codez, From iscroll-lite.js
refresh: function () {
var that = this,
offset;
that.wrapperW = that.wrapper.clientWidth;
that.wrapperH = that.wrapper.clientHeight;
that.scrollerW = that.scroller.offsetWidth;
that.scrollerH = that.scroller.offsetHeight;
that.maxScrollX = that.wrapperW - that.scrollerW;
that.maxScrollY = that.wrapperH - that.scrollerH;
In your page that translates to,
that.wrapperH = 0;
that.maxScrollY = -that.scrollerH
When a scroll finishes, this code gets called.
var that = this,
resetX = that.x >= 0 ? 0 : that.x < that.maxScrollX ? that.maxScrollX : that.x,
resetY = that.y >= 0 || that.maxScrollY > 0 ? 0 : that.y < that.maxScrollY ? that.maxScrollY : that.y;
...
that.scrollTo(resetX, resetY, time || 0);
See that that.maxScrollY > 0 ? ? If maxScrollY is negative then scrolling up will never bounce back.
I ended up just making my own, small script to handle the scrolling:
// A custom scroller
function range(variable, min, max) {
if(variable < min) return min > max ? max : min;
if(variable > max) return max;
return variable;
}
var isScrolling = false;
var mouseY = 0;
var cScroll = 0;
if("createTouch" in document) {
// TODO: Add for mobile browsers
} else {
content.addEventListener('mousedown', function(evt) {
isScrolling = true;
mouseY = evt.pageY;
}, false);
content.addEventListener('mousemove', function(evt) {
if(isScrolling) {
var dY = evt.pageY - mouseY;
mouseY = evt.pageY;
cScroll += dY;
var firstElementChild = content.getElementsByTagName("*")[0];
content.style.WebkitTransform = 'translateY(' + range(cScroll, -(firstElementChild.scrollHeight - content.offsetHeight), 0).toString() + 'px)';
}
}, false);
window.addEventListener('mouseup', function(evt) {
isScrolling = false;
}, false);
}
and modifying a few other parts. It does save a lot of download time, I suppose, also.
I'm still going to accept answers and award the bounty in 5 days, though.
Changed question warrants a new answer. I took a look at the code and I saw that you calculated the momentum on each step of the "move" function. This does not make sense because the momentum is used after the move has ended. What this meant was to capture the mouse position at the beginning, and then calculate the difference at the end. So I added two new variables,
var startTime;
var startY;
Inside the start event (mousedown/touchstart), I added,
startY = evt.pageY;
startTime = evt.timeStamp || Date.now();
Then I have the following for my end handler,
var duration = (evt.timeStamp || Date.now()) - startTime;
if (duration < 300) {
var dY = evt.pageY - startY;
momentum = range(momentum + dY * Scroller.ACCELERATION, -Scroller.MAX_MOMENTUM, Scroller.MAX_MOMENTUM);
} else {
momentum = 0;
}
I also removed the momentum calculation from inside of mousemove/touchmove. Doing this removed the jumping around behavior that I was seeing on my iPhone. I am seeing other unwanted behaviors as well (the whole window "scrolls"), but I'm guessing that you've been working to get rid of those so I didn't attempt.
Good luck. Here's a coded up page that I duplicated for my testing. I also took the liberty to refactor the code for this section to remove some duplicated code. It's under mobile3.js if you want to look at it.

Scroll div on iPhone from anywhere on screen

I'd like to prevent the default scrolling action on my page on an iPhone with the exception of a single div. Basically, when someone swipes their finger across the screen--anywhere on the screen--this single div ought to move. The code I'm using works fine when someone is directly touching the div element, but otherwise the position of the div is pretty erratic. Where am I messing up? This is a loose modification of what I found in the Safari Developer Library.
<div id="testdiv">
test test test test
</div>
<script type="text/javascript">
var startY = document.getElementById("testdiv").offsetTop;
var curY = startY;
var touchY;
document.addEventListener('touchstart', function(event) {
touchY = event.targetTouches[0].pageY;
}, false);
document.addEventListener('touchmove',function(event) {
event.preventDefault();
curY = event.targetTouches[0].pageY - startY - touchY;
document.getElementById("testdiv").style.webkitTransform = 'translate(0px,' + curY + 'px)';
}, false);
document.addEventListener('touchend',function(event) {
startY = curY;
}, false);
</script>
Change - startY to + startY when changing the value of curY. The problem was not that it only worked when you touched the div. The value of ...pageY - touchY will be the distance the touch has moved since it was first recorded. If the previous offset was 50 pixels and it was moved 20 pixels down, then you want the new offset to be 20 + 50, not 20 − 50. You didn't notice the problem on the first touch because the initial value of startY is close to 0. In fact, the initial value of startY should be 0, and not the offsetTop of the div, since the translation will be applied relative to the starting offset.

Categories

Resources