How can get a scroll event work in Chrome and Firefox? - javascript

I currently have this piece of code:
canvas.onmousewheel = scroll;
function scroll(event) {
event.preventDefault();
var mousex = event.clientX - canvas.offsetLeft;
var mousey = event.clientY - canvas.offsetTop;
var wheel = parseInt(event.wheelDelta, 10) / 120; //n or -n
var zoom = 1 + wheel / 2;
[... do some action on canvas ...]
};
Sadly, this doesn't work in Firefox. According to MDN:
Gecko doesn't have a plan to implement this event due to legacy event
type and non-standard.
How can I get my code work for Chrome and Firefox?
edit: The full code is here (one file, about 550 LOC)

You can try the following link
From the page:
onmousewheel event and Firefox's equivalent
var mousewheelevt=(/Firefox/i.test(navigator.userAgent))? "DOMMouseScroll" : "mousewheel" //FF doesn't recognize mousewheel as of FF3.x
if (document.attachEvent) //if IE (and Opera depending on user setting)
document.attachEvent("on"+mousewheelevt, function(e){alert('Mouse wheel movement detected!')})
else if (document.addEventListener) //WC3 browsers
document.addEventListener(mousewheelevt, function(e){alert('Mouse wheel movement detected!')}, false)

I faced this problem and I wrote code to use different events depending on the browser. Certainly jQuery or other frameworks would do it easily, but I am a Vanilla Javascript programmer.
For chrome: onmousewheel and document.body.scrollTop works.
For firefox: onscroll and event.pageY do the same.
if( Browser.chrome){
window.onmousewheel=function(e){
if( document.body.scrollTop > 60){
/* code to run on scrolled window */
} else {
/* code to run when on top */
}
};
} else {
window.onscroll=function(e){
if(e.pageY > 60){
/* code to run on scrolled window */
} else {
/* code to run when on top */
}
};
}
Browsers library available at http://software.sitesbr.net/utils/

Related

javascript - restoring scroll position does not happen

function getScrollTop()
{
if(typeof(window.pageYOffset) === 'number')
{
// DOM compliant, IE9+
window.scrollY = window.pageYOffset;
}
else
{
// IE6-8 workaround
if(document.body && document.body.scrollTop)
{
// IE quirks mode
window.scrollY = document.body.scrollTop;
}
else if(document.documentElement && document.documentElement.scrollTop)
{
// IE6+ standards compliant mode
window.scrollY = document.documentElement.scrollTop;
}
}
}
function getScrollLeft()
{
if(typeof(window.pageXOffset) === 'number')
{
// DOM compliant, IE9+
window.scrollX = window.pageXOffset;
}
else
{
// IE6-8 workaround
if(document.body && document.body.scrollLeft)
{
// IE quirks mode
window.scrollX = document.body.scrollLeft;
}
else if(document.documentElement && document.documentElement.scrollLeft)
{
// IE6+ standards compliant mode
window.scrollX = document.documentElement.scrollLeft;
}
}
}
// Summed-Up
function imgButtonClick()
{
getScrollTop();
getScrollLeft();
/*Some simple hidden code*/
window.scroll(window.scrollX, window.scrollY);
}
The getScrollTop() and getScrollLeft() functions are obtained from Internet - in a legal way, but the simple code addition does not work ( scroll(window.scrollX, window.scrollY); ). Mozilla's Developer Tools' Web Console (in Firefox) does not show errors. However, once filled with constants in place of scrollX and scrollY global variables - it does the simplified functioning by scrolling the page. The rest of the code, which had not been shown in this post - functions as asked. Other global variables on the page are also performing as asked.
If you are trying to animate a scrolling effect, you will need some additional code to animate the scrolling of the page. Here are some example functions to set the scroll location instantaneously:
function scrollToElement(element, scrollToTop)
{
var element = document.getElementById(element);
var elLoc = element.getBoundingClientRect();
var topCoord = elLoc.top + (scrollToTop ? window.innerHeight : 0);
console.log("About to scroll to: " + topCoord + ":" + elLoc.left);
window.scroll(elLoc.left, topCoord);
}
function scrollBackToTop()
{
window.scroll(0,0);
}
If you are looking to animate your scrolling effect, check out these:
Click here for a javascript solution.
Click here for a css solution.
You can also accomplish this using one line of code anywhere in your application or project using jQuery with no plugins:
$('html, body').animate(
{
scrollTop: $("#target-element").offset().top
}, 1000);
Taken from here.

iOS6 JavaScript touchmove event pass through vertical scroll afterwards jQuery animate does not fire

