Converting Edge/IE JavaScript WheelEvent deltas to native scroll amounts? - javascript

In Edge and Internet Explorer, I'm noticing a strange issue with wheel events not equating to their expected scroll amounts. Even though the deltaMode values are reported as DOM_DELTA_PIXEL (0), the number of pixels found in deltaY is greater then the number of pixels actually being scrolled in a small scrollable div.
Take this code for example:
<!DOCTYPE html>
<html>
<head>
<title>test</title>
<style>
#scroller {
width: 200px;
height: 200px;
overflow: scroll;
}
</style>
</head>
<body>
<div id="scroller">
<p>test</p><p>test</p><p>test</p><p>test</p>
<p>test</p><p>test</p><p>test</p><p>test</p>
<p>test</p><p>test</p><p>test</p><p>test</p>
</div>
<script>
(function() {'use strict';
var scroller = document.getElementById('scroller');
scroller.addEventListener('wheel', function(e) {
var scrollY = e.deltaY;
console.log('scrollY:', scrollY);
setTimeout(function() {
console.log('scroller.scrollTop:', scroller.scrollTop);
}, 250);
});
})();
</script>
</body>
</html>
In the console I'm seeing values like this:
scrollY: 101.8499984741211
scroller.scrollTop: 28
In other browsers like Chrome, these values match or are at-least very close.
So the amount actually being scrolled is not the same as what is reported. Why is this, and can we get the correct value?

The reason the values are different is because IE and Edge actually scale the DOM pixels down based on how much of the window the scrolling area being scrolled takes up.
This complicates computing the proper scaling for a browser, and since IE/Edge are the only ones doing it, you will have to use some form of user-agent sniffing.
To get the correct amount of scrolling, you need to multiply the deltaY by (scroller.clientHeight / window.innerHeight) (and likewise with deltaX).
Here is that same sample with these adjustments:
<!DOCTYPE html>
<html>
<head>
<title>test</title>
<style>
#scroller {
width: 200px;
height: 200px;
overflow: scroll;
}
</style>
</head>
<body>
<div id="scroller">
<p>test</p><p>test</p><p>test</p><p>test</p>
<p>test</p><p>test</p><p>test</p><p>test</p>
<p>test</p><p>test</p><p>test</p><p>test</p>
</div>
<script>
(function() {
'use strict';
var scroller = document.getElementById('scroller');
scroller.addEventListener('wheel', function(e) {
var deltaY = e.deltaY;
var scaleY = scroller.clientHeight / window.innerHeight;
var scrollY = deltaY * scaleY;
console.log('scrollY:', scrollY);
setTimeout(function() {
console.log('scroller.scrollTop:', scroller.scrollTop);
}, 250);
});
})();
</script>
</body>
</html>
Which gives the following output in my console:
scrollY: 28.199999577518067
scroller.scrollTop: 28
Note that scrollTop values do not have floating points, so if you want the values to match exactly, you can Math.round them.

Related

conflicting value for pageY on Ipad

I am testing a page on an IPad (IOS 14.3) in portrait mode . (see code below)
I am outputting the touched Y-value of the screen.
When I tap the screen near the top the output is near 0 (depending on the thickness of finger).
When I tap the screen near the bottom the output is near 1000.
However when I swipe vertically from the top to bottom the values start from 0 but when I reach the bottom with my finger it stops near 600.
The same issue in the other directory, when I start at the bottom is shows a value around 1000 and when it reaches the top is stops around 300.
It almost seems there are missing 300px in each (vertical) direction.
Note: the X-value (horizontal swipes) is accurate, hence I've excluded it in this example. Also on Android the output from the console.log seems fine.
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1,minimum-scale=1.0, user-scalable=yes">
<style>
body,
html {
position: fixed;
width: 100%;
height: 100%;
margin: 0;
padding 0;
background-color: #AAAAAA;
}
</style>
</head>
<body>
<div style='text-align:center;font-size:25px;width:100px;border:1px solid blue;margin:auto' id='feedback'>Hello</div>
</body>
</html>
<script>
var func = function (e) {
var evt = (typeof e.originalEvent === 'undefined') ? e : e.originalEvent;
var touch = evt.touches[0] || evt.changedTouches[0];
document.getElementById('feedback').textContent = 'top:'+touch.pageY;
};
document.body.addEventListener('touchstart', func);
document.body.addEventListener('touchmove', func);
document.body.addEventListener('touchend', func);
</script>
What is the reason the with swipping vertically I am getting different results than when I touch the screen?
It was solved by changing
document.getElementById('feedback').textContent = 'top:'+touch.pageY;
into
document.getElementById('feedback').textContent =
'top:'+touch.clientY;
Although I don't quite see why these would differ in this case.

