JS: How to make document.getElementById cross-browser? - javascript

document.getElementById doesn't seem to work across all browsers (I mean some old ones) and I am sure there are developers who are not aware of this.
What solutions would you suggest to make it cross-browser?
Thanks

If document.getElementById doesn't work then either:
You're doing it wrong (invalid HTML, trying to access names instead of IDs, etc)
or
You're working with Netscape 4.x and Internet Explorer 4.x
There are three ways to deal with browsers of this era.
Tell people to upgrade. They are unmaintained, security hole ridden nightmares for user and author alike.
Build on stuff that works and make sure your JS checks for the existence of getElementById and friends before trying to use them ( if (!document.getElementById) { return false; /* Insufficient DOM to bother with JS here */ } )
Learn about document.all and document.layers

Are you sure its not this kind of problem? Have a look its interesting, I didn't know that before.
However, to complement what is already suggested by David Dorward, you write a function like below.
function getElement (id) {
if (document.getElementById) {
return document.getElementById(id);
}
else if (document.all) {
return window.document.all[id];
}
else if (document.layers) {
return window.document.layers[id];
}
}

getElemID(obj){
if(document.getElementByID){
return document.getElementByID(obj);
}
else if (document.all){
return document.all[obj];
}
else if (document.layers){
return document.layers[obj];
}
else {
alert("Could not find support");
return false;
}
}

function getDOM() {
if (document.getElementById) {
return document.getElementById; 
}
var window_document = window.document || {};
var elements = window_document.all || window_document.layers;
if(elements) {
return function(x) { return elements[x]; }
}
// everything failed
throw new InternalError('No means to "getElementById"');
}
... then
var getElementById;
try {
getElementById = getDOM();
} catch(err) {
alert(err);
}
// implicit 0K
var oHTMLElement = getElementById('#main');

Related

Elegant way to return from try...catch - Javascript

Elegant way to return null. Don't want to return it twice.
Option 1:
function readSessionStorage(key) {
try {
if (typeof window !== 'undefined') {
return JSON.parse(window.sessionStorage.getItem(key));
}
return null;
} catch {
return null;
}
}
Option 2:
function readSessionStorage(key) {
try {
return JSON.parse(window.sessionStorage.getItem(key));
} catch {
return null;
}
}
Option 3:
If we pick this option, why should we do this?
function readSessionStorage(key) {
try {
if (typeof window !== 'undefined') {
return JSON.parse(window.sessionStorage.getItem(key));
}
} catch {}
return null;
}
Why do I need to do this?
I'm getting DOMException if I try to get window.sessionStorage, and hence I need to use try...catch.
function readSessionStorage(key) {
if (typeof window !== 'undefined' || !window.sessionStorage) {
return JSON.parse(window.sessionStorage.getItem(key));
}
return null;
}
Original Code:
function readSessionStorage(key) {
if (typeof window !== 'undefined') {
return JSON.parse(window.sessionStorage.getItem(key));
}
return null;
}
Well, that's odd. No response in seven months? Well, I was looking for something like this myself, and couldn't find an eloquent solution. I created my own.
Edit: upon further work, I found that the null safety operators do exist in JS! Yay! The function below is still quite helpful, perhaps more so now since one can use the safety operator to get to the precise value desired, then handle it correctly.
Consider using a function similar to this:
const defineCheck = (item, defaultVal = '') => {
try {
return (item !== null && item !== undefined) ? item : defaultVal;
} catch {
return defaultVal;
}
}
This might not exactly fit your needs, but you can make your function however you want. This will keep the clutter out of your important business or UI logic, so you and others can focus on what is important.
Sample usage (if account is known to have an expected value):
tempForm.ExpirationDate = defineCheck(account.expirationDate);
tempForm.LicenseType = defineCheck(account.licenseType, 'Nada');
With null safety:
tempForm.ExpirationDate = defineCheck(account?.license?.expireDate);
tempForm.LicenseType = defineCheck(account?.license?.licenseType, 'Nada');
I hope you and others find this useful, or better yet, share a much better way to deal with this problem. I'd hoped the C# null safety check operator made its way to JS, but no luck (e.g., "account?.license?.licenseType ?? 'Nada'")

Vanilla Javascript Error IE only

