Dojo provides a lot of power and attempts to make it digestable in layers. For server-side developers, there's "widgets without coding", for HTML+CSS devs Dojo provides wonderful facilities for quickly building template-driven widgets, and for serious JavaScript and DHTML hackers Dojo is the standard library you will wish JavaScript always had.
This book serves as a guide to these layers, introducing concepts as you need them and working downward from high-level usage to getting your hands dirty in building your own widgets, custom namespaces, and unit tests.
In the Introduction, you'll get an overview of how Dojo can help you, what problems it solves, and where in the book you might be able to best find the information you're looking for. Also, remember that because this book is maintained by the community and is online, you can search it (and the rest of the Dojo site) at any time.
Lastly, thanks for checking out Dojo and the Dojo Book. It's your applications that have inspired us to build Dojo and the stories of how people are improving experiences with the toolkit that keep us going.
Dojo is a set of layered libraries. The bottom most layer is the packaging system that enables you to customize the distribution of Dojo for your application. On top of Dojo's package system reside language libraries such as the Dojo event system and the language utilities that greatly improve and simplify the lives of JavaScript developers. Environment-specific libraries are provided in some cases, but in most uses, you'll only need to know that all of Dojo works without incident across every major browser.
The bulk of the Dojo code lives in the application support libraries, which are too numerous to display completely in the diagram. dojo.gfx provides native vector graphics support across the major browser implementations of SVG and VML. dojo.lfx is a lightweight effects library, while dojo.io is "where the ajax lives."
Most of the "action" in Dojo is in the widget toolkit, that contains a template-driven system for constructing widgets, a declarative parser for putting widgets in pages without writing JavaScript, and a set of base components that implement common-case solutions to web interaction problems that HTML+CSS alone cannot solve.
Dojo is an Open Source DHTML toolkit written in JavaScript. It builds on several contributed code bases (nWidgets, f(m) and Burstlib), which is why we refer to it sometimes as a "unified" toolkit. Dojo aims to solve some long-standing historical problems with DHTML which prevented mass adoption of dynamic web application development.
Dojo allows you to easily build dynamic capabilities into web pages and any other environment that supports JavaScript sanely. You can use the components that Dojo provides to make your web sites more useable, responsive, and functional. With Dojo you can build degradeable user interfaces more easily, prototype interactive widgets quickly, and animate transitions. You can use the lower-level APIs and compatibility layers from Dojo to write portable JavaScript and simplify complex scripts. Dojo's event system, I/O APIs, and generic language enhancement form the basis of a powerful programming environment. You can use the Dojo build tools to write command-line unit-tests for your JavaScript code. The Dojo build process helps you optimize your JavaScript for deployment by grouping sets of files together and reuse those groups through "profiles."
Dojo does all of these things by layering capabilities onto a very small core that provides the package system and little else. When you write scripts with Dojo, you can include as little or as much of the available APIs as you need to suit your needs. Dojo provides:
Dojo is being built around a single markup language that provides application authors a (more) simple way of declaring and using responsive DHTML interface components. Renderings can be made available in several rendering contexts (such as SVG, or perhaps even the desktop or Flash), but the markup language (DojoML) and scripting language (JavaScript) will not change. Better yet, the DojoML parser accepts extended HTML and SVG as valid input, and can be used to easily create Degradeable Responsive Applications.
Dojo's homepage is: http://dojotoolkit.org.
Imagine for a second that you've just come from looking through the API of Dojo. You've memorized every function, every parameter, every example. All of the API has been studied and digested, but you're still not sure how to proceed. You know that there are many ways to do event handling, but you don't know the difference between them and you certainly don't know which one is best suited for your application.
I've made this assumption in writing the book; it allows me to focus on what's important about the book, and separate it from the API. With understanding Dojo, the why of Dojo, you're now free to learn the how by looking at the API. Or, if you already know the API, you're free to learn why it exists in the first place.
Though this book is a result of many people's work, I(we)'ll be speaking in the first person throughout this book. It not only allows a more personal interaction between the reader and the author, but quite often, describing an author's action is confusing when expressed in the plural form. "We're now opening our text editor" is an awkward sentence at best.
Saying "the reader" every sentence is dumb, and since you already know that you're the one reading it, I'll just refer to you as "you".
Decisions on the coding of Dojo aren't made by a single person. Almost every significant change to Dojo is done after discussion of how it is engineered. Because of this, I'll run into situations where I want to discuss how something was decided on. In this case, I'll be using the word "we". As in, "we decided that we wanted Dojo to be awesome."
On the way to a final topic, you'll be going through some higher-level topics. In order to understand the lower-level topics, and where they fit in to Dojo, you need to have some explanation of how you got way down there. To do this, each of the points along the tree, all the way down to the edge of the branch will have a few paragraphs of explanation. These are overviews for all topics contained underneath. It's worthwhile, if you have a specific topic that you want to read about, to go down the tree, reading each of the brief descriptions, before you read the article itself.
I want this to be more a book than a user manual. Therefore, I've tried to use fun analogies and brief stories. Many topics of this book are very difficult to process, and using existing knowledge to bring you up to speed is a great way to make these easy to digest. As a general guideline, I'll try to start an article with a paragraph, followed by a brief analogy or story, flowing into content. Because I don't want you to feel overwhelmed by code, examples will not be back to back. I think that if I have multiple examples, it's important to explain why I provided each one and the differences between them.
I think that the moral of the story of Goldilocks and the Three Bears is that things should neither be lacking, nor in excess, they should be "just right". That said, there's nothing worse than sitting down to get your learn on and be constantly switching from one topic to another, from one page to another, when you want to stay focused on what originally brought you to the article to begin with. Likewise, you don't want to get halfway through an article and realize that you missed both lunch and dinner. To prevent this, as a general guideline, each article in this book should take you about twenty minutes to read. That sounds just right to me.
I discuss in the book how Dojo is a toolkit, a framework, and everything in between. It's overwhelming just to learn what Dojo can do for you, much less learn what you can do to Dojo. Because of this, the book is split into three different parts.
Part 1 is all about using Dojo, the toolkit. That is, looking at a set of functions and objects that are available to you "out of the box" that don't require any tinkering. Ideally, you should be able to call a function, or initialize and object, without providing any custom logic to it.
Part 2 is meant to focus on more than just using things. We want you to know how to build your own widgets, packages, extend CSS and HTML and get into the nitty gritty of things, such as replacing the data provider for a widget.
Part 3 is a "look into the engine" explaining not only how thing work but why the Dojo developers designed them that way. Much of Dojo's code seems to work based on voodoo. For the advanced programmer, knowing this voodoo will allow you to write tighter, better code.
On any page where the dojo.book sidebar appears (including this page) click "create new page". The new page will have the currently viewed page selected as its parent by default
Because Dojo is a toolkit, it's potential uses are unlimited. The only real restrictions are that you're using it in an envornment that uses JavaScript. Though many of you will be reading this in the context of a browser, it's important to remember that JavaScript isn't limited to just that specific use.
With "Ajax" becoming such a popular buzzword, many are looking for an end-all solution to its complexities that not only work well in existing environments, but will allow much more complicated interaction in the future, allowing for very complicated transfer of data between client and server. With the aformentioned widget functionality, and an easy way to implement your own widgets, Dojo works very well alongside HTML.
Dojo also abstracts many of the differences between browsers that have pained developers for years. Things such as differing event objects and HTTP transport systems are a worry that will be forgotten.
Dojo provides a launching point for developing code. This means that you can quickly throw a project together for a client or boss without having to reinvent the wheel. We've built Dojo to be highly reusable, so code you have created for one project can be quickly moved to another without any refactoring.
Responsive applications make many calls to the back-end of web applications which have different call and response characteristics. Standardizing on a portable set of tools for this is beneficial to understanding and predicting overall system behavior.
Because Dojo's widget system sits on top of standard HTML, designers will be able to dive right in with a very shallow learning curve. Dojo allows for designers to add degradable functionality to their (already existing) page without having to implement any logic or strange programming language.
This book describes what Dojo provides and how to use the toolkit. You will find detailed information about the Dojo APIs in the API Reference Doc.
Three words are used to describe a stereotypical developer: geek, nerd, and dork. Many people assume that these words all mean the same thing when, in fact, they each have very specific meanings. Once you learn the meanings of each of these words, you'll quickly be able to run down the list of all of your friends listening to techno in dimly lit rooms and sort them into each category. Even better, you now have the power of more accurately describing a person.
Geeks, Nerds, and Dorks: A geek has a very focused knowledge of a subject (that guy that memorized the language of myst), a nerd is a master at many subjects (that girl you go to when you need homework help), and a dork is just plain socially inept (Napoleon Dynamite).
In software development, a framework is a defined support structure in which other project can be organized and developed. A framework typically consists of several smaller components; support programs, libraries, and a scripting language. There may also be other software involved to aid in development and meshing of the different components of a project. As you can see, dojo could be part of a framework, but it isn't a framework in itself.
A library is defined as a collection of related functions and subroutines used to develop software. They are distinguished from executables in that they are not independant programs; rather, they are "helper" code that provide access to common functions in one easy to manage location. After reading this you are probably saying, "Hey! dojo is a collection of libraries!", and you would be correct; however, dojo is much more than just a collection of libraries.
Now on to toolkits. A toolkit is generally used in reference to graphical user interface (GUI) toolkits. Basically, a library that is mainly focused on creating a GUI. Yes, dojo could also fall under this category, in fact our name implies it. Why do we call dojo a toolkit? Certainly not because it focuses mainly on GUI development, right? Well quite simply, because dojo is so much more than just a collection of libraries.
The previous paragraphs have probably left you still wondering what exactly we consider dojo. Obviously it is not a framework, but is it a toolkit or a library? Let's solve this once and for all. Typically, a library is a predetermined file that you include into your application, and that is how you gain access to those functions. However, with dojo, we have wrapped a package system around our libraries. This brings a slight twist to the idea of a library.
With this system we have broken each library up into several pieces. You have the core functions, and then several sub libraries where related, but less often used, functions are stored. This helps keep dojo's footprint based entirely on your needs as a developer. More about that will be covered later in the book but, for now, know that because of this flexibility, dojo is more than just a library, which falls into the realm of a toolkit with a few added functionalities. So as the name implies, dojo is a toolkit... and yet is more.
Want to know what Dojo is and how it can help you with your javascript development? In this section we will walk through the process of putting Dojo to use with its collection of pre-packaged "Out of the Box" widgets that can get you using Dojo right away. We will focus on developing programs using existing dojo components. This section will not get into any advance features, as they are included in future chapters. If you are new to Dojo or even new to JavaScript, then this chapter is for you. If, however, you are a seasoned programmer, you can still find reading this section purposeful as it will provide a helpful overview to the Dojo toolkit.
The sample application is the familiar Hello World app however, it does introduce the main features of Dojo and will build the foundation for developing much more sophisticated web applications.
Thanks to Lance Duivenbode and Seth Fair for help in writing this chapter.
This section talks about some of the tools available to help develop JavaScript programs. With the increasing popularity ofJavaScript there surely will be alot of changes in this space in a relatively short time. In this section you will find helpful tools that may or may not be part of Dojo.
Also remember to check the Dojo FAQs especially the Common Pitfalls section.
IDEs are available which support a wide range of web development activity, including editing Javascript and HTML files, deploying code to servers, and integration with existing features like source control. In addition, some include runtime tools and browser integration to assist in debugging (for example, myeclipse and ATF)
If you use one of the JavaScriptEditors that checks your syntax as you enter your code, you can be warned immediately of simple errors and save yourself a lot of time. Some Eclipse plugins even specifically support Dojo idioms by giving you tooltips for common Dojo functions and doing more complex analysis for errors (not just normal syntax checking).
Debugging on Firefox may need you to use the djConfig flag debugAtAllCosts (see further below for information). The debugAtAllCosts flag is sometimes necessary to locate exceptions or syntax errors. Even without the debugger, Mozilla and Firefox are unable to locate a line of code loaded by dojo.require() due to a flaw in the way eval() debugging hooks are implemented in Spidermonkey. Currently, the Javascript console will report all source references as the location of the eval() call itself (in the bootstrap code), but with an additional line offset equal to the offset in the corresponding *.js file.
If you are having wierd problems with Firefox, it is often worthwhile running Firefox in safe mode.
This is because installed extensions can interfere with the DOM tree, CSS, or even with javascript. In Windows there is a shortcut to start Firefox in safe mode from within the Mozilla Firefox folder, from the Start button.
If you are using a debugger with IE, go to Tools | Options | Advanced and make sure that Disable Script Debugging is not ticked. Using debugAtAllCosts can also help significantly - read about it below.
The Safari Developer FAQ has some general information about developing with Safari, as well as instructions on how to turn on a debug menu that allows showing a Javascript console.
There is a "dom inspector" type tool, but it requires using a nightly build of Safari.
debugAtAllCosts may have unobvious side-effects - you should only use it if you are actually debugging. If you hit problems with files not loading or __package__.js then check that you are using writeIncludes() correctly, and try removing this flag to ensure that the flag is not the issue.
You generally should not use a packaged build if you want to
debug, because in a packaged build the majority of code will end up in your dojo.js file
(and usually be obfuscated due to compression).
To use it needs one more line in your page. Here's how you might use it:
<script>
djConfig = {
isDebug: true,
debugAtAllCosts: true
};
</script>
<script src="/path/to/dojo/dojo.js" />
<script>
// dojo includes here
dojo.require("dojo.myModule");
dojo.hostenv.writeIncludes(); // this is a new line
</script>
// ...
Once you've structured your code this way, if you open up your debugger, you will see all the constituent files listed and you can then set breakpoints. You may need to add in dojo.require() statements for files that you want the files to show, above the writeIncludes(). If you do not, then any javascript files that need loading after the call to writeIncludes().
You
can see an example of using debugAtAllCosts with your own widgets
within your own namespace in
dojo/src/tests/widget/test_Custom_Widget_Debugging.html
When debugAtAllCosts is set to true, then dojo.require() does not actually include the package at that point. It only gets included in the page once you call dojo.hostenv.writeIncludes().
Normally when you dojo.require() a file that is not already loaded, it is fetched using using an xmlhttprequest, and loaded into your Javascript runtime environment using an eval() statement (each of the eval items in the RHS screenshot above is actually a js file). When the file is eval()'ed, dojo.require() calls will occur, and those then recursively load any further dependencies (those which are not already loaded).
When you set debugAtAllCosts to true, the file is still fetched using xmlhttprequest but it is not eval()'ed. Instead a regexp is used to find the dojo.require() dependencies, and required files may be fetched and searched recursively for dependencies. Each of the required javascript filenames is added into a list of pending files that need to be loaded (the list is in order so that dependent files are loaded in the correct order). When you then call dojo.writeIncludes(), inside the head of the document script tags are appended with src attributes set to the js files that need to be loaded. The script tags cause the files to be loaded by the browser: files loaded this way are understood by debuggers much better than when the files are eval()'ed.
If you just need to debug one or two known files, then you can just include those specific files using script tags after dojo.js. e.g.
<script src="/dojo.js" type="text/javascript"></script>
<script src="/mine/myWidget.js" type="text/javascript"></script>
You cannot do this for files that are packaged into your
dojo.js (Of course, using packaged files makes debugging hard in other
ways too!).
Javascript is slow and you want to speed it up?
Use a ProfilingJavascript tool to help you find the functions where all the time is spent, and optimise those routines.
A great many problems can be resolved by watching the traffic between the browser and the server. If you are having any problems with Ajax calls such as bind() or with js/html/css/jpg files not loading as you would expect, this is often the best way to diagnose them quickly. Also if you are having problems with required files not loading then this is a good starting point to find out why.
Sometimes Dojo's error messages about syntax errors are really cryptic due to how dojo loads files via dojo.require(), however you can get a better message by simply directly including the js file with the [script] tag.
The debugAtAllCosts flag can also be used.
There's also a lint program that Brian has recommended.
You can also find the debug error message in the dojo source code and remove the corresponding try/catch statement so that when the error occurs you get a debugger breakpoint instead of a dojo debug message.
Javascript has a debugger keyword that forces a breakpoint to occur. Just insert debugger; and if you have a debugger for your browser, then it will stop at the debugger keyword.
This is especially useful when using Venkman, because otherwise it can be difficult to get Venkman into debugging mode (e.g. you try to click on a line of source and it puts in a [F] future breakpoint).
It also works with the Firebug extension for Firefox.
You can debug the old fashioned way (print statements in the code). There are three functions for this:
Debugging output:
Alternately, in djConfig, you can specify which element they are appended to by
providing an id of that element: i.e.:
var djConfig = {
isDebug: true,
debugContainerId : "dojoDebug"
};
and have a div
Ajax Toolkit Framework is an incubator project within the Eclipse Web Tools Project. It is also a pluggable framework for other AJAX tools. It provides Javascript syntax checking, server deployment, Mozilla embedding, runtime tools such as XHR Monitor, and a debugger based on Mozilla Spidermonkey, JSD, and the Eclipse debug UI. Eclipse integration provides access to existing plugins like ant, subclipse for SVN, server development tools, etc. AJAX "Personalities" provide the potential for tighter integration with Dojo and other toolkits, although so far very little has been done in this area.
There's visual Javascript
validation built into ATF.
It includes both basic syntax validation and optionally Jslint
validation for less obvious potential syntax problems. It works just
like MS Word as-you-type spellchecking and Eclipse as-you-type Java
validation - as you type if you make a syntax error (or just do
something somewhat sloppy) you'll get a red or yellow squiggly under
the warning/error and an explanation in the margin. A background task
will run validation against all files and place markers in the code
such that you can see errors across a project.
You
can download and use ATF and its prerequisite
Eclipse components (Eclipse SDK, WTP, EMF, GEF, JEM, xulrunner) and
choose to use all of ATF or just install the "javascript" feature which
implements this validation.
Note
- you have to fetch and manually install jslint.js, Dojo and xulrunner due to Eclipse policies - make sure to read the ATF
readme to get the full functionality of ATF.
JSEclipse
is a commercial Eclipse plugin which provides a rich Javascript editor
with support for syntax validation, code completion and Dojo idioms.
To be written...
JS-Sourceror performs syntax checking and variable type and flow analysis on JavaScript files.
See http://skrul.com/blog/projects/javascript
It also does syntax checking, as well as scope checking and structure browsing.
Aptana is an open-source, JavaScript-focused IDE, including code assist for Dojo. It works as a stand-alone app on Mac, Linux, or Windows, or as a plug-in for Eclipse.
Profiling is the term for looking at where time is spent by any Javascript code. If you have a problem with code taking too long, then it helps to use a profiling tool to diagnose exactly where all the time is being used.
When using some profiling tools you may need to use debugAtAllCosts and not a packaged version of Dojo (see DebuggingJavascript). Using debugAtAllCosts will enable the profiling tool to allocate time spent per function to the correct source file -- otherwise you will end up with the elapsed times being allocated to anonymous functions which will make it difficult for you to understand!
dojo.profile is a package that implements timing primitives for recording how much time is spent in particular functions. To learn how to use it, the best resource is to search the tests directory and look at how it is used by various tests.
Displays the time it took to load each file.
Free but a bit buggy to use. I have found it easiest to get into debugging mode with Venkman by using a debugger keyword in your sourcecode. Run the Venkman debugger, then run your code and it will stop at the breakpoint.
Venkman contains a profiling tool, although the reports are a bit difficult to use. It does work.
If you are new to Dojo or want a quick overview of the toolkit then take a look at the HelloWorld Tutorial. This tutorial describes step by step how to build a simple Dojo application. You will learn some basic concepts about widgets, events and how to connect to the server code. Each step builds on the previous until you have a working application. It takes about an hour to go through the tutorial.
Dojo offers many editions of its code base. At first, it might seem daunting to try to figure out exactly which one you need. To quickly dispel any worries, let me assure you that every single edition of dojo provides a fully functioning system. Whether you download one of our editions, or the full, uncompressed source code, you'll be able to perform any of the examples discussed in this book.
TODO: fold in information from the READMEIn order to use dojo in your HTML pages, you need three sections of code, in this order:
1. Flags
<script type="text/javascript">
djConfig = { isDebug: false };
</script>
The flags control various options of dojo; often developers will set isDebug to true in order to get debugging output on their page. (There are also some other flags related to debugging; see the Debugging section of the code for details.)
2. Include the dojo bootstrap
<script type="text/javascript" src="/path/to/dojo/dojo.js"></script>
This includes the bootstrap section of dojo, and if you are using a release build, then dojo.js will also include code for some of the dojo modules.
3. Define what resources you are using
<script type="text/javascript">
dojo.require("dojo.event.*");
dojo.require("dojo.io.*");
dojo.require("dojo.widget.*");
</script>
This section is much like java's "import" statement. You specify every resources that you are using in your code. However, note that widgets are a special case and don't need to be declared explicitly, assuming that (as is the case with the built-in dojo widgets), a manifest file defines which widget is in which resource file.
Even though Dojo is made up of many different packages, it's frequently used in very specific ways. Because of this, we've created special editions of Dojo aimed toward these users. A visit to dojo's download page will show you which editions are currently available, such as the Ajax and Widget edition.
An edition is very simple, really. The important file is dojo.js, which is created by merging the most frequently used packages and compressing the resulting code. This means that when you have a script tag that calls dojo.js, you're getting not just the basic Dojo codebase, but the additional functionality that is most pertinent to your specific use.
You might wonder why so much additional code comes in each edition. This is the full code base, and allows you to use functionality that is outside of your specific build. It means that even if you have a very specifically tailored edition of Dojo, you aren't limited to only using that featureset. If your site uses the event and I/O systems heavily, but one of your pages uses a widget, then you don't have to worry that your widget will break. This also means that any of the examples in this book will work no matter what edition you've downloaded.
Events in JavaScript or Dojo based applications are essential to making applications work. Connecting an event handler (function) to an element or an object is one of the most common things you will do when developing applications using Dojo. Dojo provides a simple API for connecting events via the dojo.event.connect() function. One important thing to note here is that events can be mapped to any property or object or element. Using this API you can wire your user interfaces together or allow for your objects to communicate. The dojo.event.connnect() API does not require that the objects be Dojo based. In other words, you can use this API with your existing interfaces.
Here we connect the event handler, helloPressed, to the onClick property of the hello button element. When the button is clicked the funtion helloPressed will be called. function helloPressed(){
alert('You pressed the button');
}
function init(){
var helloButton = dojo.widget.byId('helloButton');
dojo.event.connect(helloButton, 'onClick', 'helloPressed')
}It is also possible to use the Dojo event model to connect simple objects. To demonstrate, lets define a simple object with a couple of methods:
var exampleObj = {
counter: 0,
foo: function(){
alert("foo");
this.counter++;
},
bar: function(){
alert("bar");
this.counter++;
}
};
So lets say that I want exampleObj.bar() to get called whenever exampleObj.foo() is called. We can set this up the same way that we do with DOM events:
dojo.event.connect(exampleObj, "foo", exampleObj, "bar");
Now calling foo() will also call bar(), thereby incrementing the counter twice and alerting "foo" and then "bar". Any caller that was counting on getting the return value from foo() won't be disappointed. The source method should behave just as it always has. On the other hand, since there's no explicit caller for bar(), it's return value will be lost since there's no obvious place to put it.
In either case, each time dojo.event.connect is called with the same arguments it will result in multiple connections. Later we will discuss strategies on how to guard against this.Notice that dojo.event.connect takes a different number of arguments in the examples above. dojo.event.connect determines the types of positional arguments based on usage.
The Dojo event system allows you to connect to DOM elements or nodes or plain JavaScript objects. The API is sophisticated enough that it allows you to connect multiple listeners to a single object so you can have multiple actions as a result of a single event such as a mouse click. Of course there is an API to disconnect the listeners too. The Connecting the Pieces chapter describes the Dojo Event system in more detail.At Dojo, we're committed to making DHTML applications usable, both for authors and for users, and with a lot of help from our friends, particularly Aaron Boodman and Mark Anderson, we have come up with solutions to the usability problems outlined above. We're providing it in a single, easy to use API and a package that requires only two files to function. The dojo.io package provides portable code for XMLHTTP and other, more complicated, transport mechanisms. Additionally, the "transports" that plug into it each provide their own logic to make each of them easier to use.
Most of the magic of the dojo.io package is exposed through the bind() method. dojo.io.bind() is a generic asynchronous request API that wraps multiple transport layers (queues of iframes, XMLHTTP, mod_pubsub, LivePage, etc.). Dojo attempts to pick the best available transport for the request at hand, and in the provided package file, only XMLHTTP will ever be chosen since no other transports are rolled in. The API accepts a single anonymous object with known attributes of that object acting as function arguments. To make a request that returns raw text from a URL, you would call bind() like this:
dojo.io.bind({
url: "http://foo.bar.com/sampleData.txt",
load: function(type, data, evt){ /*do something w/ the data */ },
mimetype: "text/plain"
});
That's all there is to it. You provide the location of the data you want to get and a callback function that you'd like to have called when you actually DO get the data. But what about if something goes wrong with the request? Just register an error handler too:
dojo.io.bind({
url: "http://foo.bar.com/sampleData.txt",
load: function(type, data, evt){ /*do something w/ the data */ },
error: function(type, error){ /*do something w/ the error*/ },
mimetype: "text/plain"
});Regular web requests and Ajax requests with dojo.io.bind are much alike. Both use URL's and both use the HTTP protocol. But with browser requests, it is always clear to user when something goes wrong. You may get a 404 - Page Not ound, or a Server Unavailable, or at least something that says "Error". Ajax requests happen in the background, so when they error out the user won't know. Even worse, if the response never comes the browser may appear to lock up.
That's why it's extremely important to provide an error handler and a timeout handler with dojo.io.bind. You should consider these as critical as URL or the load function
At the very least, you should alert the user that something went wrong. Here's an example:
var kw = {
url: "/cgi-bin/timeout.cgi",
load: function(type, data, evt){
document.myForm.myBox.value = data;
dojo.byId("boxLoadTime").innerHTML = new Date();
},
error: function(type, data, evt){
alert("Holy Bomb Box, Batman! An error occurred: " + data);
},
timeoutSeconds: 2,
timeout: function(type, data, evt){
alert("I am tired of waiting.");
}
};The error() function takes the same arguments that load() does. But unlike load(), the only useful parameter is data, which contains the error message.
The timeoutSeconds and timeout parameters should be used together. timeoutSeconds defaults to 0, which means "wait forever". (In other Ajax libraries, this is called a synchronous request). 0 is not desirable. Even if you expect the request will take a long time, you should set a high value here (e.g. 3600 = 1 hour), not 0. The timeout function takes the same arguments as error() and load(), but they are rarely consulted.So let's apply this to a trivial example. Suppose you have a text file on the web server with url my_message.txt.
Hello Ajax World!
You would like to load the file contents in an INPUT box without refreshing the page. Here's how:
<html>
<head>
<title>Insert title here</title>
<script type="text/javascript" src="/js/dojo/dojo.js"></script>
<script type="text/javascript">
function loadRemotely(e) {
var kw = {
url: "my_message.txt",
load: function(type, data, evt) {
document.myForm.myBox.value = data;
dojo.byId("boxLoadTime").innerHTML = new Date();
},
method: "GET"
};
dojo.io.bind(kw);
} function initAjax() {
dojo.event.connect(dojo.byId("loadIt"), "onclick", "loadRemotely");
}
dojo.addOnLoad(initAjax);
</script>
</head>
<body>
Form loaded at:
<script type="text/javascript">document.write(new Date());</script>
<form name="myForm">
<input type="button" id="loadIt" value="Click here to load value.">
<input type="text" name="myBox" size="50" />
Text loaded at: <span id="boxLoadTime">N/A</span>
</form>
</body>
</html>Click the button and the value in my_message.txt automatically loads into the box. The date and time stamps on the page prove it does not reload when the user clicks the button.
This demonstrates the bare minimum you need for dojo.io to connect with Ajax. At the very least, you need an object of type dojo.io.Request. In our examples, this is the "kw" variable. dojo.io.Request needs the following:
There are many more optional parameters, and we'll see these in later examples.
Then just call dojo.io.bind with the dojo.io.Request variable. Here, bind means "connect this page with that URL and let things happen."
Why do all that for some static text? The answer is ... you won't. The URL is going to be a server-side program which returns content - mostly XML (the "X" in AJAX), but it could be text, HTML, or even binary data.
The Url of dojo.io.Request may contain parameters, like so:
url: 'myprogram.php?firstname=Chicken&lastname=Little&key=111111'
There are two problems: (1) it's difficult to URL encode everything, (2) it doesn't allow for dynamic parameters. The above works fine for everyone named Chicken Little, but ...
It's easier and more flexible to send an entire form of data. And you can do that with the formNode parameter of dojo.io.Request.
<script>
var kw = {
url: "myprogram.php",
load: function(type, data, evt){
document.myForm.myBox.value = data;
},
error: function(type, data, evt){
alert("Holy Bomb Box, Batman! An error occurred: " + data);
},
timeoutSeconds: 2,
timeout: function(type, data, evt){
alert("I am tired of waiting.");
}
formNode: dojo.byId("myForm");
};
dojo.io.bind(kw);
</script>
<form id="myForm">
<input type="hidden" name="key" value="111111" />
<input type="text" name="firstname" length="50" />
<input type="text" name="lastname" length="50" />
<input type="text" name="myBox" length="50" />
</form>
Dojo's code is split into logical units called modules. These are much like packages in Java, except that in Dojo a module can contain both constructors (like classes in Java) and simple functions.
For example, the "dojo.html" module contains a number of functions, such as dojo.html.getContentBox(). The "dojo.dnd" module contains a number of constructors for things like HtmlDragObject etc.
Note the naming convention - functions start with a lowercase letter, and constructors (which are technically functions but act more like classes) start with a capital letter.
Modules could be called "namespaces", except for the fact that "namespaces" has a different (but related) meaning w.r.t. widgets.
In the simple case, a Dojo module is defined in a single JavaScript file. But sometimes, a single module is split into multiple files.
For example, the dojo.html module, although originally defined in a single file, was getting too big, so we split into multiple files. This is for performance reasons, so that the browser only downloads the code it needs.
Unfortunately this implementation detail is not transparent to the Dojo user. You have to know which file contains the functions you
need, and then include that file explicitly.
Each of these files is called a resource.
dojo.require("dojo.html.extras")will include the file src/html/extras.js, which in turn defines some of the functions (but not all the functions) in the dojo.html module.
A single JavaScript file never defines multiple modules, although often a single file will define multiple constructors. In Java this would be equivalent to defining two classes in the same file.
All of this complication is for performance reasons, trying to balance
How do you know what resources to dojo.require()?
1. modules
First, find out what modules you are using. In this example we'll assume you are using dojo.lfx.html.
2. resources
By reviewin the API doc you can see that dojo.lfx.html is defined in two files:
Depending on what functions you are using, you will either do
dojo.require("dojo.lfx.html");or
dojo.require("dojo.lfx.html");
dojo.require("dojo.lfx.extras"); dojo.provide("dojo.html.extras")For historical reasons, the dojo.provide() call serves two functions:When browsing through web sites and online applications, there are hundreds of widgets that come across your screen. Each button in your web browser is a widget. Each text entry box is a widget. We all know what a limited set of widgets that standard HTML provides: an input box, a button, a hyperlink.
Dojo widgets take an item like a text input box and adds functionality of a more user friendly object, like a graphical calendar to choose a date from. And it does this without breaking the original item on which the new functionality is built on.
The first thing that you'll notice about widgets is that the are somewhat similar to a macro expansion, such as C's #define. Your source HTML is a simple
<button dojoType="Button" id="foo"> Click me </button>
and yet a pretty blue button shows up, and when you look at the generated DOM, it's a complicated tree of DOM nodes with a lot of absolute positioning and background images.
See? Click me
But, that's not all there is. For each widget, besides the visible manifestation, there's also a pure javascript object that manages that generated DOM tree.
In the above case, the generated javascript object is called, unsuprisingly, "foo". You can get it by doing:
var myButton = dojo.widget.byId("foo");A Dojo widget wraps around your HTML. It looks at how the HTML is organized, what type of tags have been specified, what attributes they have, which tags are children of what other tags. All of these different variables allow a versatility in how skeletons are laid out, in what tags they use, in how the widget chooses to interpret them.
You can think of a widget as the final form that covers a skeleton. The top widget layer reflects the structure and functionality of the skeleton it sits on rather than covering it up.The simplest example of a skeleton is a single tag. A form input box, for example, is designed to simply accept a value.
<inputvalue="default">
But what happens when we want to help the user choose from an existing list of items? Enter the ComboBox widget.
<input dojoType="ComboBox" value="default">
As you can see, this is a functional skeleton. Not only is there an input box if the user does not have JavaScript enabled, but we can use the widget as part of a normal form. After all, we've only added on to it, we haven't changed the original purpose of the input element.
But this widget provides no data for the combo box to use.
<input dojoType="ComboBox" value="default" dataUrl="comboBoxData.js">
If you want to produce valid W3C HTML, you will have to use an alternative method to building your skeletons. The dojoType attribute is not recognized by W3C and its validation tool will complain about it being their. Below are two examples that do not use invalid attributes to build on your skeletons.
<input class="dojo-ComboBox" value="default" dataUrl="comboBoxData.js">
<dojo:ComboBox value="default" dataUrl="comboBoxData.js">
When there are certain attributes (dataUrl in this example), this widget will use that information to process the information to be contained in the combo box.
Sometimes you'll have skeletons defined in code and not even know it. In fact, many of the widgets provided by dojo assume that the underlying HTML is the same that would be encountered in every day life. Making this select element into a widget is as simple as adding the dojoType attribute.
<select dojoType ="combobox">
<option value="foo">foo</option>
<option value="bar">bar</option>
<option value="baz">baz</option>
<option value="thud">thud</option>
</select>
As you can see, you've just gained a whole lot of something for nothing. And, the data that will be used in the combo box is provided in a way that any web designer would understand.
TODO: this section needs to differentiate between ComboBox and Select.
You may ask yourself, "Why would I use wigets?" I honestly couldn't have answered this a few months ago, before finding Dojo anyway. The answer is really quite simple once you see how widgets improve the functionality and appearance of your web applications, without taking a long time to implement.
Widgets "enhance the user experience". In layman's terms, that means that you can design web pages that are easier for people use, more quickly understandable, less error-prone, and flashier than web pages in plain html.
Easier to use - the Select widget for example, will narrow down the list of available choices based on keystrokes the user enters. That makes it faster to use than a normal HTML select box.
More quickly understandable - a web page with tabs will let the user easily navigate between different sections, and helps to make clear all the different sections of code that are on one page.
Less error prone - validation widgets will immediately notify users when they have entered an incorrect value, and/or automatically correct the value.
Flashier - dojo's menu code will fade in / out menus, or use some other effect, rather than a plain appear/disappear that you get with pure CSS menus
Widgets make it easy for web developers to add enhanced functionality. Here's why:
Web designers are generally very good with HTML. The really good ones are usually so involved in design that they don't even bother with learning the extra stuff that comes along with its dynamic aspect.
For these kinds of users, specifying widgets via HTML is a great solution. Not only is markup useful for being able to design with a placeholder element laying in wait, but many of the widgets actually analyze what they've laid out and use them as if properties were passed to the JavaScript object.
A great example of this is the tree widget. All that the designer has to do is lay out an HTML list, assign some attributes and they can have things going without having to touch a bit of code.
Widgets solve a bunch of issues (like cross-browser support) behind the scenes, so you don't have to worry it. See the next section for more detail about that.
Using Dojo to add dynamic capbilities to your web applications can be a little daunting at first.
Let's look at the programming model in more detail to better understand how to use Dojo to build some really cool apps. The programming model is object-oriented inspired and includes "classes" with methods and multi-level
inheritance coupled with aspect-oriented event model famous in JavaScript. You will find the API doc quite useful to determine the methods and properties that are inherited from the parent "classes".
Thanks to Eugene Lazutkin and Bill Keese for help on this chapter.
Dojo supports two programming models, declarative and programmatic. Which one you use depends on what you are doing. In most cases, the declarative model may be easier to use as it is markup but there are times when you will use the programmatic model. You can, of course, intermix the models on the same page as needed.
The best way to show the models is through the use of widgets which can be used either declaratively or programmatically. Although both models are available we will mainly use the declarative model through out this book when both are an option.
The tutorial used the declarative model to create a button on the page using the code below.
<BUTTON widgetId="helloButton" dojoType="Button">Hello World!</BUTTON>
The following declarative formats are equivalent.
<?xml:namespace prefix = dojo /><dojo:widget></dojo:widget> <DIV dojoType="widget"> <DIV class=dojo-widget></DIV></DIV>
We will discuss the declarative model in more detail shortly.
You can also declare widgets programmatically using the dojo.widget.create API as follows. When declaring widgets programmatically, the API returns the widget id which you use to call the appropriate methods.
var myTabPane= dojo.widget.createWidget("TabPane", {id: "myTabPane"}, srcDiv);
Which approach to use is discussed later in the book but for now we want to introduce the idea that you have different options.
Before we get into the declarative model let's look at the widget infrastructure to better understand what methods are available when using widgets in your pages.
In addtion to methods, each class has parameters that are useful. There are two types of parameters listed here.
dojoType instructs Dojo how to process the element when the page is loading. Keep in mind that Dojo manipulates the DOM as it renders the page so you must use the Dojo APIs to access the widget ids. Dojo keeps a reference of all widgets it has created that can be accessed with the dojo.widget.byId function - providing you specify either the widgetId or id attribute in your markup. Also you can use dojoAttachEvent using this method.
In order to make namespaces practical and easy to use, Dojo has a concept of modules and resources. This concept was introduced in Part 2 Modules, Resources and Widget Namespaces when we first saw the HelloWorld tutorial. Resources are used to define a namespace and can be dynamically loaded on demand.
In order to load a resource you should request it using dojo.require(). It takes a string of text, which denotes a downloadable component. The content of this string is interpreted and a subject of Dojo conventions. First, it is traced to a single JavaScript file on disk (on web server). There are ways to affect this interpretation but out of the box it has a very sane behavior:
dojo.xxx => dojo/src/xxx.js
dojo.xxx.yyy => dojo/src/xxx/yyy.js
dojo.xxx.yyy.zzz => dojo/src/xxx/yyy/zzz.js
and so on. For example if you see a statement like that:
dojo.require("dojo.json");
It will load a file named dojo/src/json.js. If you don't know what it contains, you can go and look it up. To sum it up: a namespace hierarchy essentially reflects a file system hierarchy. By convention, if you see somebody using dojo.foo.bar.baz(), you can find it's definition in dojo/src/foo/bar.js, or, if it is not there, in one of files of dojo/src/foo/ (more on that case later), or in dojo/src/foo.js.
Dojo allows you to define your own resources and modules. For your custom resource you define your own top-level object. I will use "example" in my examples. If the Dojo loader sees that the first component is not "dojo", it applies following rule:
example.xxx => dojo/../example/xxx.js
example.xxx.yyy => dojo/../example/xxx/yyy.js
example.xxx.yyy.zzz => dojo/../example/xxx/yyy/zzz.js
Be sure to put the dojo.provide() call in your module resource, so that Dojo knows it found the right file. For instance, xxx/yyy/zzz.js should have dojo.provide("xxx.yyy.zzz") in it.
Essentially it means that on your web server next to the "dojo" folder there is an "example" folder, which files are interpreted in the same way.
There is more information about the namespace and how it is used in the Widgets chapter.
JavaScript is at its heart an object oriented language, but it is a prototype based object oriented language which does not have the same structure as class based languages like Java.This concept can be hard for new programmers who are not familiar with its construct. Dojo brings the object orientedness into a more familiar domain by modeling concepts that can be followed from Java and letting the toolkit handle the prototyping, inheritance and odd procedures JavaScript requires to make it work. Because of this, it not only allows people to get programming in object oriented JavaScript quicker, but it makes it faster to program because you can let the toolkit handle all of the odd procedures JavaScript requires to make it work. It all begins with a simple dojo.declare() function.
Classes in Dojo are declared with a declare statement and assigning it a Class Name.Within the body can be variables, methods and constructors (know in Dojo as an initializer).
dojo.declare("ClassName",null, {//class body
});
(Note: ClassName is the basic name, but to avoid naming conflicts, use package names like my.class.ClassName. For simplicity sake, we will start out with using just the simple name.)
Let's add some more content to our class by giving it a name and showing what the initilizer can do.Following is a persons class with an initializer and a moveToNewCity() function:
dojo.declare("Person", null, {
//acts like a java constructor
initializer: function(name, age, currentResidence){
this.name=name;
this.age=age;
this.currentResidence=currentResidence;
},
moveToNewCity: function(newState)
{
this.currentResidence=newState;
}
});
To create an object of this class you use the new keyword:
//create an instance of a new person
var matt= new Person('Matt', 25, 'New Mexico');
The initializer function is called once the object is created and the arguments are passed to it initializing the object.Our Matt object who is 25 currently lives in New Mexico, but let's say he moves a little further west to California.We can set his new currentResidence with the Person class method moveToNewCity(): matt.moveToNewCity('California');
Now the current value of matt.currentResidence shows that he now lives in California.
A person can only do so much, so let's create an Employee class that extends the Person class.The second argument in the dojo.declare() function is for extending subclasses.
dojo.declare("Employee",Person, {
//acts like a constructor
initializer:function(name, age, currentResidence, position)
{
Employee.superclass.initializer(name, age, currentResidence);
this.password="";
this.position=position;
},
login: function()
{
if(this.password!="" && this.password!=null){
alert('you have successfully loged in with the password '+this.password);
}
else
{
alert('please ask the administrator for your password');
}
}});
The first line in the initializer calls Employee.superclass.initializer, the Person class constructor. Dojo handles all of the requirements for setting up the inheritance chain.Methods or variables can be overridden by setting the name to the same as it is in the parent class. The Employee class can override the Person class moveToNewCity(), perhaps by letting the company pay for moving expenses.
You initialize the sub class the same as the Person class with the new keyword.
var kathryn=new Employee(' Kathryn ', 26, 'Minnesota', 'Designer');
The Employee class passes the first three arguments down to the Person class, and sets the position.Kathryn has access to the login() function found in the Employee class, and also the moveToNewCity() function by calling kathryn.moveToNewCity(‘Texas’); Matt on the other hand, does not have access to the Employee login() function.
matt.login() // ERROR can’t log in because he is not an Employee
If your class contains arrays or other complex objects, they should be declared in the initializer, due to some subtleties of object inheritance in javascript. Note that simple types (strings, numbers) are fine to declare in the class directly.
dojo.declare("my.classes.bar", my.classes.foo, {
// coupledObjects: [1, 2, 3, 4] - doesn't do what I want;
// ends up being like a static!!
numItem : 5, // one per bar
strItem : "string", // one per bar
initializer: function() {
this.coupledObjects = [ ]; // each bar should have it's own array
this.expensiveResource = new expensiveResource(); // one per bar
}
});
On the other hand, if you want an object or array to be static (shared between all instances of my.classes.bar), then you should do something like this:
dojo.declare("my.classes.bar", my.classes.foo, {
initializer: function() {
dojo.debug("this is bar object # " + this.statics.counter++);
},
statics: { counter: 0, somethingElse: "hello" }
});
The example below inherits from my.classes.foo and then mixes in "my.mixin". This is similar to multiple inheritance but there are some subtle differences, namely that this.inherited can only reference my.classes.foo, not my.mixin.
dojo.declare("my.classes.bar", [my.classes.foo, my.mixin], { initializer: function() { my.mixin.call(this /*, args*/); // invoke some mixin constructor // (note: my.mixin.prototype is ignored) }, valueForPrototype: 3, methodForPrototype: function() { } });
However, there is an issue with respect to constructor code in any simple JavaScript inheritence system. The constructor function of an object must be executed to create a prototype object for an inheritor. Therefore the constructor function must serve as both prototype-initializer and instance-initializer. The double duty of inherited constructors can be non-obvious and lead to subtle bugs.
Given the constructor above, when a bar object is created to use as a prototype, the mixin properties and properties created in the constructor become members of the inherited object's prototype. Almost always these properties are not intended to be part of a prototype.
As a practical matter, the extra prototypical properties are usually ignored as matching instance properties are created at object-instantiation time. However, for example, having an extra expensiveResource can be costly. And errors can result if the environment is not ready to create an expensiveResource at inherits-time. Errors caused by these conditions can be hard to track down, especially if the developer is not aware of how constructors are used when inheriting prototypes.
Separating instance-initializer tasks from prototype-initializer tasks eliminates these concerns. Therefore dojo.declare creates a standard, controlled constructor and separates instance-initialization tasks into a separate, optional initializer method.
Note: dojo.declare cannot inherit from an object that has a non-trivial constructor because dojo.declare does not allow constructors to also perform instance initialization.However, you can inherit from a dojo.declare created constructor without restriction
Sometimes one wants to invoke a method on an object from an ancestor prototype. JavaScript allows any function to call any other function in any context via the call and apply built-ins. So there are techniques like:
my.classes.bar.prototype.someMethod == function() {
// invoke any function in our context
anyFunction.call(this);
// invoke inherited version of this method in our context
my.classes.foo.someMethod.apply(this, arguments);
}
(Note: in these examples, the == indicates an assertion that the named property is equivalent to the function shown. Actual assignment is done via dojo.lang.extend or dojo.declare.)
As a convenience, dojo.inherits puts a reference to the ancestor prototype into the descendent constructor, and a reference to the descendent constructor into the descendent prototype. These extra references allow a great deal of extra flexibility in general, and also allow calling ancestor methods without explicitly naming the ancestor:
// invoke inherited version of this method in our context this.constructor.superclass.someMethod.apply(this, arguments);
However, the above technique will cause an infinte loop if someMethod is once removed. E.g., if we have foo -> bar -> zot, you can run into an issue like this:
The error results because this.constructor.superclass referenced in bar's identify function refers to zot's superclass (causing bar.identify to call itself).my.classes.foo.prototype.identify == function() { return "I'm a foo"; }
my.classes.bar.prototype.identify == function() { return "I'm a bar and " + this.constructor.superclass.identify.apply(this, arguments); }
my.classes.zot.prototype.identify == function() { return "I'm a zot and " + this.constructor.superclass.identify.apply(this, arguments); } bar = new my.classes.bar(); alert(bar.identify()); // "I'm a bar and I'm a foo" zot = new my.classes.zot(); alert(zot.identify()); // stack overflow
To resolve these issues, objects created from dojo.declare constructors include a function called inherited that safely invokes an ancestor method.
inherited: function(methodName /*string*/, arguments /* arrayLike */)
inherited correctly handles the problem scenario above:
my.classes.foo.prototype.identify == function() { return "I'm a foo"; } my.classes.bar.prototype.identify == function() { return "I'm a bar and " + this.inherited('identify', arguments); } my.classes.zot.prototype.identify == function() { return "I'm a zot and " + this.inherited('identify', arguments); } bar = new my.classes.bar(); alert(bar.identify()); // "I'm a bar and I'm a foo" zot = new my.classes.zot(); alert(zot.identify()); // "I'm a zot and I'm a bar and I'm a foo"
The syntax is:
ordojo.declare(className /*string */, superClass /*function*/ [, initializer /* function*/]);
dojo.declare(className /*string */, [superClass /*function*/, mixin /* function */, ...] [, initializer /* function*/]);
Including the target className in the argument list allows the object path to be created automatically (i.e. intermediate namespaces are created as needed). Also, dojo.declare stores className in an eponymous property in the created object's prototype (e.g. my.classes.foo.prototype.className == "my.classes.foo").
Technically speaking, JavaScript does not have classes: object construction is based on prototypes. For this reason reference to the term class has been (mostly) avoided above.
It seems that the difference is not of great practical importance. It's true that constructor functions in JavaScript are actual objects, but they operate like classes in the sense that they generally have no other purpose than as a mold for object instantiation. It is noted that classes are typically compile-time (or at least meta-) constructs and JavaScript constructors exist as Objects at runtime and contain actual data.
The name dojo.declare was chosen after much debate. The name is vague but easy to remember, read, and type. The method name inherited is lifted (at least) from ObjectPascal.ÂÂ
In general, a script should do the following in the ... section:
<!-- Step 1 (Optional) Set djConfig -->
<SCRIPT type=text/javascript>
djConfig = {
debug: true
};
</SCRIPT>
<!-- Step 2: Load dojo -->
<SCRIPT src="js/dojo/dojo.js" type=text/javascript></SCRIPT>
<!-- Step 3: call dojo.require -->
<SCRIPT>
dojo.require("dojo.book.myWidget.*");
<!-- Step 4 (Optional): define initialization functions -->
function initMyStuff() {
...
}
dojo.addOnLoad(initMyStuff);
</SCRIPT>
The order is important! If you do the steps out of order, dojo may not initialize properly, and your page will be a mess.
This script element is responsible for loading the base Dojo script that provides access to all the other Dojo functionality. Following this we add the requires statements which pulls in functionality needed by the application.
Use the dojo.addOnLoad to call functions which use the widget ids because Dojo must completely load the page and finish parsing the HTML before a reference can be made to the id. So, for example, the following will not work:
<BUTTON widgetId="helloButton" dojoType="Button">Hello World!</BUTTON>
<SCRIPT>
// ILLEGAL!! helloButton does not exist yet
dojo.byId("helloButton").width2height = 0.5;
</SCRIPT>
Instead, place the script in an initialization function:
<SCRIPT src="js/dojo/dojo.js" type=text/javascript></SCRIPT>
<SCRIPT>
function initMyStuff() {
dojo.byId("helloButton").width2height = 0.5;
}
dojo.addOnLoad(initMyStuff);
</SCRIPT>
<BUTTON widgetId="helloButton" dojoType="Button">Hello World!</BUTTON>
Dojo defines a global object called "dojo" which serves as an umbrella for everything Dojo-related. It simulates a namespace and was created to prevent clashes in the global JavaScript? namespace between the code in Dojo and other toolkits or user supplied code. Unfortunately it cannot be used during a bootstrap process, so special global variables should be used. All of them are prefixed with "dj".
You will need to use exactly two top-level Dojo-defined objects: "dojo", which serves as a namespace, and "djConfig", which is used to supply initialization parameters to Dojo, and should be created before Dojo's bootstrap.
For example, to turn off global widget searching, add these lines just *before* you include dojo.js:<script type="text/javascript">
djConfig = {
parseWidgets: false
};
</script>
A common use case for DHTML/ajax is to fetch a fragment of html using XHR or some other way, and change the innerHTML of a div with that content. Problem with this is that it doesn't instanciate widgets and doesn't fire scripts. ContentPane was created to make widgets and scripts work and reduce the potential for memory leaks. ContentPane is a base widget for many (Html)widgets, it handles remote loading as well as local setting of content and instanciating widgets in that content. Think of it as islands in your page that can easily switch content using setContent() or setUrl().
Many other widgets inherits ContentPane, like Tooltip, Dialog, FloatingPane etc. That means that all the methods and properties of ContentPane also applies to them.
ContentPane is often used as children of Layout widgets like LayoutContainer, TabContainer, AccordionContainer
Dont misstake it for a Iframe though, It should not be used on very large html fragments.
Simple usage ... <div id="cpane" dojoType="contentPane" href="initialContent.html"><div> <a href="javascript:dojo.widget.byId('nextContent.html')">Goto nextPage</a> ...
Basic options
Methods apart form those provided by ContentPane's superclass HtmlWidget
Methods (Intended as event hooks using dojo.event.connect)
In order to prevent the default messages you can do something like this:
<script>
var myLoadMessage = {
show: function(event){
event.preventDefault();
... custom code here
},
hide: function(){...}
}
dojo.addOnLoad(function(){
var pane = dojo.widget.byId('myPaneId');
dojo.event.connect(pane, "onDownloadStart", myLoadmessage, "show");
});
</script>
<div dojoType="ContentPane" id="myPaneId">...startcontent...</div>
or
<div dojoType="ContentPane"
onDownloadStart="myLoadMessage.show(arguments[0]);">...startcontent...</div>
When used as a child to TabContainer, AccordionContainer or PageContainer TabContainer, AccordionContainer or PageContainer extends Widgets with these extra options
When used as a child of LayoutContainer LayoutContainer package extends Widgets with this option
FAQ
<script>
var i = 0;
function addToI( j ){
i = i + j;
return i;
}
</script>
becomes (from window scope):
(function( ){
var i = 0;
function addtoI( j ){
i = i + j;
return i;
}
})
<script>
i = 0; // note lack of var
addToI = function( j ){ // we could also do window.addToI = function( j ){ ...
i = i + j;
return i;
}
</script>
becomes (from window scope)
i = 0;
function addtoI( j ){
i = i + j;
return i;
}
<script>
this.i = 0;
this.addToI = function( j ){
this.i = this.i + j;
return this.i;
}
</script>
becomes (from window scope)
(function(){
this.i = 0; // now it is a property of this function and can be reached from the outside.
this.addToI = function( j ){
this.i = this.i + j;
return this.i;
}
})
var added = dojo.widget.byId('myPaneId').scriptScope.addToI( 10 );
dojo.debug(added) // prints 10
added = dojo.widget.byId('myPaneId').scriptScope.addToI( 10 );
dojo.debug(added); // prints 20
and so on...
Now lets say we have html that would like to alert the value of i (this.i) in plain html that would be:
<button onclick="alert(i);">Tell me i !</button> // As explained above this wont work in ContentPane(unless you set it to global by omitting var).Now if we now the ID of the contentPane that pulls in this content we could do:
<button onclick="alert(dojo.widget.byId('myPaneId').scriptScope.i);">Tell me i !</button>
// this will work in ContentPane
That is'nt very useful as we don't always know the ID of the contentPane that pulls in the html when we write the content.
<button onclick=" scriptScope.i">Tell me i !</button> // this will work in ContentPane // NOTE: Due to a bug in ContentPane 0.3.1 you need to add a extra space before the keyword // Thank you Sasha Firsov for finding that!The parent scope of scriptScope is window, the reason for that is to avoid messing with widget internals. Just imagine the disaster a redefinition of setUrl function would cause otherwise.
... some content <div dojoType="DatePicker" id="myPicker"></div> ... rest of contentA content script could look like:
<script>
var o = {
storeDate: function( ){
var datePick = dojo.widget.byId('myPicker');
var date = datePick.storedDate;
// save date somewhere
}
};
_container_.addOnLoad(function(){
var picker = dojo.widget.byId('myPicker');
dojo.event.connect(picker, "onSetDate", o, "storeDate");
});
// remember to disconnect onUnLoad, very important!!
_container_.addOnUnLoad(function(){
var picker = dojo.widget.byId('myPicker');
dojo.event.disconnect(picker, "onSetDate", o, "storeDate");
});
</script>
<html>
<head>
<script>
var djConfig = {isDebug: true};
</script>
<script src="dojo/dojo.js"></script>
<script>
var scriptScope = this;
if(typeof _container_ == 'undefined'){
var _container_ = dojo;
}
_container_.addOnLoad(function(){
dojo.debug("Successfully loaded!");
});
this.doWhenClicked = function(txt){
dojo.debug(txt);
}
</script>
<body>
<a href="javascript:scriptScope.doWhenClicked('You clicked a link!');">Click here!</a>;
</body>
</html>
*********Your mainpage***********
<html>
<head>
<script src="dojo/dojo.js"></script>
<script>
dojo.require("dojo.widget.ContentPane");
dojo.require("dojo.widget.LayoutContainer");
function changeUrlInClient(url){
var client = dojo.widget.byId("client");
client.setUrl(url);
}
</script>
</head>
<body>
<div dojoType="LayoutContainer" layoutChildPriority='none' style="border: 1px solid blue; width: 800px; height: 300px;">
<div dojoType="ContentPane" layoutAlign="left" style="width: 200px;" executeScripts="true" href="linkpage.html"></div>
<div widgetId="client" dojoType="ContentPane" layoutAlign="client" style="border:1px solid red;"></div>
</div>
</body>
</html>
*******linkpage.html************
<html>
<head>
<script>
var o = {
listen: function(evt){
// if the onclick came from a
</head>
<body>
<a href="content1.html">content1</a>
<a href="content2.html">content2</a>
<a href="content3.html">content3</a>
<a href="content4.html">content4</a>
</body>
</html>
**********mainpage************
<html>
<head>
<script src="dojo/dojo.js"></script>
<script>
dojo.require("dojo.widget.FloatingPane");
dojo.require("dojo.widget.Button");
</script>
</head>
<body>
<div dojoType="FloatingPane"
title="Login example"
style="width: 300px; height: 300px;"
executeScripts="true"
cacheContent="false"
href="login.php">
</div>
</body>
</html>
*********login.php**********
<?php
session_start();
// are we trying to login?
if(isset($_GET["login"])){
// this could of be a database instead
$users = array(
"JohnDoe"=>
array("pass"=>"foo", "id"=>1),
"JaneDoe"=>
array("pass"=>"bar", "id"=>2),
"JuniorDoe"=>
array("pass"=>"baz", "id"=>3)
);
if(isset($_POST["user"]) && isset($_POST["pass"])){
$pass = $_POST["pass"];
$user = $_POST["user"];
if(isset($users[$user]) && ($users[$user]["pass"] == $pass)){
$_SESSION["id"] = $users[$user]["id"];
exit("(true);");
}
}
//if we get here we have failed to login
exit("(false);");
}
// logout?
if(isset($_GET["logout"])){
unset($_SESSION["id"]);
}
if(isset($_SESSION["id"])){
// it is safe to show secret content
?>
<script type="text/javascript">
this.logout = function(){
_container_.setUrl("login.php?logout=true");
}
</script>
<h3>You have successfully logged in!</h3>
showing secret content here
<a href="#" onclick="scriptScope.logout();">log out</a>
<?php
}else{
//no it wasnt safe, show our login script
?>
<script type="text/javascript">
this.ok = function(){
_container_.domNode.style.cursor = "wait";
dojo.io.bind({
formNode: dojo.byId("login"),
mimetype: "text/javascript",
handler: function(type, data){dojo.debug(data);
_container_.domNode.style.cursor = "";
if(type=="load"){
if(data){
_container_.setUrl("login.php");
}else{
dojo.byId("message").innerHTML = "Wrong username or password";
}
}else{
dojo.byId("message").innerHTML = "An error occured while login, please try again.";
}
}
});
}
this.quit = function(){
_container_.hide();
}
</script>
<form name="login" id="login" method="post" action="login.php?login=true">
<div id="message" style="text-align:center; color: red;">You need to login</div>
<label for="user">Username:
<input type="text" name="user"/>
<label for="pass">Password:</label>
<input type="password" name="pass"/>
<button dojoType="Button" onClick="scriptScope.ok();"/>login</button>
<button dojoType="Button" onClick="scriptScope.quit();">quit</button>
</form>
<?php
}
?>
<html>
<head>
<script src="dojo/dojo.js"></script>
<script>
dojo.require("dojo.widget.ContentPane");
</script>
</head>
<body>
<div dojoType="ContentPane" >
<script>
// this script will fire before dojo makes our parent a ContentPane widget, so it wont be
// affected by executeScripts at all.
// i wont have a _container_ variable and scriptScope wont hide any variables
// it will work just as a inlne javascript block always has
alert("This alert will fire event if you have executeScripts=false");
</script>
</div>
</body>
</html>
Like HTML buttons, the dojo button sizes to fit its content. Usually, you will provide an onclick="..." attribute to specify what happens when the button is pressed.
This needs to be rewritten for 0.9
By default, dojo uses a blue gradient background. But you can provide your own. You will need to create three .gif images: one for the left, one for the right, and one for the center. The filenames must end with l, r, or c, respectively. You can specify image sets for four different conditions:
For example, you can use these files:
as the disabled image of your button like this:
<button dojoType="Button" disabledImg="/images/buttons/disabled" > Quit </button>
API Reference: dojo.widget.Button
See Also: DropDownButton, comboButton
A combination Button and DropDownButton. Use this for a button that has a common action (e.g. "Make Regular Dinner") and less common related actions (e.g. "Make Romantic Dinner" and "Make TV Dinner")
<button dojoType="comboButton" menuId='saveMenu'> <img src="images/editIcon.gif" width="32" height="32"> Save </button> <div dojoType="PopupMenu2" id="editMenu" toggle="wipe"> <div dojoType="MenuItem2" iconSrc="images/save.gif" caption="Save" accelKey="Ctrl+S" onclick="mySave();" /> <div dojoType="MenuItem2" iconSrc="images/saveAs.gif" caption="Save As...." accelKey="Ctrl+A" onclick="mySaveAs();" /> </div>
You can also specify your own background images, as in Button.
API Reference: dojo.widget.ComboButton
See Also: Button, DropDownButton, PopupMenu2, MenuItem2
Editor2 Widget in dojo provides a WYSIWYG editor for HTML content. The core is compact and lightweight, while a plugin framework ensures that any functionality can be achieved by plugins.
Basic html editing capacity is implemented in the core, which is the RichText widget. Currently keyboard shortcuts are also hardcoded in this widget (TODO