Site Home  »  Avoid Common Mistakes

Avoid Common Mistakes

Tags:  


This page is a first draft, which will be revised and improved. Please leave us a note on the community page regarding any issue.

This page will show what are the parts of the JavaScript language that you can not use in your app. Usually the restrictions are made to avoid security holes or due to the fact your app's code is running in a fully secured sandbox. As the basis of Crossrider and GreaseMonkey sandboxes are the same, most of the items in here are taken from Oreilly's article about GreaseMonkey pitfalls 1

Auto-eval Strings

In places where you want to set up a callback function (such as window.setTimeout to run a function after a delay), JavaScript allows you to define the callback as a string. When it's time to execute the callback, Firefox evaluates the string and executes it.
When using Crossrider, your app is running inside sandbox. When the timer event will raise the browser will try to find the function inside the string (for example, "myFunc") but the sandbox code does not visible anymore to the page and the event will not run.
So, instead of using:
setTimeout("myFunc()", 1000);
You should put a reference to your function so the when the event will happen it will have the reference of the function instead of trying to evaluate it and search for it on the original page. For example:
setTimeout(myFunc, 1000);
 or if you want to pass some params to the function, you can always use anonymous function. For example:
setTimeout(function() {
myFunc(yourParams);
},
1000);

Event Handlers

Another common pattern in JavaScript is setting event handlers, such as onclick, onchange, or onsubmit. The most common way to set up an onclick event handler is to assign a string to an element's onclick property:
var elmLink = document.getElementById('somelink');
elmLink.onclick = 'my_func(this)';
This technique fails in a user script for the same reason the first window.setTimeout call failed. By the time the user clicks the link, the my_func function defined elsewhere in the user script will no longer exist.OK, let's try setting the onclick callback directly:
var elmLink = document.getElementById('somelink');
elmLink.onclick = my_func;
This also fails, but for a completely different reason. The document.getElementById function returns an XPCNativeWrapper around an Element object, not the element itself. That means that setting elmLink.onclick to a function reference sets a property not on the element, but on the XPCNativeWrapper. With most properties, such as id or className, the XPCNativeWrapper will turn around and set the corresponding property on the underlying element. But due to limitations of how XPCNativeWrappers are implemented, this pass-through does not work with event handlers such as onclick. This example code will not set the corresponding onclick handler on the actual element, and when you click the link, my_func will not execute.

There are two ways to solve this issue:
Using addEventListener method:
var elmLink = document.getElementById('somelink');
elmLink.addEventListener("click", my_func, true);
Or even simpler is to use the native supported jQuery:
$jquery('#selector-to-identify-your-element').click(my_func);

Custom Properties

JavaScript allows you to define custom properties on any object, just by assigning them. This capability extends to elements on a web page, where you can make up arbitrary attributes and assign them directly to the element's DOM object.
var elmFoo = document.getElementById('foo');
elmFoo.myProperty = 'bar';
This doesn't work in Crossrider, because elmFoo is really a wrapper around the element named foo, and wrappers don't let you define custom attributes with this syntax. You can set common attributes like id or href, but if you want to define your own custom attributes, you need to use the setAttribute method:
var elmFoo = document.getElementById('foo');
elmFoo.setAttribute('myProperty', 'bar');
If you want to access this property later, you will need to use the getAttribute method:
var foo = elmFoo.getAttribute('myProperty');

Again, maybe an easier way is to use the native jQuery data function, where you can set and get custom attributes easily.

Communicating With The Page

Although it's not recommended, sometime you want to run local JavaScript function on the page, add a JavaScript variable or get the value of JavaScript variable.
There are differences between the Firefox and Internet Explorer sandbox (read more about it below) but there is unified way of how to communicate with the page.
Let's assume you have this script on the local HTML page:
var myLocalVar = 1;
function myLocalFunc(msg) {
alert(msg);
}
While in IE you can simply access both myLocalVar and myLocalFunc, in FF you will get an exception if you try to do so.
In order to access JS functions and variables on the local page you can use unsafeWindow variable. This is a reference to the real window object and it's very important to use only in case you trust the page. This method will work on both FF and IE and keep your application fully cross-browser:
var foo = unsafeWindow.myLocalVar; //will return 1
unsafeWindow.myLocalVar = 2; //will set the PAGE variable to 2
unsafeWindow.myLocalFunc("test"); //will run the page function from your app
Please note: in Internet Explorer calling to 'myLocalVar' and 'MyLocalFunc' (without the "unsafeWindow." prefix) will work as well but using this method will break your application on Firefox!

IMPORTANT: Differences Between Internet Explorer and Firefox

Due to lack of standards between browsers, Crossrider has developed app engine for each browser. This situation cause inevitable differences that you should consider when developing cross browser apps.
The main differences are related to the window object and JavaScript variables scope.

JavaScript Variables (how to define them properly and avoid conflict with the HTML page)

