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

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!

Related

Weird 'ChildNodes of undefined' error in IE8 with an AngularJS application

TypeError: Unable to get value of the property 'childNodes': object is null or undefinedundefined
After making a long list of modifications to my application in order to support IE8, including:
running all of the views in their compiled form through W3C validator, setting up xdomain.js proxy to support CORS API calls, making some general restructures, etc.
I was very disappointed to find out that IE8 still throws this weird error at me, while IE9 works perfectly fine.
Making changes to the Angular-seo package to prevent it from running when the client is an IE browser.
Any ideas on what can it be?.
Make sure that all your tags are closed properly. I just spent hours trying to figure out what the problem was, until I noticed this in my code:
<span>some text<span>
I finally realized I didn't close the <span> tag properly. After that everything just worked.
Without the code you are running it is a bit difficult. However there is a command to use for debugging. First you need to identify which variable might not contain an object [i.e.
"object is null or undefined"].
For example, parent, then you can use
//next look to see if parent is something
if('undefined'==(typeof parent)) alert("variable empty:parent");
Once you find something that is empty that you are expecting to be an object then you can go trace back from there. Also use a browser debugged tool, to identify the line number of the error.
Often if using the child nodes, you may not have the right level or you need to access as an array i.e. you need something like.
parent.childNodes[0].childNodes[0].value
In IE you are also dealing with unsupported functions. So getElementById will work but some other similar ones do not. Again typeof can be useful.
//next ensure function supported
if( 'undefined'==(typeof document.getElementsByClassName) ){
alert("Not Supported"); // notice ^ no () required here
//...add code to handle differently when not supported
}
This may reveal if you can use a function
IE8 is so old and non-standards compliant it doesn't support childNodes[]. http://quirksmode.org/dom/core/#t70

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

Javascript Methodname is replaced with !==

On the server lies a html file with javascript code included.
This javascript code includes a method called something like "CheckObject".
This file works for all users, except one specific (but important).
He gets a javascript error and in his browser sourcode appears something unbelievable:
The methodname "CheckObject" is replaced with "Check!==ect", means the "Obj" of the method name is replaced with !==.
Why could that be?
Hope anybody can help me!
Best regards
If he's using a browser that supports extensions (like Firefox, Chrome, and some others), it's probably worth disabling all of the extensions and seeing if the problem goes away.
If you haven't already, I'd completely clear his cache in case there was a bad page transfer once and the browser is reusing it.
I can't imagine how it would be happening reliably otherwise.

How to help the debugger see my javascript, or how to best refactor my script to help make it debugger-friendly?

