How to identify buggy behavior in "input" event without browser detection? - javascript

I'll start with the question. When a specific browser has a buggy implementation of a feature and your javascript needs to know whether the current browser has that buggy implementation or not so it can use an alternate strategy, how do you figure out if the implementation is buggy without doing browser type sniffing (which is generally considered bad)?
Here's the whole situation.
I was working on some code that wants to use the "input" event for getting notifications of user changes to an <input type="text"> field (works much more live than the "change" event), but when that event isn't supported, it uses a much more complicated scheme involving a bunch of other events.
Since the "input" event is only supported in some browsers, I went in search of a way to do feature detection for the event (rather than browser user agent sniffing) since feature detection is generally a more robust way of doing things. As such, I came across this great article for doing exactly that and this code seems to work:
var isEventSupported = (function(){
var TAGNAMES = {
'select':'input','change':'input',
'submit':'form','reset':'form',
'error':'img','load':'img','abort':'img'
}
function isEventSupported(eventName) {
var el = document.createElement(TAGNAMES[eventName] || 'div');
eventName = 'on' + eventName;
var isSupported = (eventName in el);
if (!isSupported) {
el.setAttribute(eventName, 'return;');
isSupported = typeof el[eventName] == 'function';
}
el = null;
return isSupported;
}
return isEventSupported;
})();
Then, I ran into problems with IE (surprise, surprise). While IE purports to support the "input" event and it passes the feature test above and it works most of the time, IE's support is buggy as hell. It doesn't even trigger the event when the user hits the backspace key (among other missing behaviors). As such, I can't rely on it in IE. So, I had built this nice clean code that did a feature test for the "input" event and uses it's very clean implementation when present and when not present used this much uglier work-around involving monitoring eight other events. Now, it's busted in IE because the feature test for the "input" event passes so the code attempts to use it, but it's buggy as hell so it doesn't work.
Since these IE bugs show up on user actions, I can't think of any way to devise a javascript feature test to identify the buggy behavior. As such, my only current path is to resort to browser sniffing and refuse to rely on the "input" tag if the browser is IE.
Are there any options here for identifying the buggy behavior in the "input" event besides browser sniffing? If one had to do browser sniffing, is there a way to identify IE by behavior rather than a user agent string that can be freely spoofed and isn't guaranteed to be accurate?

jamie-pate suggest something like this:
var broken = false,
ta = angular.element('<textarea>').on('input', function(evt) {
broken = true;
});
ta.attr('placeholder', 'IESUCKS');
So you can check for "supports input event and is not 'broken'" in your code.
See https://github.com/angular/angular.js/issues/2614?source=c

If you are interested in a cross-browser "input change" event, here is my implementation:
function onInputChange(domInput, callback) {
if (domInput.addEventListener) {
domInput.addEventListener('input', callback, false); // Firefox, etc.
} else if (domInput.attachEvent) {
domInput.attachEvent('onpropertychange', callback); // IE
}
}
Usage example:
var leInput = document.getElementById('myInput');
var leCallback = function () {
// awesome stuff here
};
onInputChange(leInput, leCallback);
Works in all browsers, supports keyboard input and copy/paste.
However there is the exception of that cursed IE9, which didn't exist at the time I wrote the above code. Would Microsoft ever consider fixing their bug? :\

Related

visibilitychange event is not triggered when switching program/window with ALT+TAB or clicking in taskbar

