JavaScript DOM on IE - javascript

I want to make a JS function that switch visible/hidden.
var foo = function(n){
var hidden_elements = document.getElementsByName('hidden');
for(var i=0;i<hidden_elements.length;i++){
hidden_elements[i].style.visibility = 'hidden';
}
hidden_elements[n].style.visibility = 'visible';
};
It works on Firefox and Chrome, but it doesn't on IE. Why?
Thanks in advance.

I would recommend saving yourself the horror and going with:
http://jquery.com/
http://mootools.net/docs/core
http://dojotoolkit.org/
etc....
The libraries do a lot to smooth over the surprises of different browsers. If you are being super minimalist you can always check the source for how they are handling the differences. Also have a look at quirksmode's compatibility listing.
I know I didn't give a solid answer but you are going to run into these troubles all the time and these are some good tools for hammering them out.

IE up to IE8 does not follow W3C specs. Microsoft has their own standards. Many scripting methods that work on Firefox or Chrome (which are W3C standards) may not work properly in various builds on IE.
Why don't you try something from scratch? Either that, or do some ease of access. You can do this by making a pattern for ids and dynamically building those ids (may be incremental). Then, access those tags from their id.
Access by name is not preferred. Id is most appropriate.

Your html is invalid. the "name" property needs to be unique. Use "class" instead.
Internet Explorer might give some issues, so DOM polyfills like flowjs could be used.

Related

Browser support for getElementsByTagNameNS