I have a Div Element acting as a button but it will only work once

I am 11 years old and I started learning Javascript a couple of months ago, So I am trying to make a page where if you scroll down too much it will take you back to the top so I made a Div element that fills up a large space and onmouseover it will take you back up to the top but if you try it a second time it won't do anything. Please help. Thanks in advance !
I hope my understanding of your problem is right. You have a div and you want to go up each time you scroll too much.
As an example of how to handle the scroll in vanilla JavaScript you can have a look at the document for the onscroll event: https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onscroll.
Here is an example:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<style>
#container {
height: 500px;
width: 515px;
overflow: auto;
}
#foo {
height: 1000px;
width: 500px;
background-color: #777;
}
</style>
</head>
<body>
<div id="container">
<div id="foo"></div>
</div>
<script>
var container = document.getElementById('container');
container.addEventListener('scroll', function(event) {
// Get top and left value
var top = container.scrollTop
if (top > 400) {
// Go to the top
container.scrollTop = 0;
}
}, false);
</script>
</body>
</html>
In this example the contained element is bigger that the container so the container becomes scrollable with the overflow: auto; css property.
The scripts applies a onscroll event that checks the scroll value of the container and reset it to 0 when it exceed an arbitrary value (400 in the example).
I hope this has been useful to your work.

Detecting mousewheel with onscroll

I tried by several ways to detect accurately mousewheel / DOMMouseScroll event, but it seems that the result will vary much from browser to another browser, and above all from hardware to another hardware. (ex: MacBook Magic Trackpad fires many mousewheel events, etc.)
There has been many attempts of JS library to "normalize" the wheelDelta of a mousewheel event. But many of them failed (I don't find the relevant SO question anymore but there are some that point this failure).
That's why I try now a solution without the mousewheel event, but rather onscroll event. Here is an example of scrolling / mousewheel detection with a hidden container that scrolls (#scroller), and the normal container (#fixed_container) with normal content.
As #scroller has a finite height (here 4000px), I cannot detect scrolling / mousewheel
infinitely...
How to allow endless scroll events (by setting an infinite height for #scroller? how?) ?
Code / Live demo :
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<style>
body { overflow:hidden; }
#scroller { height: 4000px; }
#fixed_container { position:fixed; top:0px; left:0px; }
#text { position:absolute; top:100px; left:100px; }
</style>
<script type="text/javascript">
window.onscroll = function(e) {
console.log("scroll event detected! " + window.pageXOffset + " " + window.pageYOffset);
e.preventDefault();
return false;
}
</script>
</head>
<body>
<div id="scroller"></div>
<div id="fixed_container">
<div id="text">
Bonjour
</div>
</div>
</body>
</html>
"How to allow endless scroll events"
This should do it:
$(window).scroll(function() {
var st= $(window).scrollTop();
var wh= $(window).height();
var sh= $('#scroller').height();
if(sh < st+wh*2) {
$('#scroller').css({
height: st+wh*2
});
};
});
Tested in IE11, Firefox, Chrome, Opera, and Safari.
In the fiddle below, clicking adds text, so you can see it scroll:
Fiddle

Can't get scrollTop() to work in both Chrome & Firefox

I am having trouble getting the scrollTop() method to work in both Firefox and Chrome. I used $('body, html').scrollTop(); however, it doesn't work in Chrome. Only $('body').scrollTop(); works in Chrome. Any thoughts would be greatly appreciated. Below is my code.
<!DOCTYPE html>
<html>
<head>
<title>Demo</title>
<style type="text/css">
body {
height: 2000px;
}
#light {
display: block;
position: fixed;
top: 50%;
left: 50%;
margin-left: -400px;
margin-top: -200px;
width: 800px;
height: 400px;
background-color: blue;
z-index:1002;
overflow: auto;
}
</style>
</head>
<body>
<div id="light">
</div>
<!-- Used the google jQuery link for ease of use in this example -->
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$(window).scroll(function () {
var offset = $('body, html').scrollTop();
var view = $(window).height();
var total = $(document).height();
var percent = 1-(offset / (total - view));
var widthFactor = 800*percent;
var marginFactor = -(400*percent)
if(percent > 0.33){
$("#light").css({ "width" : widthFactor,
"margin-left" : marginFactor});
};
});
});
</script>
</body>
</html>
Use the document object instead
$(document).scrollTop();
I had this same issue. Best solution for me was to do it on window:
var offset = $(window).scrollTop();
In order for this to work though, your body and html elements can't have a height set to 100%. use min-height instead
EDIT: the HTML element can use height: 100%, however if you need the body to stretch to full height you have to use min-height: 100% instead. Otherwise the scrollTop always returns "0"
Try this, this is scroll on top with animation which is seen more effective
$("html, body").animate({ scrollTop: 0 }, 2000);
Demo Here
You use multiple selector and it will return an array of DOM elements. Calling getter function of this array seems undefined in Chrome (setter functions should work)?
Anyway you can use $('body').scrollTop() || $('html').scrollTop() in you case.
Or just $(document) as mentioned in Justin's answer.
Used this solution:
window.scrollY || window.pageYOffset || document.body.scrollTop + (document.documentElement && document.documentElement.scrollTop || 0)
Supplied in this answer in another thread:
https://stackoverflow.com/a/33462363
You don't need to involve jQuery and it works fine for me.
try this simple javascript code for scroll element using id
document.getElementById("id").scrollTop=0;
Remove height style from the body,html tags. Add an id to the main div under body e.g. #content then use following script. As previously quoted run $(document).scrollTop(); in the browser console and make sure it returns a value not 0.
$('body, html').animate({
scrollTop: $('#content ').offset().top
}, 1000);

