I like to add a background on a div only for browsers which support drag & drop for files
I don't like to use modernizr though, just a one line script
Why not just copy required parts from Modernizr?
var isEventSupported = (function() {
var TAGNAMES = {
'select': 'input', 'change': 'input',
'submit': 'form', 'reset': 'form',
'error': 'img', 'load': 'img', 'abort': 'img'
};
function isEventSupported( eventName, element ) {
element = element || document.createElement(TAGNAMES[eventName] || 'div');
eventName = 'on' + eventName;
// When using `setAttribute`, IE skips "unload", WebKit skips "unload" and "resize", whereas `in` "catches" those
var isSupported = eventName in element;
if ( !isSupported ) {
// If it has no `setAttribute` (i.e. doesn't implement Node interface), try generic element
if ( !element.setAttribute ) {
element = document.createElement('div');
}
if ( element.setAttribute && element.removeAttribute ) {
element.setAttribute(eventName, '');
isSupported = typeof element[eventName] == 'function';
// If property was created, "remove it" (by setting value to `undefined`)
if ( typeof element[eventName] != 'undefined' ) {
element[eventName] = undefined;
}
element.removeAttribute(eventName);
}
}
element = null;
return isSupported;
}
return isEventSupported;
})();
Usage:
if (isEventSupported('dragstart') && isEventSupported('drop')) {
...
}
And for File API:
var isFileAPIEnabled = function() {
return !!window.FileReader;
};
You can use:
return 'draggable' in document.createElement('span') && typeof(window.FileReader) != 'undefined';
If you don't want to deal with Modernizr at all, you can just replicate what it does for drag'n'drop detection:
var supportsDragAndDrop = function() {
var div = document.createElement('div');
return ('draggable' in div) || ('ondragstart' in div && 'ondrop' in div);
}
Got it from Modernizr GitHub repository:
https://github.com/Modernizr/Modernizr/blob/master/feature-detects/draganddrop.js
checkout modernizr source code technique for the HTML5 drag and drop detection https://github.com/Modernizr/Modernizr/blob/master/feature-detects/draganddrop.js
except this seems to incorrectly detect iOS as supporting drag and drop
Not sure why everybody needs to create a new element to check this. I think it's enough to just check that the body element supports dragging events and that the browser supports the File API
supportsDragAndDrop(){
body = document.body
return ("draggable" in body || ("ondragstart" in body && "ondrop" in body))
&& window.FormData && window.FileReader
}
Related
This proposed solution seems to work quite well in most scenarios but I noticed it fails to detect support for translate3d properties when a <script> tag is used within the <body>.
Reproduction online
This is the function used to detect the 3d support:
function has3d() {
if (!window.getComputedStyle) {
return false;
}
var el = document.createElement('p'),
has3d,
transforms = {
'webkitTransform':'-webkit-transform',
'OTransform':'-o-transform',
'msTransform':'-ms-transform',
'MozTransform':'-moz-transform',
'transform':'transform'
};
// Add it to the body to get the computed style.
document.body.insertBefore(el, null);
for (var t in transforms) {
if (el.style[t] !== undefined) {
el.style[t] = "translate3d(1px,1px,1px)";
has3d = window.getComputedStyle(el).getPropertyValue(transforms[t]);
}
}
document.body.removeChild(el);
return (has3d !== undefined && has3d.length > 0 && has3d !== "none");
}
I have made a JavaScript tab view of a simple HTML page.
I've added onClick functions for header tags using JavaScript via nodes.
The onClick function performs a function called showTab passing on this as a parameter.
I understand that this is [object window].
The header tag onClick functions are set as shown below:
node.onclick = function() { showTab(this); };
The showTab function is as follows:
function showTab(e)
{
var node = (e && e.target) || (window.event && window.event.srcElement);
alert(node.innerHTML);
}
Everything works fine, when i click on one of the headers, an alert appears with its innerHTML.
However, I did use a little help from Google to achieve this. And I would like some help understanding exactly what this line means:
var node = (e && e.target) || (window.event && window.event.srcElement);
I did my own research and saw it can be considered as the equivalent as sender in C#.
But I would like to know thoroughly how it works and what it is referring to and how it knows which node is calling the showTab function as there are 3 header tags that perform the same function, all without id's.
Ah, the joys of dealing with Events and browser.
The Trident Engine (Internet explorer and others based on that engine) deals with events differently than most (all?) of the other browsers.
<html>
<head>
<title>Test</title>
</head>
<body>
<button id="test_button">Click me</button>
<script>
// UGLY, UGLY, UGLY... don't really use this
var button = document.getElementById("test_button");
if (window.attachEvent) {
button.attachEvent("onclick", showTab);
} else {
button.addEventListener("click", showTab);
}
function showTab(e)
{
// Most browsers pass the event as 'e'
// Microsoft puts the event in window.event
// Either way, event will now point to the object we want.
var event = e || window.event;
// Once again, the different browsers handle the `target` property differently.
// Target should now point to the right event.
var target = event.target || event.srcElement;
alert(target.innerHTML);
}
</script>
</body>
This line:
var node = (e && e.target) || (window.event && window.event.srcElement);
is equivalent to this logic:
var node;
if (e && e.target) {
node = e.target;
} else if (window.event && window.event.srcElement) {
node = window.event.srcElement;
} else {
node = undefined;
}
The purpose of this code is to handle the fact rhat older versions of IE don't pass the event structure to an event handler. Instead, it is stored in a global variable window.event and the event target is also stored in a difference property of the event.
It is a bit more common (and I think more readable) to do something like this:
function showTab(e) {
// get the event data structure into e
e = e || window.event;
// get the source of the event
var node = e.target || e.srcElement;
alert(node.innerHTML);
}
In reality, any decent size project should use a library function for abstracting the differences in event handlers so that this browser-specific code only has to be one place in the project or use a pre-built library like jQuery for this type of thing. Here's a cross-browser event handler:
// refined add event cross browser
function addEvent(elem, event, fn) {
if (typeof elem === "string") {
elem = document.getElementById(elem);
}
function listenHandler(e) {
var ret = fn.apply(this, arguments);
if (ret === false) {
e.stopPropagation();
e.preventDefault();
}
return(ret);
}
function attachHandler() {
// older versions of IE
// set the this pointer same as addEventListener when fn is called
// make sure the event is passed to the fn also so that works the same too
// normalize the target of the event
window.event.target = window.event.srcElement;
var ret = fn.call(elem, window.event);
if (ret === false) {
window.event.returnValue = false;
window.event.cancelBubble = true;
}
return(ret);
}
if (elem.addEventListener) {
elem.addEventListener(event, listenHandler, false);
} else {
elem.attachEvent("on" + event, attachHandler);
}
}
It's getting the dom element which was clicked, either e.target for standards compliant browsers or window.event.srcElement (could be e.srcElement instead for newer IE)
see: http://www.quirksmode.org/js/events_properties.html
I'm trying to code a simple form with file upload which targets the iFrame. I've got it pretty much covered, but Internet Explorer does not support load method on dynamically generated iFrames so I need to do an alternative approach for it. My question is - what's the best and most accurate (plus light) way of identifying the browser type using Javascript?
I know that Modernizr can check for specific methods / properties, but not quite sure if it could help in this specific scenario. It has Modernizr.hasEvent(), but I can't use it to check the dynamically created elements.
The easiest way to check this, to my mind would be:
if ('onload' in iFrameVar)
{
console.log('your code here');
}
Where iFrameVar is a reference to an iframe, of course:
function elemSupportsEvent(elem,e)
{
var f = document.createElement(elem);
if (e in f)
{
console.log(elem + ' supports the '+ e + ' event');
return true;
}
console.log(elem + ' doesn\'t support the '+ e + ' event');
return false;
}
elemSupportsEvent('iframe','onload');//logs "iframe supports the onload event" in chrome and IE8
Just a quick fiddle by means of example of how you can use a function to check event support on various elements.
In response to your comment: if you want to check for dynamic content -as in ajax replies- you could simply use the readystatechange event:
xhr.onreadystatechange = function()
{
if (this.readyState === 4 && this.status === 200)
{
var parent = document.getElementById('targetContainerId');//suppose you're injecting the html here:
parent.innerHTML += this.responseText;//append HTML
onloadCallback.apply(parent,[this]);//call handler, with parent element as context, pass xhr object as argument
}
};
function onloadCallback(xhr)
{
//this === parent, so this.id === 'targetContainerId'
//xhr.responseText === appended HTML, xhr.status === 200 etc...
alert('additional content was loaded in the '+ this.tagName.toLowerCase+'#'+this.id);
//alerts "additional content was loaded in the div/iframe/td/whatever#targetContainerID"
}
if you want to check support for particular event, you can try something like this :
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;
})();
here is a live demo for the above :
http://kangax.github.com/iseventsupported/
Use navigator.userAgent. It contains browser user agent
if (navigator.userAgent.search("MSIE 6") == -1){
// We've got IE.
}
I want to listen for events on <p> elements at window or document level as there are too many such elements to attach an onclick event hander for each.
This is what I've got:
window.onload=function()
{
window.addEventListener('click',onClick,false);
}
function onClick(event)
{
alert(event.target.nodeName.toString());
}
I need advice on the code above, is it good?
And also, how can I check if the clicked element is a <p> element other than checking nodeName?
For example, if the <p> element contains a <b> element and that is clicked, the nodeType will be b not p.
Thank you.
I think it is good, and you can perform checking this way
var e = event.target
while (e.tagName != 'P')
if (!(e = e.parentNode))
return
alert(e)
If I was you I'd register for the "load" event in the same way that you are for "click". It's a more modern way of event handling, but might not be supported by some older browsers.
Checking the nodeName is the best way to interrogate the node:
function onClick(event) {
var el = event.target;
while (el && "P" != el.nodeName) {
el = el.parentNode;
}
if (el) {
console.log("found a P!");
}
}
Consider this:
(function () {
// returns true if the given node or any of its ancestor nodes
// is of the given type
function isAncestor( node, type ) {
while ( node ) {
if ( node.nodeName.toLowerCase() === type ) {
return true;
}
node = node.parentNode;
}
return false;
}
// page initialization; runs on page load
function init() {
// global click handler
window.addEventListener( 'click', function (e) {
if ( isAncestor( e.target, 'p' ) ) {
// a P element was clicked; do your thing
}
}, false );
}
window.onload = init;
})();
Live demo: http://jsfiddle.net/xWybT/
window.event.srcElement.options(window.event.srcElement.selectedIndex).value works in Internet Explorer (and Chrome) but not in FireFox. How to make this work in FireFox as well?
event.target.options[event.target.selectedIndex].value. Though as always with events you'd have to have passed the event object into a function, so eg.:
<script>
function selectChanged(event) {
var target= event.target || event.srcElement;
doSomethingWith(target.options[target.selectedIndex].value);
};
</script>
<select onchange="selectChanged(event)">...</select>
Setting the handler directly and using this may be easier:
<select id="x">...</select>
<script>
document.getElementById('x').onchange= function() {
doSomethingWith(this.options[this.selectedIndex].value);
};
</script>
Note that looking at options[selectedIndex] is for compatibility with older browsers. These days you can usually just get away with saying select.value.
There is no global event object in Firefox. Events are passed to their handlers as an argument. Also, instead of srcElement, you look for target.
If you use a javascript library like jQuery, all the browser specific quirks are handled for you.
Otherwise, I suggest you to read these articles
http://www.quirksmode.org/js/introevents.html
http://www.quirksmode.org/js/events_properties.html
var addEvent = (function() {
function addEventIE(el, ev, fn) {
return el.attachEvent('on' + ev, function(e) {
return fn.call(el, e);
});
}
function addEventW3C(el, ev, fn) {
return el.addEventListener(ev, fn, false);
}
return window.addEventListener ? addEventW3C:addEventIE;
})();
var domRef = document.getElementById('foo');
addEvent( domRef, 'change', function(e) {
e = e || window.event;
var el = e.target ? e.target : e.srcElement,
value = el.value;
alert( value )
});
in IE, event is a property of window, in modern DOM supporting browsers it's passed as the first argument.
IE uses srcElement where most other browsers (including Firefox) use target.
Also, Firefox passes around event objects, whereas IE just populates the global event object w/the current event's data.
You'll have to handle both in your code. How you handle the 2nd one will depend on how you're assigning the handler.
But here's one way.
function changeHanlder( event )
{
var elem = event.target || event.srcElement;
alert( elem.options[elem.selectedIndex].value );
}
It's also worth noting that all the modern javascirpt libraries handle this abstraction for you.
There are two approaches:
Assume there is markup
<SELECT name="ddlQuery" id="ddlQuery" style="width:273px;"
onchange="GetDropDownValue(event)">
...
on HTML.
One using js function:
function GetDropDownValue(e)
{
var rtnVal = "";
var sel = document.getElementById(getTargetID(e));
for (var i = 0; i < sel.options.length; ++i) {
if (sel.options[i].selected == true) {
rtnVal = sel.options[i].value;
break;
}
}
alert(rtnVal);
return rtnVal;
}
function getTargetID(e) {
if (!e) { var e = window.event; }
var objTarget = e.srcElement ? e.srcElement : e.target;
return objTarget.id;
}
another using jQuery:
$('#ddlQuery').val()
Firefox uses e.htmlEvent.target.nodeName
you can use try/catch to handle both browsers.