I won't bother you with too much "blahblahblah" as most of you will know the following script all too well. A lot of questions popped up around this topic after Google let this beast out to play in the wild.
var elements = [
"script1.js",
"script2.js"
];
var downloadJSAtOnload = function(elements) {
if (toString.call(elements) !== "[object Array]") {
return false
}
var i, element;
for (i = 0; i < elements.length; i++) {
element = document.createElement("script");
element.src = elements[i];
document.body.appendChild(element)
}
return true
};
if (window.addEventListener) {
window.addEventListener("load", function() {
downloadJSAtOnload(elements)
}
, false)
} else {
if (window.attachEvent) {
window.attachEvent("onload", function() {
downloadJSAtOnload(elements)
})
} else {
window.onload = function() {
downloadJSAtOnload(elements)
}
}
};
This script is doing what it has to do in all browsers but IE10 and 11 from what I can see. I tested with browserstack and real machines with the same result.
May be it is due to minification of the script so I will also give you the minified version of the above script, as we are using it in our live environment:
var elements=["script1.js","script2.js"],downloadJSAtOnload=function(n){if("[object Array]"!==toString.call(n))return!1;var t,e;for(t=0;t<n.length;t++)e=document.createElement("script"),e.src=n[t],document.body.appendChild(e);return!0};window.addEventListener?window.addEventListener("load",function(){downloadJSAtOnload(elements)},!1):window.attachEvent?window.attachEvent("onload",function(){downloadJSAtOnload(elements)}):window.onload=function(){downloadJSAtOnload(elements)};
Anything I was doing wrong here? As it is indeed doing its thing in FF, Chrome, Safari and Opera I was expecting IE (you can call me a fool, now) to behave... at least once.
Seems the Error is on "[object Array]"!==toString.call(n).
The IE can't referer to the function toString() directly when using .call( something ). You can use following code to fix your Problem:
not minifed
if( Object.prototype.toString.call(elements) !== "[object Array]") {
return false;
}
minified
if("[object Array]"!==Object.prototype.toString.call(n)) return !1;
See more Informations about the toString()-Function here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString under "Using toString() to detect object class".

Create a polyfill for HTML5 Validation