Which browsers/versions support getElementsByTagNameNS(), and to which extent? I can't seem to find a good reference.
[Edit] I am interested in a complete reference, but my immediate need is for namespaced xml returned from an AJAX call (which jQuery doesn't seem to handle btw).
Sitepoint says Firefox as of version 1.5, Safari as of version 3 and Opera as of version 9.
Firefox versions lower than 3.6 did a case insensitive search which as corrected in version 3.6.
Microsoft claims to support it as of IE9. However, according to Dottoro, this is only true for HTML documents. I'm not sure if you can't really trust Dottoro because selecting by namespace does not make sense for HTML documents anyway.
You should be able to use XPath if getElementsByTagNameNS is not supported. Wrappers are required, though, since IE does not support the standard API – see Yaldex and NCZOnline for hints how to get IE to cooperate. Or ask Microsoft's support.
I would recommend to ensure XHTML documents have actually been served with a XML content type when you plan to use the function on the DOM of a web page.
Chromium 14 does also support the method (and honors namespaces in contrast to old Safari versions). Support might have been in long before, I just don't know the earliest Chrome/Chromium version with support.
It seems all browsers but not IE are supporting DOM Level 3 XPath. Use XPath to replace calls to getElementsByTagNameNS if there are issues with it. See NCZOnline for an introduction and notes on browser support.
I know this is old, but this might be useful to someone. You can just use plain old getElementsByTagName in IE. Instead of calling node.getElementsByTagNameNS('someNamespace', 'someNodeName'), call node.getElementsByTagName('someNamespace:someNodeName').
Or use the following shim:
var getElementsByTagNameNS = function(node, ns, tagName) {
if (node.getElementsByTagNameNS) {
return node.getElementsByTagNameNS(ns, tagName);
}
return node.getElementsByTagName(ns + ':' + tagName);
};
And call it like this:
getElementsByTagNameNS(someNode, 'someNamespace', 'someNodeName');
Have you taken a look at this reference?
Specifically, here.

IE8 / JavaScript: Override native implementation of document.all.item?

I know this is crazy, but IE can drive one to do crazy things.
Here's the deal: we have a SharePoint 2007 site with Content Editor Web Parts. We have a contingent of users with Internet Explorer 8. We want to keep the site in IE8/IE8 Standards modes for better rendering of content. However, this configuration breaks the ability to open the Rich Text Editor window from the C.E. web part.
If we force IE8 into IE7 document mode or quirks mode, it works. Indeed, other sources online have suggested doing just that to fix the problem. But we'd really rather have everything run in standards mode.
Through some debugging, we found the source of the problem to be the use of document.all.index("<web_part_id>") JavaScript when retrieving a web part object on the page. In IE8 standards, this returns an object with most of the properties either empty, null or undefined; most notably, the id property is not set. If you were to use document.getElementById to retrieve the same ID, you get back a fully populated object. Likewise if IE8 is not in standards mode, you get back a mostly (but not completely) populated object -- but populated enough to avoid the script error, anyway.
All this code looks like it is injected dynamically into the SharePoint page, so that rules out simply replacing references to document.all. However, we got the crazy idea of redefining the document.all.item method to actually invoke document.getElementById. Our attempts so far to do this don't work, so perhaps someone might shed some light on what we're doing wrong (okay, there's lots wrong about this, but it's IE and SharePoint, right?).
Our initial attempt at an override looks like this:
<script type="text/javascript">
document.all.item = function(id) { return document.getElementById(id); }
</script>
This code is in the document HEAD, above any other script references (SharePoint or otherwise), but it does not appear to supersede the native code.
Thoughts, ideas, suggestions and criticisms welcome!
document.all is a HTMLCollection, so you may use HTMLCollection.prototype to change the behaviour:
if(document.all
&& !window.opera
&& typeof HTMLDocument=='object'
&& typeof HTMLCollection=='object')//filter IE8
{
HTMLCollection.prototype.item=
function(id)
{
return((this==document.all)
? document.getElementById(id)//document.all
: this[id]//maintain native behaviour for other collections
);
}
}
So you're saying you can't change the references to document.all.item because SP directly injects it to the html? So couldn't you use the DOM to replace it?
BTW, I feel your SharePoint pain. I've built SP workflows, and those are enough of a pain for me!

running an XPath expression SVG inside an HTML with javascript

I'm trying to run an xpath-expression over an svg which is embedded in html. I just cannot figure out how to set up the parameters. I want find elements that have an arbitary attribute from a given namespace. I use the following xpath expression:
var xpathexp = "//*[#*[namespace-uri()='"+this.typo7namespace+"']]";
I tested this expression and it worked as expected.
this is the code to find the result set:
var result = this.svgdocument.contentDocument.evaluate( xpathexp, this.svgdocument.documentElement, null, XPathResult.ANY_TYPE, null );
Could anybody tell me, or post a link to a tutorial, how to deal with the namspaces, the namespace resolvers??
Greetings...
Here's the Mozilla tutorial on using XPath:
https://developer.mozilla.org/en/Introduction_to_using_XPath_in_JavaScript
Here's one on writing custom namespace resolvers:
https://developer.mozilla.org/en/Introduction_to_using_XPath_in_JavaScript#Implementing_a_User_Defined_Namespace_Resolver
I found these interfaces to be rather clunky, though, so I wrote an abstraction layer that would take the xpath string and a context node, and would return a regular js array. It works inside the browser and embedded in Java under Mozilla Rhino:
https://svn.apache.org/repos/asf/commons/sandbox/gsoc/2010/scxml-js/trunk/src/javascript/scxml/cgf/util/xpath.js
All of the above should work in all browsers except for IE6-9.
IE6-8 does not support SVG natively, so this should be less important to your question. For completeness, though, here's a good article describing XPath support in earlier IE8, including support for resolving namespaces:
http://www.nczonline.net/blog/2009/04/04/xpath-in-javascript-part-3/
Apparently, IE9 also does not include support for XPath in the browser, which is more problematic, as it does support SVG natively. Probably the best approach here is to use ActiveX to work with MSXML APIs:
IE9 selectSingleNode missing from beta, how to overcome this in JavaScript?

alternative when an older browser does not accept jquery

I have just been altered to the fact that a user of my website is using a very old browser which does not run jquery (in this case Safari 1.x) and as a result can not access the login panel which uses jquery's slideToggle function.
Can anyone think of a fix which detects whether a browser is able to use jquery - and if not make that link go to a different page rather than showing the login panel?
You could a little conditional check like
if(!'jQuery' in window) {
// jQuery is not available
}
or, if Safari 1.x doesn't know about the IN operator (I'm not sure) use
if(!window.jQuery) {
}
I think there are alternative answers to this, but for me, I would have to weigh up the time it will take you to support his obsolete browser (I'm sure there may be other things inside the site), versus the payback to you...
In the plain HTML source code for the the href= of the login link, set that to a plain HTML login page.
Using jQuery, attach the click handler to the link, if this part fails, thats ok, the browser will just follow the href in the link to the plain login page, allowing your old-browser-user to login still.
$(document).ready(function(){
$('#login_link_id').click(function(){
// Your code here
});
});
If you use javascript/jQuery you should ALWAYS ensure your site works perfectly without it. In this case if you have a login popup box; you probably assign a click event assigned after the DOM has loaded.
What you should do is ensure that if jQuery isn't present the link loads a "normal" login webpage as opposed to the popupbox. I use something similar to this:
Log in
<script>
if(!'jQuery' in window) {
$(document).ready(function(){
//assign on click event to loginlink
});
}
</script>
If jQuery doesn't exist then login.html will be opened normally.
Wow, seriously?! Safari 1.x?? Anyhow, try this...
var isJQSupported = false;
$(function() { //shorthand for document.ready
isJQSupported = true;
//your usual code
});
if (!isJQSupported) {
window.location = "http://www.apple.com/safari/download/";
}
To me it sounds like safari 1.X has problems with jQuery internally. Which means simple checks like whether $ exists in the global space or whether $(function) does anything are not going to help.
The most likely root cause will be that javascript throws an error in loading of jQuery itself which will then stop the rest of your javascript code from execution.
There are four viable options here.
Either make the website work with noscript. Replace your login control with pure HTML and postbacks and ask the user to turn javascript off. This option is useful since you won't be fixing the issue for safari 1.x problems specifically.
You can make javascript check for safari 1.X and other non-supported browsers and only load jQuery through script tag injection or ajax if your user is using a supported browser. If the user is using a browser not compatible with jQuery then you can instead use plain javascript.
Get a copy of safari 1.x and see why jQuery breaks. Then fix it and ask for it to pulled into the release of jQuery 1.5. This relies on the fix being something that does can be done without hacking and that the jQuery team agrees is worth adding in.
Ask the user to use a compliant browser.
There might be some more options. I would personally lean towards asking the user to use a compliant browser because supporting Safari 1.x is ridiculous.
This seems like a case where progressive enhancement is needed.
You have to do multiple checks
see if $ exists
see if $.fn exists
[not sure if needed] check if $.support is a function
check for feature support as needed with $.support() http://api.jquery.com/jQuery.support/
At the end of the check, when jQuery reports that features you need are present - the rest of the script can run.
If you're not sure which features mentioned in the support you use, then this might need a single test on Safari 1.x to see what are the values returned by $.support(), but that is what your nasty old-browser-user can do for you (if you prepare code and publish) and report the resulting text. Then you compare the list with other [old] browsers that are accessible and determine features that are required.
The easy way would be to require everything and cancel all scripts if suport for any feature is missing. This will also rule out IE6 and IE7 and opera below 9.something and firefox below 2.0 or including - I'm not sure.
Use a server side language to detect if it's the old safari based on user-agent and load a different javascript file

Alternative methods for creating dynamic JavaScript?

Background
I am working on a project that runs in an embedded web browser in a small device with limited resources. The browser itself is a bit dated and has limits to its capabilities (HTML 4.01†, W3C DOM Level 2†, JavaScript 1.4). I have no documentation on the browser, so what I do know comes from trial and error.
The point is to retrieve dynamic content from a server so that only a minimal amount of inflexible code needs to be embedded into the device running the web browser. The browser does not support the XMLHTTPRequest object, so AJAX is out. Working with I do have, I wrote a bit of test code to dynamically insert JavaScript.
† Minor portions of these standards not supported
EDIT
While I cannot actually confirm it, I believe that this site may list the DOM support for the embedded browser because I see "Mozilla/4.0 (compatible; EBSWebC 2.6; Windows NT 5.1)" as the user agent in the server log.
<html>
<head>
</head>
<body onload="init()">
<div id="root"></div>
<script type="text/javascript">
<!--
function init() {
// Add a div element to the page.
var div = document.createElement("div");
div.id = "testDiv";
document.getElementById("root").appendChild(div);
// Set a timeout to insert the JavaScript after 2 seconds.
setTimeout("dynamicJS()", 2000);
}
function dynamicJS() {
...
}
//-->
</script>
</body>
</html>
Method 1
I initially implemented the dynamicJS function using Method 1 and found that while the code executes as expected in Chrome, IE8, and FireFox 3.5, the JavaScript is not actually retrieved by the embedded browser when the element is appended.
function dynamicJS() {
var js = document.createElement("script");
js.type = "text/javascript";
js.src = "js/test.js";
document.getElementById("root").appendChild(js);
}
Method 2
Looking for a work around, I implemented Method 2. This method actually works in the embedded browser as the JavaScript is retrieved and executed, but it does not work in other modern web browsers's I tested against (Chrome, IE8, FireFox 3.5).
function dynamicJS() {
var js= '<script type="text/javascript" src="js/test.js"> </s' + 'cript>';
document.getElementById("testDiv").innerHTML = js;
}
Question
I'm new to JavaScript and web programming in general, so I'm hoping one (or more) of the experts here can shed some light on this for me.
Is there anything technically wrong with Method 2 and if not, why doesn't it work in modern web browsers?
There is nothing technically wrong with method 2 but most modern browsers have very loose HTML parsers that tend to get caught up in the code that you're sending. Specifically they parse the </script> in your JavaScript string literal as an end tag. This manifests itself in two ways:
You'll see an "Unterminated String Literal" error.
All code after the </script> text will be rendered as text on the page.
A common workaround for this problem is to split the </script>. You can do this with the following code. Yes, I know its a hack, but it works around the problem.
function dynamicJS() {
var js= '<script type="text/javascript" src="js/test.js"></s' + 'cript>';
document.getElementById("testDiv").innerHTML = js;
}
Realistically though, you should be able to use your first approach strictly using the DOM APIs. I've found that some browsers can be really picky about loading scripts added by script in that they will only load them if they are placed as a child of the <head> element. This is how the YUILoader works, so I'd be surprised if it didn't work in all browsers.
Here's an example, you'll want to check this to make sure that it works in all browsers, and add some error checking around the assumption that there will be a <head> element but it give you the general idea.
if (!document.getElementsByTagName) {
document.getElementsByTagName = function(name) {
var nodes = [];
var queue = [document.documentElement];
while (queue.length > 0) {
var node = queue.shift();
if (node.tagName && node.tagName.toLowerCase() === name) {
nodes.push(node);
}
if (node.childNodes && node.childNodes.length > 0) {
for (var i=0; i<node.childNodes.length; i++) {
if (node.childNodes[i].nodeType === 1 /* element */) {
queue.push(node.childNodes[i]);
}
}
}
}
return nodes;
};
}
function dynamicJS() {
var js = document.createElement("script");
js.setAttribute('type', 'text/javascript');
js.setAttribute('src', 'js/test.js');
var head = document.getElementsByTagName('head')[0];
head.appendChild(js);
}
The innerHTML property has not yet actually been standardized, though all modern browsers support it, and the draft standard of HTML5 includes a definition of how it should work. According to the HTML5 specification:
When inserted using the document.write() method, script elements execute (typically synchronously), but when inserted using innerHTML and outerHTML attributes, they do not execute at all.
innerHTML was first introduced in Microsoft Internet Explorer 4, and due to its popularity among authors, has been adopted by all of the other browsers, which is what led to its inclusion in HTML5. So, let's check Microsoft's documentation:
When using innerHTML to insert script, you must include the DEFER attribute in the script element.
So apparently, in IE you can get scripts inserted via innerHTML to execute, but only if you add a defer attribute (I do not have IE in front of me to test this). defer is another feature that was first added to IE; it was included in HTML 4.01, but not picked up by any of the other browsers for quite a while. HTML5 includes a much more detailed description of how <script defer> should work, though it appears to be slightly incompatible with how it works in IE, as it does not allow execution of scripts added via innerHTML. The HTML5 definition of <script defer> appears to be implemented in Firefox 3.5 and Safari 4.
In summary, innerHTML hasn't really been standardized yet, but instead simply implemented by all of the browser vendors in slightly different ways. In IE, the original implementation, it didn't support execution of scripts except with a defer attribute, and defer hasn't been supported in other browsers until just recently, and so the other browsers simply don't support execution of scripts added using innerHTML. This behavior is what HTML5 is standardizing on, so unless Microsoft objects, is probably going to be what goes into the standard.
It sounds like the browser you are working with didn't do as good a job of implementing a compatible innerHTML, as it executes scripts added using innerHTML no matter what. This is unsurprising, as the behavior isn't standardized and so needs to be either reverse engineered or gleaned from reading the documentation of other browsers (which may not have included this fact in the past). One of the main goals of HTML5 is to actually write down all of these unwritten assumptions and undocumented behaviors, so that in the future, someone implementing a browser can do so without being misled by a spec that doesn't match reality, or without having to do the effort of reverse engineering the existing browsers.
It looks to me that you may have to use Method 2 on your embedded browser, and Method 1 if you want to run on the common desktop browsers. It would probably be a good idea to try Method 1 first, and fall back to Method 2 if that does not work, and then error out (or silently fail, depending on your needs) if neither one works.
A long shot but does the embedded browser support iframes?
And if so would you be able to use that to load in whatever additional JS you needed and access it via the iframe?

Categories