Jquery Draggable not positioning scrollbars properly

I need to make an image viewer that allows large images to be loaded into a container and then dragged within the container so that the entire image is viewable but the image is never dragged out of bounds. The below code works perfectly except the scrollbars are not accurately synchronizing with the position of the dragged image and allow the image to be scrolled out of bounds. How can I synchronize the scroll bars with the image while it is being dragged?
Edit:
Here is a working example
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.6/jquery-ui.min.js"></script>
<style>
.container{margin: auto;cursor: move;width: 80%; position: relative; min-width:885px;}
#screen{overflow:auto; width: 80%; height: 600px; clear: both; border: 1px solid black; background-color: #CCCCCC; float:left; margin-right: 15px;}
</style>
</head>
<body>
<div class="container">
<div id="screen">
<img class="drag-image" id="draggable" />
</div>
</div>
</body>
</html>
<script type="text/javascript">
$(function () {
$('#draggable').attr('src', 'http://i.imgur.com/uPjIz.jpg').load(function () {
CreateDraggablePicture();
});
});
function CreateDraggablePicture() {
var x = ($('#draggable').width() - $('#screen').width() - $('#screen').offset().left) * -1;
var y = ($('#draggable').height() - $('#screen').height() - $('#screen').offset().top) * -1;
var x2 = $('#screen').offset().left;
var y2 = $('#screen').offset().top;
$("#draggable").draggable({ containment: [x, y, x2, y2], scroll: true });
}
</script>
These plugins seems to do the same effect you describe here
http://www.azoffdesign.com/overscroll (seems to be the best one)
http://hitconsultants.com/dragscroll_scrollsync/scrollpane.html (I didn't find the download link though)
http://the-taylors.org/jquery.kinetic/ (didn't see an option for scrollbars, but it's mobile friendly)
I had this exact problem with a similar jQuery plugin. I eventually had to figure out and manually modify the plugin's math. I believe the secret was that it was missing css margins or css padding from the calculation.
See if that helps

Categories

Resources