I have a div with a javascript touchmove event listener that scrolls the image inside the div horizontally on iOS6 Mobile Safari. I'd like allow vertical scrolling of the page to bubble up to the browser but when this occurs, jQuery.animate no longer works.
I've posted a simplified version of the code that demonstrates the problem at
https://gist.github.com/4047733
The steps I take to recreate the problem are:
Swipe picture left/right and notice how it animates back to the left edge
Touch the picture and scroll the page up/down
Repeat left/right swipe and notice the picture does NOT animate back to the left edge. It appears jQuery animate fails after touchmove occurs without e.preventDefault
Here is the javascript inside jQuery document ready from the gist link above
var el = document.getElementById("swipebox"),
$slider = $('#swipebox').find('img'),
startX, startY, dx, dy,
startLeft,
animateH = false,
animateV = false;
var onTouchStart = function(e) {
startLeft = parseInt($slider.css('left'), 10) || 0;
startX = e.touches[0].pageX;
startY = e.touches[0].pageY;
};
var onTouchMove = function(e) {
dx = e.touches[0].pageX - startX;
dy = e.touches[0].pageY - startY;
if (
animateH ||
(!animateV && Math.abs(dx) > 5)
) {
// prevent default, we are scrolling horizontally,
animateH = true;
$slider.stop().css({'left': startLeft+dx*2});
e.preventDefault();
} else if (Math.abs(dy) > 5) {
// do NOT prevent default, we are scrolling the page vertically
animateV = true;
} else {
// direction of scroll is undetermined at this time
// we've moved less than 5px in any direction
e.preventDefault();
}
return false;
};
var onTouchEnd = function(e) {
$slider.stop().animate({'left': 0}); // animate image back to left
e.preventDefault();
animateH = false;
animateV = false;
};
var onTouchCancel = function(e) {
console.log('onTouchCancel');
};
el.addEventListener('touchstart', onTouchStart, false);
el.addEventListener('touchmove', onTouchMove, false);
el.addEventListener('touchend', onTouchEnd, false);
el.addEventListener("touchcancel", onTouchCancel, false);
Any insight would be greatly appreciated.
This is bug in iOS6. jQuery animate timers fail when scrolling window in iOS6.
Currently there are few workarounds on this:
Create your own timer functions like someone did: https://gist.github.com/3755461
Use CSS3 transition instead of jQuery.animate. This is preffered way - css3 transitions doesn't have such problem. You can use this jquery plugin http://ricostacruz.com/jquery.transit/ to easily manipulate CSS transitions in JavaScript.

lock mousewheel scroll to focus div

I have a sidebar on my site that occasionally needs to be scrolled. It's an unpleasant user experience when, after scrolling the sidebar to the bottom with the mousewheel, the main window begins to scroll.
I'd like to make sure the main window does not scroll with the mouse wheel when the mouse wheel is over my sidebar, though preferably only when my sidebar itself is showing scrollbars.
I'd probably like to avoid use of javascript scroll bars as they seem to often not perform as well as the defaults (laggier, wait on other js code, etc), though it looks like this solution may require them.
Thanks!
EDIT::
Here's the code I ended up using, a simple adaptation of what I found in the first link from my accepted answer.
(function($) {
$.fn.hasScrollBar = function() {
return this.get(0).scrollHeight > this.height();
}
})(jQuery);
/** This is high-level function.
* It must react to delta being more/less than zero.
*/
function handle(delta) {
slider = $('#wl-slider');
if(slider.hasScrollBar()){
if (delta > 0 && slider.scrollTop() == 0 && slider.hasClass('scroll-hover')){
event.preventDefault();
event.returnValue = false;
}
else if (delta < 0 && (slider.get(0).scrollHeight - slider.scrollTop() == slider.outerHeight()) && slider.hasClass('scroll-hover')){
event.preventDefault();
event.returnValue = false;
}
}
}
/** Event handler for mouse wheel event.
*/
function wheel(event){
var delta = 0;
if (!event) /* For IE. */
event = window.event;
if (event.wheelDelta) { /* IE/Opera. */
delta = event.wheelDelta/120;
} else if (event.detail) { /** Mozilla case. */
/** In Mozilla, sign of delta is different than in IE.
* Also, delta is multiple of 3.
*/
delta = -event.detail/3;
}
/** If delta is nonzero, handle it.
* Basically, delta is now positive if wheel was scrolled up,
* and negative, if wheel was scrolled down.
*/
if (delta)
handle(delta);
}
/** Initialization code.
* If you use your own event management code, change it as required.
*/
if($('#user-level').size() != 0)
{
if (window.addEventListener)
/** DOMMouseScroll is for mozilla. */
window.addEventListener('DOMMouseScroll', wheel, false);
/** IE/Opera. */
window.onmousewheel = document.onmousewheel = wheel;
}
I guess you won't suffice with standard event handlers e.g. in jquery and you need to handle directly the mouse wheel - I just googled these resources:
http://www.adomas.org/javascript-mouse-wheel/
http://www.javascriptkit.com/javatutors/onmousewheel.shtml
And then look directly if the sidebar is already scrolled at the bottom/top and if yes, filter out the scroll event.

