Introduction
What is Turnabout?
Turnabout is a plugin by Reify for Internet Explorer 6.x and above, which mimics the functionality of the Greasemonkey extension for Mozilla Firefox.
What is this Web Page?
Because scripting against web pages relies on the DOM, and because Internet Explorer stubbornly insists on its own interpretation of the DOM, there are some small differences which a scripter will have to take into consideration before making the jump from Greasemonkey to Turnabout. This page includes notes on using Turnabout, a list of common problems and their solutions, as well as code samples and cliches which can be used with Turnabout.
This page is not designed to teach javascript, the DOM, or anything like that.
You can view this page in Firefox, but some of the javascript examples use proprietary code that only works in Internet Explorer 6 and above.
Making a Script
The structure of a Turnabout script is the same as that of a Greasemonkey script, most likely on purpose, to maximize interoperability (most GM scripts I've tried work just fine in Turnabout with no changes). Anyway, there's a header, with metadata about the script, and then there's the script itself...
Code Sample: Metadata Header
The metadata header that precedes javascript code in a Turnabout script is almost exactly the same as its equivalent in Firefox, and in 99% of cases it will not differ. The only difference worth mentioning is that Turnabout supports the use of some scripts as libraries which other scripts installed on the same machine can call upon.
If a Turnabout script is a library, it will not have a @name value, but will have instead a @library field, which contains the title of the library.Again: a script must have either a @name value or a @library value, but never both.
// ==UserScript== // @name The Name of the Script [if it's not a library] // @library The Name of the Library [if it is a library] // @namespace http://www.example.com/namespace // @description This is a text description of what the script does. // @include http://www.url.of.pages.to.affect // @exclude http://www.url.of.page.to.keep.unaffected // ==/UserScript==
DOM Differences
There are a number of differences in how IE6 and IE7 interpret the document object model. Some of them will give you nightmares, but some of them may actually be handy. Where possible, I have posted workarounds.
Methods
For finding and manipulating elements and nodes. Internet Explorer 6 is generally pretty good about handling the DOM methods, and thankfully you won't notice a lot of change in your scripts in this regard. There is one small exception, however.
getElementById() is imprecise
The getElementById method is normally used to grab a single element, but in IE6 (and in IE7b3) using this method will also grab elements where the value of the NAME attribute matches the value of the ID you've asked for. For example:
var foo = document.getElementById('bar')
Will not only grab <div id="bar"> but <div name="bar"> as well.
Microsoft's Extensions to DOM Methods
Not happy leaving well enough alone, Microsoft has added several proprietary methods for node manipulation within the DOM. If you've only scripted within Firefox, you may not be familiar with them. Some of them are not useful at all. Quirks Mode, the cross-browser scripting site, recommends against all of them, but I list most of them anyway, because Quirksmode is interested in Cross-browser compatibility, not IE user-scripting.
clearAttributes()
Strips out the attributes of an element. Note that it does not remove theID=attribute.
MSDN Notes | ExamplemergeAttributes()
foo.mergeAttributes(bar)copies the attributes from nodebarto nodefoo.
MSDN Notes | ExampleremoveNode()
foo.removeNode(false)removes the nodefoo.foo.removeNode(true)removesfooand all of its child nodes!
MSDN Notes | ExamplereplaceNode()
foo.replaceNode(bar)replaces the nodefoowith the nodebar. Note that it replaces the entire contents of the node, over-writing any ID, name, or style information.
MSDN Notes | ExampleswapNode()
foo.swapNode(bar)pulls the old switcharoo, replacing the contains of nodefoowith nodebar, and vice-versa.
MSDN Notes | Example
Walking the DOM Tree
Actually, in terms of moving around in the DOM, Internet Explorer performs remarkably well in comparison to Firefox. All of the major properties are there, essentially identical to the W3C specifications, except that IE6+ has a couple of proprietary properties not shared by Mozilla.
Some of the new tools in your arsenal?
You Can Use the children[ ] Property!
The children property grabs all nodes that are HTML elements. This is different than the childNodes property, which grabs all children, including text nodes. So, the resulting array will by definition be equal or smaller.
Using the children Property
Usage of the children property is similar to that of the childNode property, but it will return a subset of all child nodes, including only the HTML elements.
This button reports "1" because the <button> element has a child text node.
This button reports "0" because the <button> element has no other element nodes.
You Can Use the sourceIndex[ ] Property!
The sourceIndex property exists in Internet Explorer, but not in Firefox as of 1.5.5. It allows you to refer to any HTML element on the page by an index number. This index number corresponds to the location of the same element in the array created by document.getElementsByTagName('*').
Here is an example of what the sourceIndex property can do (requires IE6+).
Using the sourceIndex Property
Where n is the number of an element in the document, foo
will take the element referred to by n as its value.
<button onclick="alert('Element #' + this.sourceIndex)">
Example of sourceIndex Property
</button>
Javascript Differences
Here's where it gets a little hairy. Microsoft and Firefox differ on a lot of points when it comes to Javascript. Sometimes, it's hard to figure out why Microsoft decided to implement some things the way it did (see Event Listeners), but the news is not all bad. Internet Explorer contains some useful "additions" to the comparatively standardized interpretation of javascript by Firefox.
Methods
getYear() Returns the 4-digit Year
In previous versions of both Firefox and IE, the getYear method could return a non-Y2k compliant 2-digit number if the value of Date object was between the years 1900 and 2000. Firefox addressed this issue by introducing the getFullYear() method, which always returns a 4-digit number, and now getYear is deprecated, though its behavior is unchanged. Internet Explorer supports both getFullYear and getYear as 4-digit numbers. So, pick your poison.
The stopPropagation Method Does Not Work
In both browsers, if nested elements share the same event, clicking on the inner element will fire that event for the inner element, but then also fire the same event again for the second element. This can cause problems. In Firefox, you could halt event bubbling using the stopPropagation() method, but IE does not support this. Instead, you must set the cancelBubble property for an event to true, which produces nominally the same effect.
Code Sample: Using window.event.cancelBubble
The following example shows how to use cancelBubble to stop events from bubbling up to their parents. In this example, there are three elements: a dark gray DIV, which contains a light gray DIV, which contains an INPUT button. Clicking on any element opens an alert box which tells us what we've clicked on. Notice that clicking the dark gray box reports "Dark Gray DIV", but that clicking on the light gray box will report first "Light Gray DIV" and then "Dark Gray DIV". The onclick event is bubbling up to its parent elements, just like it should. However, clicking the button will only report "The Button", because cancelBubble has been set to true in the function.
Here is the code:
<SCRIPT LANGUAGE="JScript">
function noBubble()
{
if (window.event.srcElement.tagName == "INPUT")
window.event.cancelBubble = true;
alert('The Button');
}
</SCRIPT>
<DIV style="padding:10px;background:#333333;"
onclick="alert('Dark Gray DIV')">
<DIV onclick="alert('Light Gray DIV')"
style="width:50;height:50;background:#cccccc;">
<INPUT TYPE="button" VALUE="Button"
onclick="javascript:noBubble();" />
</DIV>
</DIV>
The above code produces this in Internet Explorer. Click an area below:
Events
The resize Event Fire Multiple Times
In Firefox, resizing the browser window fires the resize event exactly one time, after the user has "let go" of the window. However, in Internet Explorer, the resize event will fire continuosly, every time the window size changes at all. This can make a huge difference if your script does something like increment a variable every time the window is resized.
Internet Explorer Supports onmouseenter and onmouseleave
You may already be familiar with the handy onmousever and mouseout events, which are fired by the mouse moving on top of or away from the element that calls it, but also any elements that it bubbles up to. This means that if a DIV has a button nested inside, and both these elements have onmouseoverDIV that contains it.
onmouseenter and onmouseleave get around this potential problem. They work in a similar manner, activating when the mouse touches an element, or moves away from it, respectively. The difference is that these events do not bubble up the document tree. Mousing to the button in the previous example would not fire the DIV's event if both were using onmouseenter.
Internet Explorer has the advantage here, since it supports both sets of events.
Example script: The Difference Between onmouseout and onmouseleave
The following examples illustrate the different behaviors you can produce by choosing between onmouseout and onmouseleave. What you should see is two sets of boxes. Each set of boxes is composed of one large box containing one small box. If you move your mouse into one of the larger boxes and then out again, it should report the color of the box you just left. However, if you mouse into one of the interior boxes and then back out again, you should notice a different between the set of boxes on the left, and the set of boxes on the right. This difference is should illustrate the ways in which onmouseleave treats the element hierarchy of a document differently than onmouseout.
Move your mouse in and then out of the following sets of boxes:
onmouseleave
|
onmouseout
|
|
|
|
Internet Explorer Does Not Support addEventListener()
Sadly, the standards-compliant method of event registration suggested in the W3C DOM Level 2 document is not supported by Internet Explorer. Using
element.addEventListener ('click',doSomething,false)
to assign a function to an event will not produce any result whatsoever. It goes without saying that related event registration methods (such as removeEventListener and dispatchEvent) do not work either.
The scripter is left with 2 cross-browser options, and 1 option that is proprietary to IE5.5+.
Internet Explorer's Options for Event Registration
- The following event registration methods produce the same result in Internet Explorer and can be used in place of the non-supported
- The Deprecated but Still-Used Way:
<A HREF="somewhere.html" onClick="doSomething()"> - The Non-Standard but Preferred Way:
element.onclick = doSomething - The Internet Explorer Only Way:
element.attachEvent ('onclick',doSomething)
addEventListener()
Turnabout supports GM_xmlhttpRequest
Turnabout supports the GM_xmlhttpRequest API, which is broader than XMLHttpRequest in that it can make requests to any URL. Notes on the use of XMLHttpRequest are included in the following section.
The XMLHttpRequest Object
The XMLHttpRequest object in Firefox and other browsers is a "port" of the XMLHttp object Microsoft implemented in IE5. In terms of usage, the two are identical, however, they must be created in slightly different ways. Whereas Firefox creates an XMLHttpRequest object directly, using the line:
var foo = new XMLHttpRequest();
In versions of Internet Explorer prior to 7, an ActiveX object is invoked which instantiates XMLHttpRequest:
var foo = new ActiveXObject("Microsoft.XMLHTTP");
And all subsequent code that sets properties for the object (e.g. onreadystatechange) need not differ at all. In other words, translating a Firefox script using the XMLHttpRequest object into IE6 only requires making the above changes. Not much of a difference, really. In cross-browser scripting, this can be a slightly less trivial difference, because it requires forking the code between Firefox and IE implementations, but we have the luxury of avoiding that step if our scripts are intended only for IE5-6.
It has been announced that the XMLHttpRequest object will be supported natively in IE7, but it is unclear whether that browser will continue to support the older method as well.
GET Request Caching Issues
Internet Explorer caches the results of a request if GET is used as the HTTP method. In this, it differs from Firefox and other browsers, which do not cache GET requests. If this is a problem for your script, you may use the POST method instead. This method will not be cached, however, it should only be used if the request is a "pure retrieval" request, i.e., one that only takes data from a source, and not one that makes any kind of change to stored information (for instance, incrementing a counter), as the same request will be made every time the request is invoked. More information on the difference between GET and POST.
What Will Happen When IE7 Is Released?
This is a list of the major disparaties between the attitudes of the two browsers. It's not complete, and it may not stay accurate for long, since Microsoft is well into beta testing on Internet Explorer 7. My understanding is that the new browser will (for better or for worse) not fix many of the standards non-compliance that is documented above. I've tried all of these scripts on IE7 beta 3, and they seem to behave identically to the same scripts running in IE6, so unless Microsoft intends to make major revisions to its interpretation of the DOM and Javascript, this guide should serve you well for the next couple of years. Here's hoping that any future change is towards compliance with Firefox, Opera, and Safari, if not the W3C.