By definition, Firefox sandbox is more secured and it's totally different layer than the current viewing HTML page.
First, although it's not mandatory it will make you a better developer if you make sure to define your variables with "var". That means:
var myVar = 'some value';
is always better than using the variable without the var definitaiton, such as:
myVar = 'some value';
Why? when you define your variable with a 'var' definition it tells JavaScript that you want to use new fresh variable. If you omit the var definition, the JavaScript engine will try first to use a predefined variable under the same name. If for some reason you have used 'myVar' variable previously, it will override that variable instead of using a new variable (even if it's using the same name).

As a result of this, you should always provide meaningful names and declare your variables using 'var'!

How this general JavaScript best practice related to Crossrider apps?
in Internet Explorer you have access to the local page JavaScript variables (even without using unsafeWindow as described above). When you use the same variable name as the HTML page you are actually accessing and changing this exact variable! Let's see few examples to understand this better:
Let's assume the HTML page has this variable declared within the page:
var myVar = 100;
Now, you want to define new variable in your app and you decided to call it myVar as well. Here are some examples of what will happen in Internet Explorer:
Example 1
alert(myVar);
myVar = 1;
alert(unsafeWindow.myVar);
The first alert will print 100(!!) as it will use the variable that was already defined on the page itself. When you assign the value 1 to myVar it will change the variable on the page and might cause the original HTML page not to work as expected! (as you can see the second alert, which print the page's variable value will also print 1)

Example 2

var myVar = 1;
alert(myVar);
alert(unsafeWindow.myVar);
This is how you should handle variables in your code! by using "var" statement you actually ask to create fresh new variable (even though it has the same name of already exists variable).
As you can see from the output, the first alert will print "1" as it prints the local variable of your app and the second alert will print the page's myVar variable ("100").
As you can see, this way you can access both local and page's JavaScript variables.

Example 3
alert(myVar);
var myVar = 1;
alert(myVar);
After seeing examples 1 and 2, you could think the output of this code will be:
100
1
but it's NOT the case! Internet Explorer JavaScript engine is evaluating the code in a different way and because you have "var myVar" later in the code, the first alert will print "undefined"!

NOTE: defining variables inside functions and limit the use of global variables! Does IE (what about FF?) can access your app's global variables?

Avoid App's Multiple Running and Global Variables

As you can see the basic skeleton of Crossrider app has $jquery.ready function in it:
//"header" where you can define your functions, add includeds, workers and global variables.

$jquery(document).ready(function() {

//this is where your app actually starts

}); //for the jquery ready"
Firefox and Internet Explorer engines are bit different in the way and timing the run your application on the page. While in Firefox your app will run only once upon DOM loaded event occur, in Internet Explorer your app might run several times. This is why it's extremely important to put your app logic inside the jQuery ready function which will ensure your app logic will run only at DOM loaded event.
The 'header' section should be limited only to define more functions you wish to use and/or other Crossrider definitions (such as include and workers statements). If you will put "alert('test')" at the header section it will run only once in Firefox but will show the 'test' alert multiple times in Internet Explorer (when loading images, Ajax requests, etc.).

This is also related to defining global variables: sometime you need global variables to be defined all across your app. As described at the previous section, it's extremely important to define each variable with "var" like this:
//"header" where you can define your functions, add includeds, workers and global variables.

var myVar = 'my-var-value'; //this is the correct way

myVar2 = 'another variable'; //this is WRONG as it might cause conflicts with Internet Explorer pages!

$jquery(document).ready(function() {

//this is where your app actually starts

}); //for the jquery ready"
When possible, it's recommended to define all your variables inside your functions scope rather than global variables to ensure maximum security and hiding your variables from the HTML page.

HTML Events

Common pitfall

Some developers use inline HTML events when adding HTML elements or attaching events to an existing element.
When trying to add HTML events, such as onclick, onchange, etc. you can't put reference to your own app code.
As the app is running in a sandbox, the original html page has no way to access your code and functions.

That means that adding an event or adding an html element with inline event will not work. For example, adding:
<a href='....' onclick='myFunction'>
will not work as 'myFunction' function is in your secured sandbox and it will not be recognized when the html page will try to run it.


How to handle HTML events in your app

There are several ways to avoid this pitfall. Most likely the easiest way is to to use the mighty jQuery to attach events to an HTML element.

Examples:

Anonymous function:
$jquery('#my-element-id').click(function() {
//do something...
});

Predefine function:
function myFunc() {
//do something...
}

$jquery('#my-element-id').click(myFunc);


[1] GreaseMonkey common pitfalls


Codapt (Guest) 4 - days ago 
Just a note, unsafeWindow seems to me an artifact from greasemonkey. Crossrider no longer supports the construct. There's a forum post on the subject here: https://getsatisfaction.com/crossrider/topics/accessing_pageitem_data
Post a comment

Your Name or E-mail ID (mandatory)






 RSS of this page

Written by:   Version:   Last Edited By:   Modified