How can I know if a window cannot scroll anymore to the right?

I have a scrolling function over a recursive TimeOut making a very width window scroll horizontally until it reaches the right end, but I need to know in JavaScript if the scrolling has finished to change the direction and scroll back to the origin.
How can I accomplish this?
function ScrollIt(scrollPos, direction) {
if(direction == 'right')
window.scrollBy(scrollPos,0);
else
window.scrollBy(scrollPos,0);
cTimeOut = setTimeout("ScrollIt(1,'right')",10);
}
Of course I call the function in the onload event the first time.
Thanks in advance.
var w = document.documentElement.clientWidth;
var over = document.documentElement.scrollWidth - w;
window.onscroll = function() {
// Detect webkit browser (Chrome, Safari)
if(window.devicePixelRatio) {
if(document.body.scrollLeft==over) { alert("KABOOM (Webkit)") }
}
else {
if(document.documentElement.scrollLeft == over) { alert("CRUNCH") }
}
}
Tested FF 3.6, IE8, Chrome 3.1, Safari 4.0

Catch browser's "zoom" event in JavaScript

Is it possible to detect, using JavaScript, when the user changes the zoom in a page?
I simply want to catch a "zoom" event and respond to it (similar to window.onresize event).
Thanks.
There's no way to actively detect if there's a zoom. I found a good entry here on how you can attempt to implement it.
I’ve found two ways of detecting the
zoom level. One way to detect zoom
level changes relies on the fact that
percentage values are not zoomed. A
percentage value is relative to the
viewport width, and thus unaffected by
page zoom. If you insert two elements,
one with a position in percentages,
and one with the same position in
pixels, they’ll move apart when the
page is zoomed. Find the ratio between
the positions of both elements and
you’ve got the zoom level. See test
case.
http://web.archive.org/web/20080723161031/http://novemberborn.net/javascript/page-zoom-ff3
You could also do it using the tools of the above post. The problem is you're more or less making educated guesses on whether or not the page has zoomed. This will work better in some browsers than other.
There's no way to tell if the page is zoomed if they load your page while zoomed.
Lets define px_ratio as below:
px ratio = ratio of physical pixel to css px.
if any one zoom The Page, the viewport pxes (px is different from pixel ) reduces and should be fit to The screen so the ratio (physical pixel / CSS_px ) must get bigger.
but in window Resizing, screen size reduces as well as pxes. so the ratio will maintain.
zooming: trigger windows.resize event --> and change px_ratio
but
resizing: trigger windows.resize event --> doesn’t change px_ratio
//for zoom detection
px_ratio = window.devicePixelRatio || window.screen.availWidth / document.documentElement.clientWidth;
$(window).resize(function(){isZooming();});
function isZooming(){
var newPx_ratio = window.devicePixelRatio || window.screen.availWidth / document.documentElement.clientWidth;
if(newPx_ratio != px_ratio){
px_ratio = newPx_ratio;
console.log("zooming");
return true;
}else{
console.log("just resizing");
return false;
}
}
The key point is difference between CSS PX and Physical Pixel.
https://gist.github.com/abilogos/66aba96bb0fb27ab3ed4a13245817d1e
Good news everyone some people! Newer browsers will trigger a window resize event when the zoom is changed.
I'm using this piece of JavaScript to react to Zoom "events".
It polls the window width.
(As somewhat suggested on this page (which Ian Elliott linked to): http://novemberborn.net/javascript/page-zoom-ff3 [archive])
Tested with Chrome, Firefox 3.6 and Opera, not IE.
Regards, Magnus
var zoomListeners = [];
(function(){
// Poll the pixel width of the window; invoke zoom listeners
// if the width has been changed.
var lastWidth = 0;
function pollZoomFireEvent() {
var widthNow = jQuery(window).width();
if (lastWidth == widthNow) return;
lastWidth = widthNow;
// Length changed, user must have zoomed, invoke listeners.
for (i = zoomListeners.length - 1; i >= 0; --i) {
zoomListeners[i]();
}
}
setInterval(pollZoomFireEvent, 100);
})();
This works for me:
var deviceXDPI = screen.deviceXDPI;
setInterval(function(){
if(screen.deviceXDPI != deviceXDPI){
deviceXDPI = screen.deviceXDPI;
... there was a resize ...
}
}, 500);
It's only needed on IE8. All the other browsers naturally generate a resize event.
There is a nifty plugin built from yonran that can do the detection. Here is his previously answered question on StackOverflow. It works for most of the browsers. Application is as simple as this:
window.onresize = function onresize() {
var r = DetectZoom.ratios();
zoomLevel.innerHTML =
"Zoom level: " + r.zoom +
(r.zoom !== r.devicePxPerCssPx
? "; device to CSS pixel ratio: " + r.devicePxPerCssPx
: "");
}
Demo
Although this is a 9 yr old question, the problem persists!
I have been detecting resize while excluding zoom in a project, so I edited my code to make it work to detect both resize and zoom exclusive from one another. It works most of the time, so if most is good enough for your project, then this should be helpful! It detects zooming 100% of the time in what I've tested so far. The only issue is that if the user gets crazy (ie. spastically resizing the window) or the window lags it may fire as a zoom instead of a window resize.
It works by detecting a change in window.outerWidth or window.outerHeight as window resizing while detecting a change in window.innerWidth or window.innerHeight independent from window resizing as a zoom.
//init object to store window properties
var windowSize = {
w: window.outerWidth,
h: window.outerHeight,
iw: window.innerWidth,
ih: window.innerHeight
};
window.addEventListener("resize", function() {
//if window resizes
if (window.outerWidth !== windowSize.w || window.outerHeight !== windowSize.h) {
windowSize.w = window.outerWidth; // update object with current window properties
windowSize.h = window.outerHeight;
windowSize.iw = window.innerWidth;
windowSize.ih = window.innerHeight;
console.log("you're resizing"); //output
}
//if the window doesn't resize but the content inside does by + or - 5%
else if (window.innerWidth + window.innerWidth * .05 < windowSize.iw ||
window.innerWidth - window.innerWidth * .05 > windowSize.iw) {
console.log("you're zooming")
windowSize.iw = window.innerWidth;
}
}, false);
Note: My solution is like KajMagnus's, but this has worked better for me.
⬤ The resize event works on modern browsers by attaching the event on window, and then reading values of thebody, or other element with for example (.getBoundingClientRect()).
In some earlier browsers it was possible to register resize event
handlers on any HTML element. It is still possible to set onresize
attributes or use addEventListener() to set a handler on any element.
However, resize events are only fired on the window object (i.e.
returned by document.defaultView). Only handlers registered on the
window object will receive resize events.
⚠️ Do resize your tab, or zoom, to trigger this snippet:
window.addEventListener("resize", getSizes, false)
function getSizes(){
let body = document.body
body.width = window.innerWidth
body.height = window.innerHeight
console.log(body.width +"px x "+ body.height + "px")
}
getSizes()
⬤ An other modern alternative: the ResizeObserver API
Depending your layout, you can watch for resizing on a particular element.
This works well on «responsive» layouts, because the container box get resized when zooming.
function watchBoxchange(e){
info.textContent = e[0].contentBoxSize[0].inlineSize+" x "+e[0].contentBoxSize[0].blockSize + "px"
}
new ResizeObserver(watchBoxchange).observe(fluid)
#fluid {
width: 200px;
height:100px;
overflow: auto;
resize: both;
border: 3px black solid;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: 8vh
}
<div id="fluid">
<info id="info"></info>
</div>
💡 Be careful to not overload javascript tasks from user gestures events. Use requestAnimationFrame whenever you needs redraws.
I'd like to suggest an improvement to previous solution with tracking changes to window width. Instead of keeping your own array of event listeners you can use existing javascript event system and trigger your own event upon width change, and bind event handlers to it.
$(window).bind('myZoomEvent', function() { ... });
function pollZoomFireEvent()
{
if ( ... width changed ... ) {
$(window).trigger('myZoomEvent');
}
}
Throttle/debounce can help with reducing the rate of calls of your handler.
According to MDN, "matchMedia" is the proper way to do this https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio#Monitoring_screen_resolution_or_zoom_level_changes
it's a bit finicky because each instance can only watch one MQ at a time, so if you're interested in any zoom level change you need to make a bunch of matchers.. but since the browser is in charge to emitting the events it's probably still more performant than polling, and you could throttle or debounce the callback or pin it to an animation frame or something - here's an implementation that seems pretty snappy, feel free to swap in _throttle or whatever if you're already depending on that.
Run the code snippet and zoom in and out in your browser, note the updated value in the markup - I only tested this in Firefox! lemme know if you see any issues.
const el = document.querySelector('#dppx')
if ('matchMedia' in window) {
function observeZoom(cb, opts) {
opts = {
// first pass for defaults - range and granularity to capture all the zoom levels in desktop firefox
ceiling: 3,
floor: 0.3,
granularity: 0.05,
...opts
}
const precision = `${opts.granularity}`.split('.')[1].length
let val = opts.floor
const vals = []
while (val <= opts.ceiling) {
vals.push(val)
val = parseFloat((val + opts.granularity).toFixed(precision))
}
// construct a number of mediamatchers and assign CB to all of them
const mqls = vals.map(v => matchMedia(`(min-resolution: ${v}dppx)`))
// poor person's throttle
const throttle = 3
let last = performance.now()
mqls.forEach(mql => mql.addListener(function() {
console.debug(this, arguments)
const now = performance.now()
if (now - last > throttle) {
cb()
last = now
}
}))
}
observeZoom(function() {
el.innerText = window.devicePixelRatio
})
} else {
el.innerText = 'unable to observe zoom level changes, matchMedia is not supported'
}
<div id='dppx'>--</div>
You can also get the text resize events, and the zoom factor by injecting a div containing at least a non-breakable space (possibly, hidden), and regularly checking its height. If the height changes, the text size has changed, (and you know how much - this also fires, incidentally, if the window gets zoomed in full-page mode, and you still will get the correct zoom factor, with the same height / height ratio).
<script>
var zoomv = function() {
if(topRightqs.style.width=='200px){
alert ("zoom");
}
};
zoomv();
</script>
On iOS 10 it is possible to add an event listener to the touchmove event and to detect, if the page is zoomed with the current event.
var prevZoomFactorX;
var prevZoomFactorY;
element.addEventListener("touchmove", (ev) => {
let zoomFactorX = document.documentElement.clientWidth / window.innerWidth;
let zoomFactorY = document.documentElement.clientHeight / window.innerHeight;
let pageHasZoom = !(zoomFactorX === 1 && zoomFactorY === 1);
if(pageHasZoom) {
// page is zoomed
if(zoomFactorX !== prevZoomFactorX || zoomFactorY !== prevZoomFactorY) {
// page is zoomed with this event
}
}
prevZoomFactorX = zoomFactorX;
prevZoomFactorY = zoomFactorY;
});
Here is a clean solution:
// polyfill window.devicePixelRatio for IE
if(!window.devicePixelRatio){
Object.defineProperty(window,'devicePixelRatio',{
enumerable: true,
configurable: true,
get:function(){
return screen.deviceXDPI/screen.logicalXDPI;
}
});
}
var oldValue=window.devicePixelRatio;
window.addEventListener('resize',function(e){
var newValue=window.devicePixelRatio;
if(newValue!==oldValue){
// TODO polyfill CustomEvent for IE
var event=new CustomEvent('devicepixelratiochange');
event.oldValue=oldValue;
event.newValue=newValue;
oldValue=newValue;
window.dispatchEvent(event);
}
});
window.addEventListener('devicepixelratiochange',function(e){
console.log('devicePixelRatio changed from '+e.oldValue+' to '+e.newValue);
});
Here is a native way (major frameworks cannot zoom in Chrome, because they dont supports passive event behaviour)
//For Google Chrome
document.addEventListener("mousewheel", event => {
console.log(`wheel`);
if(event.ctrlKey == true)
{
event.preventDefault();
if(event.deltaY > 0) {
console.log('Down');
}else {
console.log('Up');
}
}
}, { passive: false });
// For Mozilla Firefox
document.addEventListener("DOMMouseScroll", event => {
console.log(`wheel`);
if(event.ctrlKey == true)
{
event.preventDefault();
if(event.detail > 0) {
console.log('Down');
}else {
console.log('Up');
}
}
}, { passive: false });
I'am replying to a 3 year old link but I guess here's a more acceptable answer,
Create .css file as,
#media screen and (max-width: 1000px)
{
// things you want to trigger when the screen is zoomed
}
EG:-
#media screen and (max-width: 1000px)
{
.classname
{
font-size:10px;
}
}
The above code makes the size of the font '10px' when the screen is zoomed to approximately 125%. You can check for different zoom level by changing the value of '1000px'.

Categories