I'm building a very simple Validation plugin that uses part of the Constraint validation API. I'm using 2 features of the API, valueMissing and patternMismatch like so:
var el = $(this)[0];
if ( el.validity.valueMissing || el.validity.patternMismatch ) {
$errMsg.show();
} else {
$errMsg.hide();
}
I'd like to write my own polyfill so these features work in older browsers as opposed to using an entire HTML5 validation library or plugin.
So I guess what I'm trying to achieve is something like this:
if (!typeof document.createElement( 'input' ).checkValidity == 'function'){
validity.valueMissing = function(){
// check for value
}
}
However, I can't figure out how to go about this, nor how the element is used as the first part of the function:
el.validity.valueMissing
Thanks in advance for any help or suggestions!
There would be a bit of work involved for this poly, but what you need to do, is alter HTMLInputElement's prototype, so each new instance has those methods and propertyes (validity, checkValidity and so on).
Something along these lines:
HTMLInputElement.prototype.testMethod = function (e) { console.log(e) };
HTMLInputElement.prototype.testProp = 'foo';
var input = document.createElement('input');
input.testMethod(input.testProp);
So I finally figured this out with some help from Poelinca's answer...
if ( typeof document.createElement( 'input' ).checkValidity !== 'function' ) {
Object.defineProperty( Element.prototype, 'validity', {
get: function() {
return this;
}
});
Object.defineProperty( Element.prototype.validity, 'valueMissing', {
get: function() {
return this.value !== "";
}
});
Object.defineProperty( Element.prototype.validity, 'patternMismatch', {
get: function() {
var pattern = this.getAttribute('pattern'),
regEx = pattern !== null ? eval( "{/" + pattern + "/}" ) : "";
value = this.value;
if ( regEx !== "" ) {
return regEx.test( value );
} else {
return true;
}
return false;
}
});
}
I used Object.defineProperty so I could call the function without using the parenthesis, which is how the native Constraint validation API works:
if ( el.validity.valueMissing || el.validity.patternMismatch ) { //...
From my research I learned that extending the DOM like this isn't really recommended as you can run into a lot of issues. For details read this article. The comments are particularly helpful as this article is quite dated.
In my case what I'm trying to do is quite simple and I only need to support IE8+. Maybe a little overkill but I like it.
Please feel free to improve / critique my answer!
Thanks!

NicEdit Error in Chrome

I'm using the NicEdit WYSIWYG plugin on my site.
It's come to my attention that when NicEdit is instantiated in Chrome, the following Javascript error is generated:
Uncaught TypeError: Object has no method 'createRange'
This doesn't stop the plugin from working, but I would like to prevent this if possible. Here is the offending method:
getRng : function() {
var s = this.getSel();
if(!s) { return null; }
return (s.rangeCount > 0) ? s.getRangeAt(0) : s.createRange();
}
NicEdit seems to be pretty much dead as a project, which is why I am asking this question here instead of over at the NicEdit forums. I am hoping that someone knows of a 'quickfix' to this problem. In all other respects NicEdit works well for me, so I am reluctant to change over to a different WYISWYG plugin just yet...
Thanks (in advance) for your help.
The problem is that the implementation of the selection object for Webkit does not define a createRange( ) method. That method seems to be specific to Internet Explorer. For Webkit and Gecko DOM implementations, the createRange( ) method is defined on the document object. With this knowledge, the fix for getRng( ) becomes:
getRng : function() {
var s = this.getSel();
var rng;
if(!s) { return null; }
if (s.rangeCount > 0) {
rng = s.getRangeAt(0);
} else if ( typeof s.createRange === 'undefined' ) {
rng = document.createRange();
} else {
rng = s.createRange();
}
return rng;
},
I encountered this as I was evaluating a number of rich text editors for an upcoming project and had to create a sample page with nicEdit.
The version at https://github.com/danishkhan/NicEdit contains this and other bugfixes.
This particular fix: https://github.com/danishkhan/NicEdit/commit/efa6a1e8867b745b841157e919a0055cb626d2c4
Same code, written in nicEdit current design:
getRng : function() {
var s = this.getSel();
if(!s) { return null; }
return (s.rangeCount > 0) ? s.getRangeAt(0) : (typeof s.createRange == 'undefined') ? document.createRange() : s.createRange();
},

javascript function overloading

Can I do the following?
function contains(element) {
// if the element is a Vertex object, do this
if (element instanceof Vertex) {
var vertex = element;
for ( var index in self.verticies) {
if (self.verticies[index].id == vertex.id) {
return true;
}
}
return false;
}
// else if the element is an Edge object, do this
else if (element instanceof Edge) {
var edge = element;
for ( var index in self.verticies) {
if (self.verticies[index].id == edge.id) {
return true;
}
}
return false;
} else {
// shouldn't come here
return false;
}
};
Basically... I want to be able to call contains() and pass it either a Vertex object or an Edge object but I don't want to have duplicate code. Is this the right way to do it? Furthermore, am I handling the assignment var vertex = element / var edge = element correctly? I want to assign element to another Vertex/Edge object and use that for my look up.
Let me know if I need to clarify.
Thanks,
Hristo
Your code should work fine.
Note, however, that there is no point (other than clarity, which is a good thing) in writing var edge = element.
Javascript variables are untyped; there is no difference between edge and element.
Also, you should probably throw an exception instead of
// shouldn't come here
return false;
Finally, why are you searching self.verticies for an Edge?
Note, by the way, that you still have duplicate code.
You can rewrite your function like this:
function contains(element) {
var searchSet;
// if the element is a Vertex object, do this
if (element instanceof Vertex)
searchSet = self.verticies;
else if (element instanceof Edge)
searchSet = self.edges;
else
throw Error("Unexpected argument");
for (var i = 0; i < searchSet.length; i++) {
if (searchSet[i].id == element.id)
return true;
}
return false;
}
Here's an approach that has a couple of advantages:
Smaller functions (no big if/else if chain)
Produces an appropriate error for missing functions without any additional coding
See what you think:
function contains(element) {
window['contains_' + typeof element](element);
};
contains_string = function(element) {
alert('string: ' + element);
};
contains('hi!'); // produces alert
contains(3); // error: 'undefined is not a function'
​It has some downsides too.
The error message isn't terribly informative (not much worse than default behavior though)
You 'pollute' the 'window' object here a little (it'd work better as part of an object)
etc

Categories