I don't usually do this and do not support this approach myself. However, my current use case concerns work with MS Sharepoint, and I need to discover if a user is in any version of MSIE or not.
There does seem to be some native Sharepoint functionality that only works in MSIE but I am unable to find how it works - and of course browser sniffing is not a good approach either. I came across this snippet (reference to author at bottom) which looks like an ok test to use:
if(window.ActiveXObject || "ActiveXObject" in window){
// Always true if browser is Internet Explorer
}
Can anyone offer comment on the long term validity of this test. I also thought about testing if the CSS3 selector prefix '-ms-' is supported, but this will of course only work on more modern versions of IE.
Any comments/advice/suggestions much appreciated.
Reference to source of
proposed solution author.
I suggest looking at http://browserhacks.com/
they have a list of several methods to test for IE11 and below.
Ones I personally use are,
// IE <= 10
var ieVersion = (function() { if (new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})").exec(navigator.userAgent) != null) { return parseFloat( RegExp.$1 ); } else { return false; } })();
// IE 11
var isIE = '-ms-scroll-limit' in document.documentElement.style && '-ms-ime-align' in document.documentElement.style;
ieVersion returns a number if true, false if not true. isIE evaluates to true or false
If you decide to go with a solution involving the user agent string, it's a safe bet that every IE version from 8 through 11 will include the Trident token:
/Trident/.test(navigator.userAgent)
This is the approach I'd use if I couldn't figure out what feature actually needed to be tested for.
I'm running document.querySelectorAll() frequently, and would like a short alias for it.
var queryAll = document.querySelectorAll
queryAll('body')
TypeError: Illegal invocation
Doesn't work. Whereas:
document.querySelectorAll('body')
Still does. How can I make the alias work?
This seems to work:
const queryAll = document.querySelectorAll.bind(document);
bind returns a new function which works identically to the querySelectorAll function, where the value of this inside the querySelectorAll method is bound to the document object.
The bind function is only supported in IE9+ (and all the other browsers) - https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind
Update: In fact you could create shortcuts to a whole range of document methods like this:
const query = document.querySelector.bind(document);
const queryAll = document.querySelectorAll.bind(document);
const fromId = document.getElementById.bind(document);
const fromClass = document.getElementsByClassName.bind(document);
const fromTag = document.getElementsByTagName.bind(document);
A common answer is to use $ and $$ for querySelector and querySelectorAll. This alias mimics jQuery's one.
Example:
const $ = document.querySelector.bind(document)
const $$ = document.querySelectorAll.bind(document)
$('div').style.color = 'blue'
$$('div').forEach(div => div.style.background = 'orange')
div {
margin: 2px;
}
<div>
test
</div>
<section>
<div>
hello
</div>
<div>
foo
</div>
</section>
The JavaScript interpreter throws an error because querySelectorAll() should be invoked in document context.
The same error is thrown when you are trying to call console.log() aliased.
So you need to wrap it like this:
function x(selector) {
return document.querySelectorAll(selector);
}
My solution covers the four following use cases:
document.querySelector(...)
document.querySelectorAll(...)
element.querySelector(...)
element.querySelectorAll(...)
The code:
let doc=document,
qsa=(s,o=doc)=>o.querySelectorAll(s),
qs=(s,o=doc)=>o.querySelector(s);
In terms of parameters, the selector s is required, but the container element object o is optional.
Usage:
qs("div"): Queries the whole document for the first div, returns that element
qsa("div"): Queries the whole document for all divs, returns a nodeList of all those elements
qs("div", myContainer): Queries just within the myContainer element for the first div, returns that element
qsa("div", myContainer): Queries just within the myContainer element for all divs, returns a nodeList of all those elements
To make the code slightly shorter (but not quite as efficient), the qs code could be written as follows:
let qs=(s,o=doc)=>qsa(s,o)[0];
The code above uses ES6 features (let, arrow functions and default parameter values). An ES5 equivalent is:
var doc=document,
qsa=function(s,o){return(o||doc).querySelectorAll(s);},
qs=function(s,o){return(o||doc).querySelector(s);};
or the equivalent shorter but less efficient ES5 version of qs:
var qs=function(s,o){return qsa(s,o)[0];};
Below is a working demo. To ensure it works on all browsers, it uses the ES5 version, but if you're going to use this idea, remember that the ES6 version is shorter:
var doc = document;
var qs=function(s,o){return(o||doc).querySelector(s);},
qsa=function(s,o){return(o||doc).querySelectorAll(s);}
var show=function(s){doc.body.appendChild(doc.createElement("p")).innerHTML=s;}
// ____demo____ _____long equivalent______ __check return___ _expect__
// | | | | | | | |
let one = qs("div"); /* doc.querySelector ("#one") */ show(one .id ); // "one"
let oneN = qs("div",one); /* one.querySelector ("div") */ show(oneN .id ); // "oneNested"
let many = qsa("div"); /* doc.querySelectorAll("div") */ show(many .length); // 3
let manyN = qsa("div",one); /* one.querySelectorAll("div") */ show(manyN.length); // 2
<h3>Expecting "one", "oneNested", 3 and 2...</h3>
<div id="one">
<div id="oneNested"></div>
<div></div>
</div>
This would work, you need to invoke the alias using call() or apply() with the appropriate context.
func.call(context, arg1, arg2, ...)
func.apply(context, [args])
var x = document.querySelectorAll;
x.call(document, 'body');
x.apply(document, ['body']);
I took #David Muller's approach and one-lined it using a lambda
let $ = (selector) => document.querySelector(selector);
let $all = (selector) => document.querySelectorAll(selector);
Example:
$('body');
// <body>...</body>
function x(expr)
{
return document.querySelectorAll(expr);
}
If you don't care about supporting ancient, awful browsers that nobody should be using anymore, then you can just do this:
const $ = (sel, parent = document) => parent.querySelector(sel);
const $$ = (sel, parent = document) => Array.from(parent.querySelectorAll(sel));
Here's some examples of usage:
// find specific element by id
console.log($("#someid"));
// find every element by class, within other element
// NOTE: This is a contrived example to demonstrate the parent search feature.
// If you don't already have the parent in a JavaScript variable, you should
// query via $$("#someparent .someclass") for better performance instead.
console.log($$(".someclass", $("#someparent")));
// find every h1 element
console.log($$("h1"));
// find every h1 element, within other element
console.log($$("h1", $("#exampleparent")));
// alternative way of finding every h1 element within other element
console.log($$("#exampleparent h1"));
// example of finding an element and then checking if it contains another element
console.log($("#exampleparent").contains($("#otherelement")));
// example of finding a bunch of elements and then further filtering them by criteria
// that aren't supported by pure CSS, such as their text content
// NOTE: There WAS a ":contains(text)" selector in CSS3 but it was deprecated from the
// spec because it violated the separation between stylesheets and text content, and you
// can't rely on that CSS selector, which is why you should never use it and should
// instead re-implement it yourself like we do in this example.
// ALSO NOTE: This is just a demonstration of .filter(). I don't recommend using
// "textContent" as a filter. If you need to find specific elements, use their way
// more reliable id/class to find them instead of some arbitrary text content search.
console.log($$("#exampleparent h1").filter(el => el.textContent === "Hello World!"));
The functions I provided use a ton of modern JS features:
const to make sure the function variable can't be overwritten.
functions defined as an arrow function aka lambda: (args) => code with implied return statement
default parameters (not supported by browsers before the year 2016)
no {} or return, since those can be skipped if there's just 1 statement in the function body.
The modern function Array.from() is used, which converts the querySelectorAll result (which is always a NodeList, or empty NodeList), into an Array, which is basically what every developer wants. Because that's how you get access to .filter() and other Array functions that allow you to process the discovered nodes further, using clean, short code. And Array.from() creates a shallow copy of all elements which means that it's blazingly fast (it just copies the memory references/pointers to each Node DOM element from the original NodeList). It's a major API enhancer.
If you care about ancient browsers, you can still use my functions but use Babel to convert the modern JS to old ES5 when you release your website.
My suggestion would be to write your entire site in ES6 or higher and then use Babel if you care about visitors from Windows XP and other dead operating systems, or just random people who haven't updated their browsers in 5+ years.
But I wouldn't recommend using Babel. Stop worrying about people who have old browsers. It is their problem, not yours.
The modern "app" world is incredibly deeply based on web browsers and JavaScript and modern CSS, and most of your visitors these days have modern, auto-updated browsers. You basically can't live modern life without a modern browser, since so many websites demand it now.
In my opinion, the days of expecting web designers to waste their time and sanity trying to make a site work on browsers from 1993 are over. It's time the laziest customers/visitors update their old browsers instead. It's not difficult. Even old and dead operating systems usually have ways to install new versions of browsers on them. And people who don't have an updated browser are only a tiny fraction of a percent these days.
For example, the Bootstrap framework, the world's most popular framework for mobile/responsive sites, only cares about supporting the 2 most recent major versions of all major browsers (at least 0.5% market share). Here's their list as of this moment:
0.5% market share or higher
last 2 major versions only
not a dead browser (not discontinued)
Chrome >= 60
Firefox >= 60
Firefox ESR
iOS >= 12
Safari >= 12
not Explorer <= 11 (meaning no versions of Internet Explorer at all)
And I completely agree with this. I was a web developer in the early 2000s and it was absolute hell. Being expected to make it work on some random, stupid user's ancient browser was hell. It took all the fun out of web development. It made me hate and quit web development. Because 90% of my time was wasted on browser compatibility. That's not how life should be. It's not your fault that some customers/visitors are lazy. And these days, visitors have no more excuses to stay lazy.
Instead, you should only target users who have modern browsers. Which is basically everybody these days. There is no excuse for anyone to use an old browser. And if they use an old browser, your site should show a big, fat banner saying "Please, join the modern world for your own sake. Download a new browser. How are you even able to live your normal life with such an old browser? Are you a time traveler from caveman times?".
People have no excuses to have old browsers anymore:
Linux: Ships with the latest versions of Firefox by default.
Mac: Ships with Safari, which is a modern browser. But some older macOS versions won't get newer versions of Safari, and older machines often can't install the latest macOS version either. Well, tough luck for those visitors. They are gonna have trouble on more than just your website. It's up to them to install a modern browser (such as Chrome), which they are able to do even on old versions of macOS. So they have no excuses. Don't waste your life catering to people on very old, buggy Safari versions.
Windows: Windows 10 ships with Edge, which is based on Chromium and is as compatible with websites as Chrome is. People have no excuse to have an old browser. And most Windows users use Chrome. As for very old, discontinued versions of Windows (XP, Vista, 7, 8), well, we yet again arrive at the same question as before: Do you care about 0.0000001% stupid visitors who use a dead OS and an old Internet Explorer version? The whole freaking web will be broken for them anyway, so who cares if your site is broken for them too? They should stop being lazy and just upgrade their OS to Windows 10, or at least install Chrome or Firefox on their current OS. They have no excuses.
iOS: If you're stuck on a super old iOS device, then you can't use the modern web. Tough luck. A lot of the web is gonna be broken for you. Get a new device. Even frameworks like Bootstrap, the world's #1 mobile web framework, doesn't support iOS 11 or earlier. It's not our problem. It's the cheapskate visitor's problem if they still hang onto such an old device. They can literally get a newer iOS device second-hand for almost no money at all and fix all of their problems with visiting the modern web. And they'll need to buy that anyway since most apps (even banking/important apps) require modern iOS versions.
Android: The browser is independently updated from the OS, and can even be sideloaded, so even if you're stuck on old Android versions, you have access to modern browsers. So you have no excuses.
Most people these days have browsers that are completely up-to-date and auto-updated. That's the fact.
So yeah... the days of website designers suffering through hell just for catering to old browsers are over. Therefore I suggest that people use ES6 and CSS3 for their websites, to make web designing a joy for the first time.
Hope you enjoy the ES6 functions I provided!
Here is my take on it. If the selector has multiple matches, return like querySelectorAll. If ony one match is found return like querySelector.
function $(selector) {
let all = document.querySelectorAll(selector);
if(all.length == 1) return all[0];
return all;
}
let one = $('#my-single-element');
let many = $('#multiple-elements li');
2019 update
Today I made a new take on the problem. In this version you can also use a base like this:
let base = document.querySelectorAll('ul');
$$('li'); // All li
$$('li', base); // All li within ul
Functions
function $(selector, base = null) {
base = (base === null) ? document : base;
return base.querySelector(selector);
}
function $$(selector, base = null) {
base = (base === null) ? document : base;
return base.querySelectorAll(selector);
}
The highly invasive version:
<script>
for(const c of [HTMLDocument, Element, DocumentFragment]) {
c.prototype.$ = c.prototype.querySelector;
c.prototype.$$ = c.prototype.querySelectorAll;
}
const $ = document.$.bind(document); // For inline events
const $$ = document.$$.bind(document);
window.$ = $; // For JS files
window.$$ = $$;
</script>
So you can chain $('nav').$$('a') like jQuery allows (I think). Since ShadowRoot extends DocumentFragment you can even inspect (open) Shadow DOMs:
const styles = $('x-button').shadowRoot.$$('style');
function $(selector, base = null) {
base = (base === null) ? document : base;
return base.querySelector(selector);
}
function $$(selector, base = null) {
base = (base === null) ? document : base;
return base.querySelectorAll(selector);
}
Why not simplier ??? :
let $ = (selector, base = document) => {
let elements = base.querySelectorAll(selector);
return (elements.length == 1) ? elements[0] : elements;
}
you dont need to add any dot before class name you can modify this also by using getEle by class or id tag etc
function $(selector){
var dot = ".";
var newSelector = dot.concat(selector);
var Check = document.querySelectorAll(newSelector);
if(Check && Check.length>0 && Check.length < 2){
return document.querySelector(newSelector);
}
else {
return document.querySelectorAll(newSelector);
}
}
$("answercell")[0].remove();
I'd like to know if it's possible to write conditional javascript within a javascript file for Internet Explorer.
i.e. something like this...
if (is IE7) {
do this } else {
do this instead
});
I know I can load a completely different script for IE using conditional comments in the head, but I only want to change a small piece of code and so loading a completely different sheet would be an 'expensive' way to do that.
When writing Javascript, doing feature detection is always the way to go instead of browser detection. So instead of doing if (IE7) do if (feature).
For example, if you want to know if your browser supports getElementsByClassName(), instead of checking the browser version, you check for the existence of the function ( if (document.getElementsByClassName) ).
Please read this great article:
Object detection on Quirksmode
If you want to know whether the
browser that views your page supports
certain objects you want to use in
your code, you should never EVER use a
browser detect. Sure, you know that
this–and–that browser will support
your code while such–and–so browser
won’t. But how about other browsers,
obscure browsers?
Not within the JavaScript file directly.
A few alternatives would be:
Using a global variable before the script is loaded to check in your JavaScript file. This is a bit of a hybrid approach and could get messy, but it's guaranteed IE detection.
<!--[if IE]>
<script type="text/javascript">
var is_ie = true;
</script>
<![endif]-->
<script type="text/javascript" src="somefile.js"></script>
Or, a more traditional approach using browser or object detection within the JavaScript file.
Conditional compilation is exactly what you are looking for.
<script>
/*#cc_on
#if (#_jscript_version == 5.7 && window.XMLHttpRequest)
document.write("You are using IE7");
#end
#*/
</script>
My go-to script for this is PPK's BrowserDetect script. It's lightweight, easily understandable, and doesn't require you to use a library. When it's loaded, you can write code like:
if (BrowserDetect.browser == "Explorer" && BrowserDetect.version >= 6 && BrowserDetect.version <= 8) {
// IE6-8 code
{
Of course, you should avoid using this at all (reasonable) costs, but there's times where it's cleaner to quarantine IE-specific code away rather than try to hack around IE-specific functions and bugs.
Despite the fact that this is an answer to the original question, this is NOT what you should do. So don't do it!
Why not work out which browser you are using and store that in a variable in javascript. Then you can have if statemenets and the like in your javascript. e.g. If I am IE then do this, otherwise do that. You get the idea!
Have you seen this? Browser sniffing
The salient bit:
var is = {
ff: window.globalStorage,
ie: document.all && !window.opera,
ie6: !window.XMLHttpRequest,
ie7: document.all && window.XMLHttpRequest && !XDomainRequest && !window.opera,
ie8: document.documentMode==8,
opera: Boolean(window.opera),
chrome: Boolean(window.chrome),
safari: window.getComputedStyle && !window.globalStorage && !window.opera
}
If you are using jquery you code do this
if ($.browser.msie && $.browser.version == '6.0') {
//do IE specific code
}
If you want to use jquery, it has a built in browser detect.
http://api.jquery.com/jQuery.browser/
I'm currently working on a site that detects the browser version by checking for various JS objects:
var is = {
ff: window.globalStorage,
ie: document.all && !window.opera,
ie6: !window.XMLHttpRequest,
ie7: document.all && window.XMLHttpRequest && !XDomainRequest && !window.opera,
ie8: document.documentMode==8,
opera: Boolean(window.opera),
chrome: Boolean(window.chrome),
safari: window.getComputedStyle && !window.globalStorage && !window.opera
}
However this doesn't work in FireFox 4. Does anyone know what objects to check for, when you want to detect FF4?
Just check for something that was introduced with Gecko 2.0, such as selection.modify:
window.globalStorage && window.getSelection().modify
and window.globalStorage won't return all versions of FF, as it requires Gecko 1.8.1 / FF2
That's a lot of overhead calling so many global objects and it looks very, very complex. Why not use something like jQuery to check browser versions or even just the simple Navigator object?
Navigator Object
You can use modernizer, it's a javascript library designed to handle browser version and trigger fallback
http://www.modernizr.com/
I would like to use feature detection to tell whether the user's version of Firefox supports the CSS style value -moz-linear-gradient. (This was added in Gecko 1.9.2. Version 3.6 of Firefox uses this.)
I can't use document.body.style.mozLinearGradient (or something similar) because -moz-linear-gradient is not a style property but a style value.
Does anyone know how to test for this without using version numbers?
I'm not sure how, but Modernizr (a nice little feature-detection script) appears to do it.
I guess you could create an (offscreen?) element, set that as it's style, and then poke around in the DOM to see if the browser successfully applied it?
Just assign it as style value and check afterwards if it is there.
Kickoff example:
function supportsMozLinearGradient() {
var element = document.getElementsByTagName('script')[0]; // Just grab an "invisible" element.
var oldstyle = element.style.background; // Backup old style.
try {
element.style.background = '-moz-linear-gradient(top, black, white)';
} catch(e) {
// Ignore failures.
}
var supports = element.style.background.indexOf('-moz-linear-gradient') > -1; // Did it accept?
element.style.background = oldstyle; // Restore old style.
return supports;
}
You should check for -moz-background-size (which was introduced in Firefox v3.6). The inference won't be picked up by other browsers since the property is prefixed.
if ('MozBackgroundSize' in document.body.style)
This is how MooTools detects Gecko (Firefox) engine (I'm "paraphrasing" slightly)
gecko = (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18)
So if it's FF it'll return 19 or 18, I believe 19 is 3.x and 18 is 2.x
And apparently FF3.6 stopped supporting document.getBoxObjectFor, so to detect 3.6 I basically do
isFF36 = gecko && !document.getBoxObjectFor
Works like a charm from a few tests I did.
If you're not using MooTools you can probably combine the two into one statement that would return something like false or 'ff' or 'f36' but I'm too lazy to work through that logic :)