Detecting scroll finish/end - javascript

I'd like to detect when a user has stopped scrolling a page/element. This may be tricky as recently enhancements to OSX's scrolling behaviour creates this new inertia effect. Is there an event fired?
The only other solution I can think of is using a interval to pick up when the scroll position of a page/element no longer changes, for example:
var element = $( el );
var previous = element.scrollLeft();
var current;
element.scroll( function(event)
{
current = element.scrollLeft();
if ( current === previous )
{
// user has stopped scrolling
}
previous = current;
});

Related

Is there a way to 'bake' the variable?

I'm creating dynamic scroll page, where I decided to use wheel event to detect user's scrolling. As you may know this event has a deltaY parameter that updates dynamically(several times for one scroll). I want the script to return true once deltaY is bigger than 100. However it updates over and over again causing my function shoot several times. Is there a way to 'bake'(be able to change once) this true value?
window.addEventListener('wheel', func)
function func(event){
if(event.deltaY>100){
var p = document.createElement('p');
var text = document.createTextNode("You scrolled over 100");
p.appendChild(text);
document.body.insertBefore(p, document.getElementsByTagName('p')[0]);
}
}
<p>Scroll the page down \/ and keep scrolling</p>
You could remove the listener once the element has been added. this way the function will not continue to fire.
window.addEventListener('wheel', func)
function func(event){
if(event.deltaY>100){
var p = document.createElement('p');
var text = document.createTextNode("You scrolled over 100");
p.appendChild(text);
document.body.insertBefore(p, document.getElementsByTagName('p')[0]);
window.removeEventListener('wheel',func);
}
}
https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener

How do I detect the Keyboard show/hide event occurence on Android browser

