Today I've discovered some very bizarre behavior in Microsoft's Edge browser, where the deltaX values for wheel events is apparently inverted! This was particularly surprising because this is not consistent with every other browser I've ever tested, including Internet Explorer 11 which returns the expected values.
Seeing this issue in action is rather simple, just run the following code, and use your mouse wheel or trackpad.
window.addEventListener('wheel', function(e) {
console.log(e.deltaX, e.deltaY);
});
For your convenience, I've created a full-page example (snippets are tricky for this):
Fullpage Working Example
Wheeling down gives a positive value and wheeling up gives a negative value as expected in Edge, IE, and other browsers. However, in Edge wheeling left gives a positive value, and right a negative value, the exact opposite of every other browser (IE11 and below included).
I made some GIF videos to show off the issue too, linked for file sizes.
IE11
Edge
The question:
Why is it like this, and is there a solution for handling the browser compatibility issues? Is there a way to feature-detect this? Is this behavior a bug, or is it documented somewhere?
The spec strongly suggests this is in-fact incorrect behavior:
If a user agent scrolls as the default action of the wheel event then the sign of the delta SHOULD be given by a right-hand coordinate system where positive X, Y, and Z axes are directed towards the right-most edge, bottom-most edge, and farthest depth (away from the user) of the document, respectively.
Notes:
I've tested this in both a Windows 10 VM and a native laptop, behavior was the same for both.
I'm reasonably sure this is not related to "natural"/inverted scrolling (off on all systems and VM hosts tested, and only happening on one axis).
On a side note, I don't know if deltaZ is inverted or even supported to begin with, I lack such an input device.
Bug Report:
I've reported the bug to Microsoft here. It has been assigned to someone, so hopefully it will be fixed.
While we await an official response from Microsoft, hopefully in the form of an update that resolves the issue, I have found a way to check if the browser is reporting an incorrectly inverted deltaX.
Edge also added support for the non-standard wheelDeltaX, wheelDeltaY, and wheelDelta properties also found in Chrome. Unlike deltaX, these values are correct, but instead refer to the actual wheel change and not the computed result. While their actual values are of little interest, we can infer the correct sign value from them.
In Chrome, deltaX and wheelDeltaX correctly have different sign values, so when one is negative, the other is positive. This means that if both values are negative or both are positive, the deltaX is incorrectly signed as it is presently in Edge. Therefore we can use this property to detect the incorrectly inverted value and un-invert it.
Here is a wrapper function I created to resolve the issue.
function wheelDeltaX(e) {
// Get the reported deltaX.
var r = e.deltaX;
// In Edge, the deltaX incorrectly matches wheelDeltaX sign.
// If both value signs match then uninvert it.
var wheelDeltaX;
if (
r &&
(wheelDeltaX = e.wheelDeltaX) &&
(
(r < 0 && wheelDeltaX < 0) ||
(r > 0 && wheelDeltaX > 0)
)
) {
r = -r;
}
return r;
}
So long as Edge retains the correct signing for wheelDeltaX when they hopefully fix the issue, this should be future-proof.
Related
I am testing a project on my Surface Pro 4 and I am using the Touch interface in javascript. According to MDN, the rotationAngle and force properties should return [0 - 90] and [0.0 - 1.0] respectively.
But no matter how I touch the display, I get fixed values for either of them. In Chrome (81) it is always 0 and 0.5. In Firefox (76), 0 and 0 are returned.
The code is simple:
element.addEventListener('touchstart', function(evt) {
// Prevent the mouse click
evt.preventDefault();
//-- Log the touch information
var touch = evt.changedTouches.item(0);
console.log(touch.radiusX);
console.log(touch.radiusY);
console.log(touch.rotationAngle);
console.log(touch.force);
});
Now, about the force property, MDN specifies that if the value is unknown for some reason (e.g. the device doesn't support it), 0.0 is returned. But nothing is mentioned about a constant 0.5 for this property or 0 for the rotationAngle.
Am I missing something? Or does the Surface Pro 4 indeed not support these values and I can not do anything about it?
I have very basic promise based self-calling function that:
takes collection of divs with certain class
checks whether they have just been moved left or right
based on result makes choice to move (transform: translate) them
with classList.add() / classList.remove()
and on transitionend - calls itself
here is function:
function transitionTest(){
console.log('called --- transitionTest() ');
var dummies = document.getElementsByClassName('dummy'),
count = dummies.length;
if(window.cache==='right'){
var transitionCollection=0;
for(var i = 0; i < dummies.length; i++){
dummies[i].classList.remove('right');
dummies[i].addEventListener('transitionend', function(){
transitionCollection++;
if( transitionCollection === dummies.length ){
transitionTest();
}
});
}
window.cache='';
} else {
var transitionCollection=0;
for(var i = 0; i < dummies.length; i++){
dummies[i].classList.add('right');
dummies[i].addEventListener('transitionend', function(){
transitionCollection++;
if( transitionCollection === dummies.length ){
transitionTest();
}
});
}
window.cache='right';
}
and here is working fiddle
So, what is wrong?
Nothing, if you are accessing via modern browser but not latest versions
of Chrome on Windows
Nothing, if you are accessing via latest versions of Chrome on Windows but refrain from causing any mouse events such as mouseenter/leave, click, even window focus event (e.g. if you stand still)
If you do such, infinite left - right movement of dummy div will occasionally break, under unclear circumstances
What gets wrong:
Dummy div, which is moving left-right infinitely, on mouseenter, mouseleave, click, sometimes and sometimes not (exact conditions are unclear) will:
go to end CSS value without transition and resumes normal operation after a while
stop entirely and resumes normal operation after a while
slow down (!? yeah, I wish I was kidding ) and stop/go to end CSS value
These errors are occurring in Chrome 45 (Win 7) and, less intensively Chrome 42 (Win XP) - which are platforms that I was able to test by now. Just to note, upper code does not need to be cross browser, I'm fully aware of implications.
Working on an app that requires the ability to hold down multiple keys at once to trigger unique functions.
I've run into a situation where certain combinations of keys will prevent 'keydown' from being triggered.
Holding a horizontal row 1 > 2 > 3 > 4 > 5 > 6 or a vertical column 1 > Q > A > Z
(6 keys appears to be the max the browser will recognise at once) will work however - if a user holds corner shape for example 1 > 2 > w the events are prevented.
Can be demonstrated using this fiddle:
http://jsfiddle.net/B1KMusic/U5L2X/light/
Interestingly though this 'shape' of keys appears significant - 2 > 3 > Wand 3 > 4 > R etc. will also be prevented.
Even if this shape is rotated across the keyboard the same shapes like: C -> X -> S and N -> H -> J will do the same.
If a gap is left and the corner is not 'connected' then the events will work properly 3 > E > D > V - but if C is pressed this will not work.
What is going on here? Is this some intentional browser default to prevent key mashing?
Edit: As the answer provided points out, this is a hardware issue so it makes sense to include hardware info: tested on a late 2013 Macbook Pro, apple stackexchange question confirms the answer here: https://apple.stackexchange.com/questions/47699/are-apple-keyboards-multi-key-rollover
This is a hardware limitation of your keyboard, not an issue with the browser. Most keyboards use a matrix arrangement for key switches, and hence cannot detect certain combinations of keys being pressed. The specific combinations which will and will not work are hardware-dependent, but in general, it is not safe to assume that more than two non-modifier keys can be pressed simultaneously.
Some gaming keyboards support n-key rollover (that is, any number of keys being pressed at once), but these are rare.
Consider the following:
var x = 2.175;
console.log(x.toFixed(2)); // 2.17
What? No, no surprise here. That's rather obvious, see: Number literal 2.175 is actually stored in memory (by IEEE-754 rules) as a value that's just a tiny little bit smaller than 2.175. And that's easy to prove:
console.log(x.toFixed(20)); // 2.17499999999999982236
That's how it works in the latest versions of Firefox, Chrome, and Opera on 32-bit Windows setup. But that's not the question.
The real question is how Internet Explorer 6 (!) actually manages to do it right as humans do:
var x = 2.175;
console.log(x.toFixed(2)); // 2.18
console.log(x.toFixed(20)); // 2.17500000000000000000
OK, I overdramatized: actually all Internet Explorers I tested this on (IE8-11, and even MS Edge!) behave the same way. Still, WAT?
UPDATE: It gets stranger:
x=1.0;while((x-=0.1) > 0) console.log(x.toFixed(20));
IE Chrome
0.90000000000000000000 0.90000000000000002220
0.80000000000000000000 0.80000000000000004441
0.70000000000000010000 0.70000000000000006661
0.60000000000000010000 0.60000000000000008882
0.50000000000000010000 0.50000000000000011102
0.40000000000000013000 0.40000000000000013323
0.30000000000000015000 0.30000000000000015543
0.20000000000000015000 0.20000000000000014988
0.10000000000000014000 0.10000000000000014433
0.00000000000000013878 0.00000000000000013878
Why the difference - in all but the last one? And why no difference in the last one? It's very similar for x=0.1; while(x-=0.01)..., by the way: until we get very close to zero, toFixed in IE apparently attempts to cut some corners.
Disclaimer: I do know that floating-point math is kinda flawed. What I don't understand is what's the difference between IE and the rest of the browser's world.
The reported behavior deviates from the requirements of the ECMA specification.
Per clause 8.5, the Number type has the IEEE-754 64-bit binary values, except there is only one NaN. So 2.175 cannot be represented exactly; the closest you can get is 2.17499999999999982236431605997495353221893310546875.
Per 15.7.4.5, toFixed(20) uses an algorithm that boils down to:
“Let n be an integer for which the exact mathematical value of n ÷ 10f – x is as close to zero as possible. If there are two such n, pick the larger n.”
In the above, f is 20 (the number of digits requested), and x is the operand, which should be 2.17499999999999982236431605997495353221893310546875.
This results in selecting 217499999999999982236 for n.
Then n is formatted, producing “2.17499999999999982236”.
I appreciate Eric's contribution, but, with all due respect, it doesn't answer the question. I admit I was too tongue-in-cheeky with those 'right' and 'amazingly correct' phrases; but yes, I understand that IE behavior is a deviation actually.
Anyway. I was still looking for an explanation what causes IE to behave differently - and I finally got something looking like a clue... ironically, in Mozilla's tracker, in this lengthy discussion. Quote:
OUTPUT IN MOZILLA:
a = 0.827 ==> a.toFixed(17) = 0.82699999999999996
b = 1.827 ==> b.toFixed(17) = 1.82699999999999996
OUTPUT IN IE6:
a = 0.827 ==> a.toFixed(17) = 0.82700000000000000
b = 1.827 ==> b.toFixed(17) = 1.82700000000000000
The difference seen in IE and Mozilla is as follows. IE is storing 'a' as a string
and Mozilla is storing 'a' as a value. The spec doesn't nail down the
storage format. Thus when IE does a.toFixed it starts out with a
exact string representation while Mozilla suffers the round trip conversions.
Would be great to have kind of official confirmation on this, but at least that explains everything I have seen yet. In particular,
console.log( 0.3.toFixed(20) ); // 0.30000000000000000000
console.log( 0.2.toFixed(20) ); // 0.20000000000000000000
console.log( (0.3 - 0.2).toFixed(20) ); // "0.09999999999999998000"
First of all, Floating points are can not be "precisely" represented in binary numbers. There will be an elevation/depression, either the value will be a little higher or a little lower. How much that is elevated/depressed depends on how the conversion is done. There is not exactly a "right value" even for a string output of ECMAScript's toFixed().
But the ECMA Standards do spice things up by er.. setting standards. Which is a good thing in my opinion. It's like "If we are all gonna make mistakes anyway, let's make the same one."
So, the question now would be, how and why does IE deviates from the Standards. Let's examine the following test cases.
Candidates are IE 10.0.9200.16688 and Chrome 30.0.1599.69, running on x64 Windows 8 Pro.
Case Code IE (10) Chrome (30)
--------------------------------------------------------------------------------
A (0.06).toFixed(20) 0.60000000000000000000 0.05999999999999999778
B (0.05+0.01).toFixed(20) 0.06000000000000000500 0.06000000000000000472
So, regardless it's IE or Chrome, we see (0.06) is not exactly equal to (0.05+0.01). Why is that? It's because (0.06) has a representation that is very close to but not equal to (0.06), so does (0.05) and (0.01). When we perform an operation, such as an addition, the very less significant errors can sum up to become an error of slightly different magnitude.
Now, the difference in represented value in different browsers can be impacted due to 2 reasons:
The conversion algorithm used.
When the conversion takes place.
Now we don't know what algo IE uses since I can't look into it's source. But the above test cases clearly demonstrate one other thing, IE and Chrome handles the conversion "not only differently" but also "on a different occasion".
In JavaScript, when we create a number (aka instance of a Number class with or without the new keyword), we actually provide a literal. The literal is always a string even if it denotes a number [1]. The browser parses the literal and creates the object and assigns the represented value.
Now, here's where things tend to go different ways. IE holds off the conversion until it is needed. That means until an operation takes place, IE keeps the number as literal (or some intermediary format). But Chrome converts it rightaway in the operational format.
After the operation is done, IE does not revert back to the literal format or the intermediary format, as it is pointless and may cause a slight loss in precision.
I hope that clarifies something.
[1] Value represented in code are always literals. If you quote them they are called String Literals.
We are modifying an older pre-existing web app and as part of that have begun viewing it using IE10. This app has a third party menu control (menu9_com.js?) and among the numerous issues we are noticing, is the positioning of this menu on IE7+ in Standards mode. In FF, Chrome, or any version of IE with Quirks - it is positioned correctly. In Standards mode, however, it is shoved far off to the right.
I've identified the function below as a possible source for the issue. Running in any mode, the value of StartLeft begins about the same. In the working modes it finishes at a value which - by definition - works. In the broken modes, it is much much higher.
Though it's not fully clear, I believe the function is walking up the DOM from a given target location and adding values on to calculate a "total" offset for the menu element it is adding. And I think the issue comes down to the different ways that offsetLeft (and maybe offsetParent?) are handled. So I'm trying to find the best way to get consistent behavior from this function but just not familiar enough with the intention of the function, nor with the behavior of offsetLeft etc in the various modes.
Here's the function:
function ClcTrgt() {
var TLoc=Nav4?FLoc.document.layers[TargetLoc]:DomYesFLoc.document.getElementById(TargetLoc):FLoc.document.all[TargetLoc];
if (DomYes) {
while (TLoc) {
StartTop += TLoc.offsetTop;
StartLeft += TLoc.offsetLeft;
TLoc = TLoc.offsetParent;
}
}
else {
StartTop+=Nav4?TLoc.pageY:TLoc.offsetTop;
StartLeft+=Nav4?TLoc.pageX:TLoc.offsetLeft;
}
}
Any suggestions? For example, I'd convert this function to use jQuery, if I knew how.
UPDATE:
I've posted the script on pastebin.
My current direction, in the absence of an actual fix to the script (which may not be worth the work), is adding this function to run after the script itself. I added some markup to facilitate it, and it just takes the menu, and puts it where it should be (which it right-aligned with another element I've identified). This is for from optimal, but it works.
function fixMenu9() {
var pTD = $('#pgMenuDivTD');
var pMN = $('#pgMenuDiv');
var additionalOffset = ExpYes ? 3 : 0;
var leftVal = (parseInt(pTD.offset().left) + parseInt(pTD.css('width'))) - (parseInt(pMN.css('width')) + additionalOffset);
$('#pgMenuDiv').css('left', leftVal);
}