The problem is with the behaviour of the event "visibilitychange".
It's triggered:
- When I switch to a different tab inside the browser window.
When I click in minimize / restore buttons for the browser window.
(this is ok)
It's not triggered:
- When I switch to a different window/program using ALT+TAB.
When I switch to a different window/program clicking on taskbar.
(this SHOULD trigger, because, just like when minimizing, the window's visibility may change)
W3 Page Visibility API Documentation: http://www.w3.org/TR/page-visibility/
There is no definition of "page visibility" regarding ALT+TAB/program switching in the spec sheet. I'm guessing it has something to do in between the OS and the Browser.
TESTED IN
Browsers:
Chrome 40.0.2214.115 m / Firefox 36.0.1 / Internet Explorer 11.0.9600.17107
OS: Windows 8.1
Is there a workaround to fix this behaviour? The implementation is fairly simple, I listen to the "visibilitychange" event using jQuery, and then in its callback, I check for the value of "document.visibilityState", but the problem is that the event is not firing when expected.
$(document).on('visibilitychange', function() {
if(document.visibilityState == 'hidden') {
// page is hidden
} else {
// page is visible
}
});
This can be done without jQuery too, but the ALT+TAB and taskbar switch hide/show expected behaviour is still missing:
if(document.addEventListener){
document.addEventListener("visibilitychange", function() {
// check for page visibility
});
}
I've also tried the ifvisible.js module (https://github.com/serkanyersen/ifvisible.js) but the behaviour is the same.
ifvisible.on('blur', function() {
// page is hidden
});
ifvisible.on('focus', function() {
// page is visible
});
I haven't tested in other browsers because if I can't make it work in Chrome on Windows I really don't care about the other browsers yet.
Any help or suggestions?
UPDATE
I tried using different vendor prefixes for the event name (visibilitychange, webkitvisibilitychange, mozvisibilitychange, msvisibilitychange) but but still the event is not triggered when I switch to a different program in the taskbar or ALT+TAB, or even if I open the start menu thing in windows with the windows key, which covers the whole screen.
I can reproduce the exact same issue in Chrome, Firefox and Internet Explorer.
UPDATE #2
Here's a roundup post I wrote for this issue and a workaround in pure Javascript to solve the encountered problems.
UPDATE #3
Edited to include a copy of the sourced blog post. (see accepted answer)
Here's a roundup post I wrote for this issue and a workaround in pure JavaScript to solve the encountered problems.
Edited to include a copy of the sourced blog post:
In any kind of javascript application we develop there may be a
feature or any change in the application which reacts according to the
current user visibility state, this could be to pause a playing video
when the user ALT+TABs to a different window, tracking stats about how
the users interact with our application, how often does him switch to
a different tab, how long does it take him to return and a lot of
performance improvements that can benefit from this kind of API.
The Page Visibility API provides us with two top-level attributes:
document.hidden (boolean) and document.visibilityState (which could be
any of these strings: “hidden”, “visible”, “prerender”, “unloaded”).
This would not be not good enough without an event we could listen to
though, that’s why the API also provides the useful visibilitychange
event.
So, here’s a basic example on how we could take action on a visibility
change:
function handleVisibilityChange() {
if(document.hidden) {
// the page is hidden
} else {
// the page is visible
}
}
document.addEventListener("visibilitychange", handleVisibilityChange, false);
We could also check for document.visibilityState value.
Dealing with vendor issues George Berkeley by John Smibert
Some of the implementations on some browsers still need that the
attributes or even the event name is vendor-prefixed, this means we
may need to listen to the msvisibilitychange event or check for the
document.webkitHidden or the document.mozHidden attributes. In order
to do so, we should check if any vendor-prefixed attribute is set, and
once we know which one is the one used in the current browser (only if
there’s the need for a prefix), we can name the event and attributes
properly.
Here’s an example approach on how to handle these prefixes:
var browserPrefixes = ['moz', 'ms', 'o', 'webkit'];
// get the correct attribute name
function getHiddenPropertyName(prefix) {
return (prefix ? prefix + 'Hidden' : 'hidden');
}
// get the correct event name
function getVisibilityEvent(prefix) {
return (prefix ? prefix : '') + 'visibilitychange';
}
// get current browser vendor prefix
function getBrowserPrefix() {
for (var i = 0; i < browserPrefixes.length; i++) {
if(getHiddenPropertyName(browserPrefixes[i]) in document) {
// return vendor prefix
return browserPrefixes[i];
}
}
// no vendor prefix needed
return null;
}
// bind and handle events
var browserPrefix = getBrowserPrefix();
function handleVisibilityChange() {
if(document[getHiddenPropertyName(browserPrefix )]) {
// the page is hidden
console.log('hidden');
} else {
// the page is visible
console.log('visible');
}
}
document.addEventListener(getVisibilityEvent(browserPrefix), handleVisibilityChange, false);
Other issues There is a challenging issue around the “Page Visibility”
definition: how to determine if the application is visible or not if
the window focus is lost for another window, but not the actual
visibility on the screen? what about different kinds of visibility
lost, like ALT+TAB, WIN/MAC key (start menu / dash), taskbar/dock
actions, WIN+L (lock screen), window minimize, window close, tab
switching. What about the behaviour on mobile devices?
There’s lots of ways in which we may lose or gain visibility and a lot
of possible interactions between the browser and the OS, therefore I
don’t think there’s a proper and complete “visible page” definition in
the W3C spec. This is the definition we get for the document.hidden
attribute:
HIDDEN ATTRIBUTE On getting, the hidden attribute MUST return true if
the Document contained by the top level browsing context (root window
in the browser’s viewport) [HTML5] is not visible at all. The
attribute MUST return false if the Document contained by the top level
browsing context is at least partially visible on at least one screen.
If the defaultView of the Document is null, on getting, the hidden
attribute MUST return true.
To accommodate accessibility tools that are typically full screen but
still show a view of the page, when applicable, this attribute MAY
return false when the User Agent is not minimized but is fully
obscured by other applications.
I’ve found several inconsistencies on when the event is actually
fired, for example (Chrome 41.0.2272.101 m, on Windows 8.1) the event
is not fired when I ALT+TAB to a different window/program nor when I
ALT+TAB again to return, but it IS fired if I CTRL+TAB and then
CTRL+SHIFT+TAB to switch between browser tabs. It’s also fired when I
click on the minimize button, but it’s not fired if the window is not
maximized and I click my editor window which is behing the browser
window. So the behaviour of this API and it’s different
implementations are still obscure.
A workaround for this, is to compensate taking advantage of the better
implemented focus and blur events, and making a custom approach to the
whole “Page Visibility” issue using an internal flag to prevent
multiple executions, this is what I’ve come up with:
var browserPrefixes = ['moz', 'ms', 'o', 'webkit'],
isVisible = true; // internal flag, defaults to true
// get the correct attribute name
function getHiddenPropertyName(prefix) {
return (prefix ? prefix + 'Hidden' : 'hidden');
}
// get the correct event name
function getVisibilityEvent(prefix) {
return (prefix ? prefix : '') + 'visibilitychange';
}
// get current browser vendor prefix
function getBrowserPrefix() {
for (var i = 0; i < browserPrefixes.length; i++) {
if(getHiddenPropertyName(browserPrefixes[i]) in document) {
// return vendor prefix
return browserPrefixes[i];
}
}
// no vendor prefix needed
return null;
}
// bind and handle events
var browserPrefix = getBrowserPrefix(),
hiddenPropertyName = getHiddenPropertyName(browserPrefix),
visibilityEventName = getVisibilityEvent(browserPrefix);
function onVisible() {
// prevent double execution
if(isVisible) {
return;
}
// change flag value
isVisible = true;
console.log('visible}
function onHidden() {
// prevent double execution
if(!isVisible) {
return;
}
// change flag value
isVisible = false;
console.log('hidden}
function handleVisibilityChange(forcedFlag) {
// forcedFlag is a boolean when this event handler is triggered by a
// focus or blur eventotherwise it's an Event object
if(typeof forcedFlag === "boolean") {
if(forcedFlag) {
return onVisible();
}
return onHidden();
}
if(document[hiddenPropertyName]) {
return onHidden();
}
return onVisible();
}
document.addEventListener(visibilityEventName, handleVisibilityChange, false);
// extra event listeners for better behaviour
document.addEventListener('focus', function() {
handleVisibilityChange(true);
}, false);
document.addEventListener('blur', function() {
handleVisibilityChange(false);
}, false);
window.addEventListener('focus', function() {
handleVisibilityChange(true);
}, false);
window.addEventListener('blur', function() {
handleVisibilityChange(false);
}, false);
I welcome any feedback on this workaround. Some other great sources
for ideas on this subject:
Using the Page Visibility API Using PC Hardware more efficiently in
HTML5: New Web Performance APIs, Part 2 Introduction to the Page
Visibility API Conclusion The technologies of the web are continuously
evolving, we’re still recovering from a dark past where tables where
the markup king, where semantics didn’t mattered, and they weren’t any
standards around how a browser should render a page.
It’s important we push these new standards forward, but sometimes our
development requirements make us still need to adapt to these kind of
transitions, by handling vendor prefixes, testing in different
browsers and differents OSs or depend on third-party tools to properly
identify this differences.
We can only hope for a future where the W3C specifications are
strictly revised, strictly implemented by the browser developer teams,
and maybe one day we will have a common standard for all of us to work
with.
As for the Page Visibility API let’s just kinda cite George Berkeley
and say that:
“being visible” is being perceived.
A working solution is proposed described here: https://stackoverflow.com/a/9502074/698168. It uses a combination of the W3C Page Visibility API, blur/focus and mouse movements. Hidden HTML pages related to Alt+Tab are identified in a probabilistic way (i.e. you cannot determine if your page is hidden with 100% accuracy).
we can do like below when switching between tabs and switching between applications
var pageVisible = true;
function handleVisibilityChange() {
if (document.hidden) {
pageVisible = false;
} else {
pageVisible = true;
}
console.log("handleVisibilityChange")
console.log("pageVisible", pageVisible)
// some function call
}
document.addEventListener("visibilitychange", handleVisibilityChange, false);
window.addEventListener('focus', function() {
pageVisible = true;
// some function call
}, false);
window.addEventListener('blur', function() {
pageVisible = false;
// some function call
}, false);
There's a very simple solution to this I have come across.
You just need to pass false to the useCapture while attaching an event listener to the document. Works like a charm!
document.addEventListener('visibilitychange', function () {
// code goes here
}, false)

Utilizing Firefox's default/built-in Event Listeners

I have a context menuitem which is activated if an image is right-clicked, the exact same way that 'context-copyimage' is activated.
Is it possible to tie/pair that menuitem to the 'context-copyimage' therefore eliminating the need to add extra (duplicate) event-listeners and show/hide handlers??!!
(Adding an observer to 'context-copyimage' defeats the purpose)
If not, is it possible to use the event-listener that 'context-copyimage' uses?
Update:
I am trying to reduce listeners. At the moment, script has a popupshowing listeners. On popupshowing, it checks for gContextMenu.onImag and if true, it shows the menuitem. Firefox's context-copyimage does the exact same thing. I was wondering if it was possible to tie these 2 in order to remove/reduce the in-script event listeners.
I was also chatting with Dagger and he said that:
... the state of built-in items isn't set from an event handler, it's
set from the constructor for nsContextMenu, and there are no
mechanisms to hook into it
So it seems, that is not possible
No, there is no sane way of avoiding the event listener that would perform better than another event listener and is compatible with unloading the add-on in session.
Hooking nsContextMenu
As you have been already told, the state is initialized via gContextMenu = new nsContextMenu(...). So you'd need to hook the stuff, which is actually quite easy.
var newProto = Object.create(nsContextMenu.prototype);
newProto.initMenuOriginal = nsContextMenu.prototype.initMenu;
newProto.initMenu = function() {
let rv = this.initMenuOriginal.apply(this, arguments);
console.log("ctx", this.onImage, this); // Or whatever code you'd like to run.
return rv;
};
nsContextMenu.prototype = newProto;
Now, the first question is: Does it actually perform better? After all this just introduced another link in the prototype-chain. Of course, one could avoid Object.create and just override nsContextMenu.prototype.initMenu directly.
But the real question is: How would one remove the hook again? Answer: you really cannot, as other add-ons might have hooked the same thing after you and unhooking would also unhook the other add-ons. But you need to get rid of the reference, or else the add-on will leak memory when disabled/uninstalled. Well, you could fight with Components.utils.makeObjectPropsNormal, but that doesn't really help with closed-over variables. So lets avoid closures... Hmm... You'd need some kind of messaging, e.g. event listeners or observers... and we're back to square one.
Also I wouldn't call this sane compared to
document.getElementById("contentAreaContextMenu").addEventListener(...)
I'd call it "overkill for no measurable benefit".
Overriding onpopupshowing=
One could override the <menupopup onpopupshowing=. Yeah, that might fly... Except that other add-ons might have the same idea, so welcome to compatibility hell. Also this again involves pushing stuff into the window, which causes cross-compartment wrappers, which makes things error-prone again.
Is this a solution? Maybe, but not a sane one.
What else?
Not much, really.
Yes this is absolutely possible.
Morat from mozillazine gave a great solution here: http://forums.mozillazine.org/viewtopic.php?p=13307339&sid=0700480c573017c00f6e99b74854b0b2#p13307339
function handleClick(event) {
window.removeEventListener("click", handleClick, true);
event.preventDefault();
event.stopPropagation();
var node = document.popupNode;
document.popupNode = event.originalTarget;
var menuPopup = document.getElementById("contentAreaContextMenu");
var shiftKey = false;
gContextMenu = new nsContextMenu(menuPopup, shiftKey);
if (gContextMenu.onImage) {
var imgurl = gContextMenu.mediaURL || gContextMenu.imageURL;
}
else if (gContextMenu.hasBGImage && !gContextMenu.isTextSelected) {
var imgurl = gContextMenu.bgImageURL;
}
console.log('imgurl = ', imgurl)
document.popupNode = node;
gContextMenu = null;
}
window.addEventListener("click", handleClick, true);
this gives you access to gContextMenu which has all kinds of properties like if you are over a link, or if you right click on an image, and if you did than gContextMenu.imageURL holds its value. cool stuff
This code here console logs imgurl, if you are not over an image it will log undefined

Easy way to detect support for transitionend event without frameworks like jQuery or Modernizr?

Have anybody out there found a simple way of detecting whether the browser supports the transitionend event or not in vanillaJs, especially in a way that actually works in all major browsers? :(
I have found this unanswered thread in here: Test for transitionend event support in Firefox, and quite a lot of almost working hacks.
Right now I am bulk adding eventlisteners to all the vendor prefixes, and it kind of works out (even though I think it is a hideous approach that hurts my eyes every time I look at it). But IE8 and IE9 does not support it at all, so I need to detect those two, and treat them separately.
I would prefer to do this without browser sniffing, and definitely without huge libraries/frameworks like jQuery
I have made a jsfiddler snippet that illustrates my problem. There is a button that spawns a dialog. When the dialog is removed by clicking close, it is transitioned in top and opacity, and after ended transition it is supposed to display=none. But if the transitionend is never fired (like in IE8 and IE9), the dialog is never removed, making it cover the show dialog button, destroying the UX. If I could detect when transitionend is not working, I could just set the display=none when closing for those browsers.
http://jsfiddle.net/QJwzF/22/
window.listenersSet = false;
window.dialogShouldBeVisible = false;
window.showMyDialog = function () {
var myDialog = document.getElementById('dialog'),
myClose = document.getElementById('close');
if (!listenersSet) {
if (!window.addEventListener) { // IE8 has no addEventListener
myclose.attachEvent('onclick', function () {
hideMyDialog();
});
} else {
myClose.addEventListener('click', function () {
hideMyDialog()
});
['webkitTransitionEnd','oTransitionEnd', 'otransitionend', 'transitionend'].forEach(function (eventName) {
myDialog.addEventListener(eventName, function (e) {
if (e.target === myDialog && e.propertyName === 'top') { // only do trigger if it is the top transition of the modalDialog that has ended
if (!dialogShouldBeVisible) {
myDialog.style.display = 'none';
e.stopPropagation();
}
}
});
});
}
listenersSet = true;
}
myDialog.style.display = 'block';
myDialog.style.top = '15px';
myDialog.style.opacity = '1';
dialogShouldBeVisible = true;
}
window.hideMyDialog = function () {
var myDialog = document.getElementById('dialog'),
myClose = document.getElementById('close');
myDialog.style.top = '-5%';
myDialog.style.opacity = '0.1';
dialogShouldBeVisible = false;
}
It is working in Opera, Firefox, Chrome and IE10, but not in IE8 and IE9 (afaik)
If I did a bad job explaining my problem, please let me know, and I will try do a better job! :)
Copied from bootstrap transition, it not only returns true if browser supports transition but also returns proper name of event
function transitionEnd() {
var el = document.createElement('div')//what the hack is bootstrap
var transEndEventNames = {
WebkitTransition : 'webkitTransitionEnd',
MozTransition : 'transitionend',
OTransition : 'oTransitionEnd otransitionend',
transition : 'transitionend'
}
for (var name in transEndEventNames) {
if (el.style[name] !== undefined) {
return transEndEventNames[name];
}
}
return false // explicit for ie8 ( ._.)
}
Hope this helps.
EIDT:
I modified a little bit default bootstrap function so it doesn't return object but string.
I would definitely use this small script available on Github.
It's listed among Modernizr "Cross-browser polyfills" page so it can be trusted but Modernizr itself is not required.
The examples in the Github page of the script are written using jQuery (and I can't understand why) but jQuery is also not required as it's written in vanilla js.
Like so you'll have a useful whichTransitionEnd method available. I can't test it right now being on my laptop without IE8/IE9 available but I guess that this method will return false (or anything falsy) in those browsers.
var transition = transitionEnd(box).whichTransitionEnd();
// return for example "webkitTransitionEnd"
It will then be quite easy to target those browsers where transitions (and thus transitionend events) are not supported. Hope this will be a nudge in the right direction.
EDIT
After tweaking with the above code the OP came up with a much shorter version of the original script. It saves a good deal of bytes and only detects support for this event, and, in case it's supported returns the name of the event itself.
You can find it here as a gist and read more about it in the comments to this answer.

HTML5 DnD dataTransfer setData or getData not working in every browser except Firefox

Consider this JSFiddle. It works fine in Firefox (14.0.1), but fails in Chrome (21.0.1180.75), Safari (?) and Opera(12.01?) on both Windows (7) and OS X (10.8). As far as I can tell the issue is with either the setData() or getData() methods on the dataTransfer object. Here's the relevant code from the JSFiddle.
var dragStartHandler = function (e) {
e.originalEvent.dataTransfer.effectAllowed = "move";
e.originalEvent.dataTransfer.setData("text/plain", this.id);
};
var dragEnterHandler = function (e) {
// dataTransferValue is a global variable declared higher up.
// No, I don't want to hear about why global variables are evil,
// that's not my issue.
dataTransferValue = e.originalEvent.dataTransfer.getData("text/plain");
console.log(dataTransferValue);
};
As far as I can tell this should work perfectly fine and if you look at the console while dragging an item you will see the id written out, which means that it's finding the element just fine and grabbing it's id attribute. The question is, is it just not setting the data or not getting the data?
I'd appreciate suggestions because after a week of working on this with three attempts and some 200+ versions, I'm starting to loose my mind. All I know is it used to work back in version 60 or so and that specific code hasn't changed at all...
Actually, one of the major differences between 6X and 124 is that I changed the event binding from live() to on(). I don't think that's the issue, but I've come to see a couple failures from Chrome when it comes to DnD while working on this. This has been debunked. The event binding method has no effect on the issue.
UPDATE
I've created a new JSFiddle that strips out absolutely everything and just leaves the event binding and handlers. I tested it with jQuery 1.7.2 and 1.8 with both on() and live(). The issue persisted so I dropped down a level and removed all frameworks and used pure JavaScript. The issue still persisted, so based on my testing it's not my code that's failing. Instead it appears that Chrome, Safari and Opera are all implementing either setData() or getData() off spec or just failing for some reason or another. Please correct me if I'm wrong.
Anyway, if you take a look at the new JSFiddle you should be able to replicate the issue, just look at the console when you're dragging over an element designated to accept a drop. I've gone ahead and opened a ticket with Chromium. In the end I may still be doing something wrong, but I simply don't know how else to do DnD at this point. The new JSFiddle is as stripped down as it can get...
Ok, so after a bit more digging around, I found that the problem actually isn't with Chrome, Safari, and Opera. What gave it away was that Firefox was supporting it and I just couldn't say the other browsers are failing, since that's something I'd normally accept for IE.
The real cause of the issue is the DnD specification itself. According to the spec for the drag, dragenter, dragleave, dragover and dragend events the drag data store mode is protected mode. What is protected mode you ask? It is:
For all other events. The formats and kinds in the drag data store
list of items representing dragged data can be enumerated, but the
data itself is unavailable and no new data can be added.
That translates to, "you have no access to the data that you set, not even in read only mode! Go f#&# yourself.". Really? Who'se the genius that came up with this?
Now, to get around that limitation you have few choices, and I'm only going to outline two that I've come up with. Your first one is to use an evil global variable and pollute the global namespace. Your second choice is to use the HTML5 localStorage API to perform the EXACT same functionality that the DnD API should have provided to begin with!
If you go down this route, which I have, you're now implementing two HTML5 APIs not because you want to, but because you have to. Now I'm starting to appreciate PPK's rant about the disaster that the HTML5 DnD API is.
The bottom line is this, the spec needs to be changed to allow for access to the stored data even if it's only in read only mode. In my case, with this JSFiddle, I'm actually using the dragenter as a way to look ahead at the drop zone and verify that I should allow a drop to occur or not.
In this case Mozilla apparently opted out of full compliance with the spec which is why my JSFiddle was working just fine in it. It just so happens that this is the one time I fully support not supporting the full specification.
There is a reason for the "protected" bit....drag/drop can span completely different windows, and they didn't want somebody to be able to implement a "listener" DIV that would eavesdrop on the content of everything that was dragged over it (and maybe send those contents by AJAX to some spy server in Elbonia). Only the DROP area (which is more clearly under the user's control) gets the full scoop.
Annoying, but I can see why it might be considered necessary.
var dragStartHandler = function (e) {
e.originalEvent.dataTransfer.effectAllowed = "move";
e.originalEvent.dataTransfer.setData("text/plain", this.id);
};
The problem is with the "text/plain". The standard specification in MSDN documentation for setData is just "text" (without the /plain). Chrome accepts the /plain, but IE does not, in any version I tried.
I struggled with the same problem for several weeks, trying to figure out why my "drop" events weren't firing properly in IE while they did in CHrome. It was because the dataTransfer data hadn't been properly loaded.
I know you already answered this, but this is a useful thread -- I just wanted to add an addendum here -- if you're setting the data yourself, you can always add the data into the field itself (ugly I know), but it prevents having to re-create functionality:
For instance, if setting your own custom data:
dataTransfer.setData('mycustom/whatever', 'data');
append the data as a new data entry, and iterate:
dataTransfer.setData('mycustom/whatever/data/{a json encoded string even}');
querying:
// naive webkit only look at the datatransfer.types
if (dataTransfer.types.indexOf('mycustom/whatever') >= 0) {
var dataTest = 'mycustom/whatever/data/';
// loop through types and create a map
for (var i in types) {
if (types[i].substr(0, dataTest.length) == dataTest) {
// shows:
// {a json encoded string even}
console.log('data:', types[i].substr(dataTest.length));
return; // your custom handler
}
}
}
tested in chrome only
Something also worth noting is that if you leave the execution chain using a timeout, the dataTransfer object won't have your data anymore.
e.g.
function dropEventHandler(event){
var dt = event.dataTransfer.getData("text/plain"); // works
var myEvent = event;
setTimeout(function(){
var dt = myEvent.dataTranfer.getData("text/plain"); // null
}, 1);
}
I was getting same error for below code:
event.originalEvent.dataTransfer.setData("text/plain",
event.target.getAttribute('id'));
I Changed code to:
event.originalEvent.dataTransfer.effectAllowed = "move";
event.originalEvent.dataTransfer.setData("text", event.target.getAttribute('id'));
And it worked for me.
I came across this post because I was having a similar experience with Chrome's dataTransfer.setData() and dataTransfer.getData() functions.
I had code that looked something like this:
HTML:
<div ondragstart="drag(event)" ondrop="newDrop(event)"></div>
JAVASCRIPT:
function drag(ev) {
ev.dataTransfer.setData("text", ev.target.id);
}
function newDrop(ev){
var itemDragged = ev.dataTransfer.getData("text");
var toDropTo = ev.target.id;
}
The code worked perfectly fine in Internet Explorer but when it was tested in Chrome, I was unable to get values set in my dataTransfer object (set in drag function) using the dataTransfer.getData() function in the newDrop function. I was also unable to get the id value from the statement ev.target.id.
After some digging around on the web, I discovered that I was suppose to use the event parameters currentTarget property rather than the events target property. Updated code looked something like this:
JAVASCRIPT:
function drag(ev) {
ev.dataTransfer.setData("text", ev.currentTarget.id);
}
function newDrop(ev){
var itemDragged = ev.dataTransfer.getData("text");
var toDropTo = ev.currentTarget.id;
}
With this change I was able to use the dataTransfer.setData() and dataTransfer.getData() functions in chrome as well as internet explorer. I have not tested anywhere else and I am not sure why this worked. Hope this helps and hopefully someone can give an explanation.
I was working on a website testing with Firefox.
In WAMP on my laptop, code like the OP's worked. However, when I moved the website to HOSTMONSTER, it didn't work there.
I followed Joshua Espana's answer, and it resolved my problem.
failed:
ev.dataTransfer.setData("text/plain", ev.target.id);
worked:
ev.dataTransfer.setData("text", ev.currentTarget.id);
Thank, Joshua!
Anything pased to the dataTransfer only becomes available on ondrop events but ONLY on ondrop events (I believe this is a security consideration to prevent data being exposed to nefarious elements during a drag).
If you try adding an ondrop handler you should see the data exposed. Well at least you would if there weren't for one final trick...
To get the drop event to fire you need call .preventDefault on the dragover event or it prevents the drop event from firing
HTML (Angular)
<div (dragstart)="handleDragStart($event)"
(dragover)="handleDragover($event)"
(dragend)="handleDragEnd($event)"
(drop)="handleDrop($event)">
<div class="sortItem">item 1</div>
<div class="sortItem">item 2</div>
<div class="sortItem" draggable="true">Draggable</div>
<div class="sortItem">item 4</div>
</div>
Handlers (Typescript)
handleDragStart(event: DragEvent){
event.dataTransfer?.setData("text", '{"some": "data"}')
console.log('dragstart data:', event.dataTransfer?.getData("text"))
}
handleDragover(event: DragEvent) {
console.log('dragover data:', event.dataTransfer?.getData("text") || 'none')
event.preventDefault()
}
handleDragEnd(event: DragEvent) {
console.log('drag end data:', event.dataTransfer?.getData("text") || 'none')
}
handleDrop(event: DragEvent) {
console.log('drag drop data:', event.dataTransfer?.getData("text") || 'none')
}
Output from the above
If you drop the item INSIDE the container with the ondrop handler
If you don't cancel the dragover event...
If you drop the item OUTSIDE container with the ondrop handler
If you don't cancel the dragover event...
Other relevant SO questions
SO: Data only available on drop
SO: Drop event not firing
I also faced with this problem but no way worked for me when I set the data in ondrag function and get it back it gives the data but when try in other events like dragover and drop it dose not work.
I went this way maybe its not a proper answer but may help some one
let selectedId = 0;
function onDrag(params) {
// event.dataTransfer.setData("text",params);
selectedId = params
}
function onDragOver(params) {
// event.preventDefault()
// const data = event.dataTransfer.getData("text");
let id = selectedId
}

javascript: How to debug keyboard events

Imagine a web application which uses custom keyboard event handlers which might do event bubbling - or event catching.
Is there a way (e.g. Firefox/Firebug addon) to debug each keystroke/keyboard event, something like:
displaying the event type and all attributes
trace which javascript method had been called
in case of event bubbling which further methods have been called
To clarify my question: I don't know which method handlers exist and where they are defined - this is what I am trying to find out.
You could try to visualize the vents with the Firebug + Eventbug extension.
For a general overview on keyboard events in different browsers, try this: http://unixpapa.com/js/key.html
In IE you can use the debugger; keyword. Not sure about the x-browser friendliness:
function sayHello() {
debugger; // will break here and launch script debugging in IE
alert('hello world');
}
In the context of your challenge:
function someKeyPress(e) {
debugger;
// inspect e.keyCode ... etc
}
I find this to be the most effective debugging technique, but then again I spend a lot of time in IE. Many folks are comfy with Firebug but I find it cumbersom and far less intuitive than IE's debugger. IE's debugger provides for easier watches and expression evaluation, and also provides interactive reflection based tooltips (like VS debugger).
Also, per your question "trace what method was called" - the call stack is very responsive and easy to follow back/up.
UPDATE:
Here's a script to place on the bottom of each page to trap and debug events, in IE:
<script type="text/javascript">
function wrapIfHandled(el, evt) {
if (el && evt && el['on' + evt]) {
el['_on' + evt] = el['on' + evt];
el['on' + evt] = function (e) {
foo(e, el['_on' + evt]);
};
}
}
function wrapAll() {
var allEl = document.getElementsByTagName("*");
for (var i = 0; i < allEl.length; i++) {
wrapIfHandled(allEl[i], 'click');
// wrapIfHandled(allEl[i], other event names <keyup, keydown, etc>
}
}
function foo(e, d) {
debugger;
d(e);
}
wrapAll();
</script>
I don't know any extensions for this purpose. But, you can write handlers for key events and then print appropriate output to the javascript console. Also you can dump traces too. Firebug has builtin trace functionality: console.trace(). You can google with js trace keywords to find some trace tools too.
This page is a good example to handle keyboard events.

Categories