I am in a fix. I am not able to identify a way to capture the keyboard show/hide status on a mobile device browser.
Problem :
I have a popup on a form in which a Text Field is present. When the user taps on the text field the keyboard shows up pushing the popup on the form and eventually making the text field invisible.
Is there a way to identify the key board show/hide status???
No, there is no way to reliably know when a keyboard is showing. The one level of control you do have is you can set your app to pan or resize when the keyboard shows up. If you set it to resize, it will recalculate your layout and shrink things so if fits the remaining screen. If you choose pan, it will keep the same size and just slide up the entire app.
you can find out keyboard show/hide inside your application,Try following code inside oncreate method,and pass your parent layout to view.
final View activityRootView = rellayLoginParent;
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener()
{
#Override
public void onGlobalLayout()
{
Rect r = new Rect();
// r will be populated with the coordinates of your view that area still visible.
activityRootView.getWindowVisibleDisplayFrame(r);
int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
//MyLog.w("height difference is", "" + heightDiff);
if (heightDiff > 100)
{ // if more than 100 pixels, its probably a keyboard...
if(lytAppHeader.getVisibility() == View.VISIBLE)
{
lytAppHeader.setVisibility(View.GONE);
}
}
else
{
if(lytAppHeader.getVisibility() == View.GONE)
{
lytAppHeader.setVisibility(View.VISIBLE);
}
}
}
});
It seems there is no reliable way to do this in the browser. The closest I have come is to listen for focus events and then temporarily listen for resize events. If a resize occurs in the next < 1 second, it's very likely that the keyboard is up.
Apologies for the jQuery...
onDocumentReady = function() {
var $document = $(document);
var $window = $(window);
var initialHeight = window.outerHeight;
var currentHeight = initialHeight;
// Listen to all future text inputs
// If it's a focus, listen for a resize.
$document.on("focus.keyboard", "input[type='text'],textarea", function(event) {
// If there is a resize immediately after, we assume the keyboard is in.
$window.on("resize.keyboard", function() {
$window.off("resize.keyboard");
currentHeight = window.outerHeight;
if (currentHeight < initialHeight) {
window.isKeyboardIn = true;
}
});
// Only listen for half a second.
setTimeout($window.off.bind($window, "resize.keyboard"), 500);
});
// On blur, check whether the screen has returned to normal
$document.on("blur.keyboard", "input[type="text"],textarea", function() {
if (window.isKeyboardIn) {
setTimeout(function() {
currentHeight = window.outerHeight;
if (currentHeight === initialHeight) {
window.isKeyboardIn = false;
}, 500);
}
});
};

Detect whether the intent of a user is to tap or to scroll up/down the page on tactile device

If a user taps (touchstart) outside a popup-div, I want to hide the popup.
But if the user's intent is to scroll/swipe (touchmove), I don't want to hide the popup.
How could the code look like to detect and respond to those two actions (with or without jQuery)?
Here is a basic example of how you could do this:
http://jsfiddle.net/4CrES/2/
The logic behind it involves detecting the initial touch time and saving it to a var
touchTime = new Date();
In the touchend handler subtract this time from the current time to get the difference:
var diff = new Date() - touchTime;
Use an if statement to decide whether the touch duration was short enough to consider it a tap, or long enough to consider it a drag.
if (diff < 100){
//It's a tap
}
else {
//Not a quick tap
}
You could write a more robust implementation by doing a similar difference of the initial touch y position to the final touch y position in the handlers. Another option is to compare the scrollTop of the scrolling area to see if it has been scrolled.
Since click events do not bubble up the DOM on mobile Safari while touch events and custom events do, I recently wrote some code to detect a quick-tap.
It's a quick-tap when
The touch event starts and ends without any movement along the screen
No scrolling occurrs
It all happens in less than 200ms.
If the touch is determined to be a 'quickTap', the TouchManager causes the touched element in the DOM to emit a custom "quickTap" event which then bubbles up the DOM to any other elements that happen to be listening for it. This code defines and creates the touch manager and it will be ready to go immediately
Drawbacks:
Uses jQuery
Only designed with one finger in mind.
also borrowed some code from modernizr. (You can omit that bit if you already include Modernizr.)
Maybe this is overkill, but it's part of a larger codebase I'm working on.
/**
* Click events do not bubble up the DOM on mobile Safari unless the click happens on a link or form input, but other events do bubble up.
* The quick-tap detects the touch-screen equivalent of a click and triggers a custom event on the target of the tap which will bubble up the DOM.
* A touch is considered a click if there is a touch and release without any movement along the screen or any scrolling.
*/
var qt = (function ($) {
/**
* Modernizr 3.0.0pre (Custom Build) | MIT
* Modernizr's touchevent test
*/
var touchSupport = (function() {
var bool,
prefixes = ' -webkit- -moz- -o- -ms- '.split(' ')
if(('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {
bool = true;
} else {
var query = ['#media (',prefixes.join('touch-enabled),('),'heartz',')','{#modernizr{top:9px;position:absolute}}'].join('');
testStyles(query, function( node ) {
bool = node.offsetTop === 9;
});
}
return bool;
}()),
MobileTapEvent = 'tapEvent';
if(touchSupport) {
/* Create a new qt (constructor)*/
var startTime = null,
startTouch = null,
isActive = false,
scrolled = false;
/* Constructor */
function qt() {
var _qt = this,
context = $(document);
context.on("touchstart", function (evt) {
startTime = evt.timeStamp;
startTouch = evt.originalEvent.touches.item(0);
isActive = true;
scrolled = false;
})
context.on("touchend", function (evt) {
window.ct = evt.originalEvent['changedTouches'];
// Get the distance between the initial touch and the point where the touch stopped.
var duration = evt.timeStamp - startTime,
movement = _qt.getMovement(startTouch, evt.originalEvent['changedTouches'].item(0)),
isTap = !scrolled && movement < 5 && duration < 200;
if (isTap) {
$(evt.target).trigger('quickTap', evt);
}
})
context.on('scroll mousemove touchmove', function (evt) {
if ((evt.type === "scroll" || evt.type === 'mousemove' || evt.type === 'touchmove') && isActive && !scrolled) {
scrolled = true;
}
});
}
/* Calculate the movement during the touch event(s)*/
qt.prototype.getMovement = function (s, e) {
if(!s || !e) return 0;
var dx = e.screenX - s.screenX,
dy = e.screenY - s.screenY;
return Math.sqrt((dx * dx) + (dy * dy));
};
return new qt();
}
}(jQuery));
To use the code you would add it to your page then just listen for the quickTap event.
<script type="text/javascript" src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
<script type="text/javascript" src="quick-tap.js"></script>
<script type="text/javascript">
$(document).on('quickTap', function(evt, originalEvent) {
console.log('tap event detected on: ', evt.target.nodeName, 'tag');
});
</script>
evt is the quickTap event.
evt.target is the tapped DOM element (not the jQuery object).
originalEvent is the touchend event where qt determines whether it was a tap or not.
You can hide the popup-div on touchend event.
In touchstart event you remember window.scrollY.
In touchend event, if scrollY positions differ the user has scrolled.

How to listen for layout changes on a specific HTML element?

What are some techniques for listening for layout changes in modern browsers? window.resize won't work because it only fires when the entire window is resized, not when content changes cause reflow.
Specifically, I'd like to know when:
An element's available width changes.
The total height consumed by the in-flow children of an element changes.
There are no native events to hook into for this. You need to set a timer and poll this element's dimensions in your own code.
Here's the basic version. It polls every 100ms. I'm not sure how you want to check the children's height. This assumes they'll just make their wrapper taller.
var origHeight = 0;
var origWidth = 0;
var timer1;
function testSize() {
var $target = $('#target')
if(origHeight==0) {
origWidth = $target.outerWidth();
origHeight = $target.outerHeight();
}
else {
if(origWidth != $target.outerWidth() || origHeight = $target.outerHeight()) {
alert("change");
}
origWidth = $target.outerWidth();
origHeight = $target.outerHeight();
timer1= window.setTimeout(function(){ testSize() }),100)
}
}
New browsers now have ResizeObserver, which fires when the dimensions of an element's content box or border box are changed.
const observer = new ResizeObserver(entries => {
const entry = entries[0];
console.log('contentRect', entry.contentRect);
// do other work hereā€¦
});
observer.observe(element);
From a similar question How to know when an DOM element moves or is resized, there is a jQuery plugin from Ben Alman that does just this. This plugin uses the same polling approach outlined in Diodeus's answer.
Example from the plugin page:
// Well, try this on for size!
$("#unicorns").resize(function(e){
// do something when #unicorns element resizes
});

How to disable scrolling in outer elements?

I have a vertically-scrolling div within a page that also scrolls vertically.
When the child div is scrolled with the mouse wheel and reaches the top or bottom of the scroll bar, the page (body) begins to scroll. While the mouse is over the child div, I'd like the page (body) scroll to be locked.
This SO post (scroll down to the selected answer) demonstrates the problem well.
This SO question is essentially the same as mine, but the selected answer causes my page contents to noticeably shift horizontally as the scrollbar disappears and reappears.
I thought there might be a solution that leverages event.stopPropagation(), but couldn't get anything to work. In ActionScript, this kind of thing would be solved by placing a mousewheel handler on the child div that calls stopPropagation() on the event before it reaches the body element. Since JS and AS are both ECMAScript languages, I thought the concept might translate, but it didn't seem to work.
Is there a solution that keeps my page contents from shifting around? Most likely using stopPropagation rather than a CSS fix? JQuery answers are welcome as is pure JS.
here's what i ended up with. very similar to #mrtsherman's answer here, only pure JS events instead of jQuery. i still used jQuery for selecting and moving the child div around, though.
// earlier, i have code that references my child div, as childDiv
function disableWindowScroll () {
if (window.addEventListener) {
window.addEventListener("DOMMouseScroll", onChildMouseWheel, false);
}
window.onmousewheel = document.onmousewheel = onChildMouseWheel;
}
function enableWindowScroll () {
if (window.removeEventListener) {
window.removeEventListener("DOMMouseScroll", onArticleMouseWheel, false);
}
window.onmousewheel = document.onmousewheel = null;
}
function onChildMouseWheel (event) {
var scrollTgt = 0;
event = window.event || event;
if (event.detail) {
scrollTgt = -40 * event.detail;
} else {
scrollTgt = event.wheelDeltaY;
}
if (scrollTgt) {
preventDefault(event);
$(childDiv).scrollTop($(childDiv).scrollTop() - scrollTgt);
}
}
function preventDefault (event) {
event = event || window.event;
if (event.preventDefault) {
event.preventDefault();
}
event.returnValue = false;
}
i've noticed the scrolling doesn't match normal scrolling exactly; it seems to scroll a bit faster than without this code. i assume i can fix by knocking down wheelDeltaY a bit, but it's odd that it would be reported differently by javascript than it's actually implemented by the browser...
I usually do it with a small hack listening to the scroll event on the document: it resets the scroll height back to the original one - effectively freezing the document from scrolling but any inner element with overflow: auto will still scroll nicely:
var scrollTop = $(document).scrollTop();
$(document).on('scroll.scrollLock', function() {
$(document).scrollTop(scrollTop);
});
and then when I'm done with the inner scroll lock:
$(document).off('scroll.scrollLock');
the .scrollLock event namespace makes sure I'm not messing with any other event listeners on scroll.
Although this is an old question, here is how I do it with jQuery. This allows you to scroll a list within an outer list, or you can change the outer list to the document to do what the OP asked.
window.scrollLockHolder = null;
function lockScroll(id){
if (window.scrollLockHolder == null){
window.scrollLockHolder = $('#' + id).scrollTop();
}
$('#' + id).on('scroll', function(){
$('#' + id).scrollTop(window.scrollLockHolder);
});
}
function unlockScroll(id){
$('#' + id).off('scroll');
window.scrollLockHolder = null;
}
And you can use it like this:
<ul onmousemove="lockScroll('outer-scroller-id')" onmouseout="unlockScroll('outer-scroller-id')">
<li>...</li>
<li>...</li>
</ul>
what about this:
div.onmousemove = function() { // may be onmouseover also works fine
document.body.style.overflow = "hidden";
document.documentElement.style.overflow = "hidden";
};
div.onmouseout = function() {
document.body.style.overflow = "auto";
document.documentElement.style.overflow = "auto";
};

Categories