I have an ASP.NET MVC project that uses some simple AJAX functionality through jQuery's $.get method like so:
$.get(myUrl, null, function(result) {
$('#myselector').html(result);
});
The amount of content is relatively low here -- usually a single div with a short blurb of text. Sometimes, however, I am also injecting some javascript into the page. At some point when I dynamically include script into content that was itself dynamically added to the page, the script still runs, but it ceases to be available to the debugger. In VS2008, any breakpoints are ignored, and when I use the "debugger" statement, I get a messagebox saying that "no source code is available at this location." This fails both for the VS2008 debugger and the Firebug debugger in Firefox. I have tried both including the script inline in my dynamic content and also referencing a separate js file from this dynamic content -- both ways seemed to result in script that's unavailable to the debugger.
So, my question is twofold:
Is there any way to help the debugger recognize the existence of this script?
If not, what's the best way to include scripts that are used infrequently and in dynamically generated content in a way that is accessible to the debuggers?
I can not comment yet, but I can maybe help answer. As qwerty said, firefox console can be the way to go. I'd recommend going full bar and getting firebug. It hasn't ever missed code in my 3 years using it.
You could also change the way the injected javascript is added and see if that effects the debugger you're using. (I take it you're using Microsoft's IDE?).
In any case, I find the best way to inject javascript for IE is to put it as an appendChild in the head. In the case that isn't viable, the eval function (I hate using it as much as you do) can be used. Here is my AJAX IE fixer code I use. I use it for safari too since it has similar behavior. If you need that too just change the browser condition check (document.all for IE, Safari is navigator.userAgent.toLowerCase() == 'safari';).
function execajaxscripts(obj){
if(document.all){
var scripts = obj.getElementsByTagName('script');
for(var i=0; i<scripts.length; i++){
eval(scripts[i].innerHTML);
}
}
}
I've never used jquery, I preferred prototype then dojo but... I take it that it would look something like this:
$.get(myUrl, null, function(result) {
$('#myselector').html(result);
execajaxscripts(result);
});
The one problem is, eval debug errors may not be caught since it creates another instance of the interpreter. But it is worth trying.. and otherwise. Use a different debugger :D
This might be a long shot, but I don't have access to IE right now to test.
Try naming the anonymous function, e.g.:
$.get(myUrl, null, function anon_temp1(result) {
$('#myselector').html(result);
});
I'm surprised firebug is not catching the 'debugger' statement. I've never had any problems no matter how complicated the JS including method was
If this is javascript embedded within dynmically generated HTML, I can see where that might be a problem since the debugger would not see it in the initial load. I am surprised that you could put it into a seperate .js file and the debugger still failed to see the function.
It seems you could define a function in a seperate static file, nominally "get_and_show" (or whatever, possibly nested in a namespace of sorts) with a parameter of myUrl, and then call the function from the HTML. Why won't that trip the breakpoint (did you try something like this -- the question is unclear as to whether the reference to the .js in the dynamic HTML was just a func call, or the actual script/load reference as well)? Be sure to first load the external script file from a "hard coded" reference in the HTML file? (view source on roboprogs.com/index.html -- loads .js files, then runs a text insertion func)
We use firebug for debug javascript, profile requests, throw logs, etc.
You can download from http://getfirebug.com/
If firebug don't show your javascript source, post some url to test your example case.
I hope I've been of any help!
If you add // # sourceURL=foo.js to the end of the script that you're injecting then it should show up in the list of scripts in firebug and webkit inspector.
jQuery could be patched to do this automatically, but the ticket was rejected.
Here's a related question: Is possible to debug dynamic loading JavaScript by some debugger like WebKit, FireBug or IE8 Developer Tool?

Add a bookmark that is only javascript, not a URL

I'm thinking that the reason I can't do this is because it might be a huge security hole, but here goes...
I want to have a bookmark on my browser (FF3, preferably) that is only a snippet of javascript code. It would merely construct a URL based on the current date and then do a window.location = on that URL.
I know that I could make a page and keep it on my local machine and just refer to it that way, but I was just wondering if you could bypass that step and actually have the "location" of the bookmark really just be javascript. I could have sworn that this was possible years ago, but I can't find anything that tells me either way now.
What you want is a bookmarklet they are easy to create and should work in most major browsers.
Edit: Stack overflow seems not to allow creating bookmarklets in the context of the site, basically you can create a new bookmark and type the following in the location field
javascript:window.location='http://www.google.com/search?q='+Date()
to get a bookmarklet that searches google for the current date.
It is worthy of note that you can put that in a function wrapper as well. imranamajeed nicely illustrated that for us... but apparently I'm too new to the site to up his post. :P
so for clarity:
javascript:(function(){
location.href = location.href + "#";
})();
(the carriage returns did not affect performance in chrome and IE)
One minor catch. IE can only handle a 508 character URL in this format. If you save it in IE with a url longer than this, it will truncate without warning and thus fail.
If you need a really complex script, you'll need to use a "hosted" bookmarklet, where you have a short bookmark that injects a script tag into the page, to "call" your hosted bookmarklet.
It isn't as nice/portable, but its the only workaround.
Google Bookmark
javascript:(function(){var%20a=window,b=document,c=encodeURIComponent,d=a.open("http://www.google.com/bookmarks/mark?op=edit&output=popup&bkmk="+c(b.location)+"&title="+c(b.title),"bkmk_popup","left="+((a.screenX||a.screenLeft)+10)+",top="+((a.screenY||a.screenTop)+10)+",height=420px,width=550px,resizable=1,alwaysRaised=1");a.setTimeout(function(){d.focus()},300)})();
Well, I just created a bookmark in FF3, went back and updated it and added the following test:
javascript:alert('Wacky%20test%20yo');
Low and behold, after I saved and loaded, I was able to get my alert.
I'm sure you can work up something similar for your needs.

Categories