论坛首页 Web前端技术论坛

dojo 入门资料

浏览 4411 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2009-02-15   最后修改:2009-04-01

    The Dojo Toolkit is an open-source JavaScript toolkit for building great web applications.

 

First Step

    Start by making a index.html file for use as a basic template for any example:

 

	<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" 
	"http://www.w3.org/TR/html4/strict.dtd">
	<html>
		<head>
			<title>Dojo Toolkit Test Page</title>    
			<style type="text/css">
				/* our CSS can go here */    
			</style>    

			<!-- load the dojo toolkit base -->
			<script type="text/javascript" src="dojoroot/dojo/dojo.js"
	    			djConfig="parseOnLoad:true, isDebug:true"></script>

			<script type="text/javascript">
				/* our JavaScript will go here */
			</script>
		</head>
		<body>
			<!-- this is a Typical WebPage starting point ... -->
			<h1 id="testHeading">Dojo Skeleton Page</h1>
			<div id="contentNode">
				<p>Some Content To Replace</p>
			</div>
		</body>
	</html>

 

Configuring Dojo

 

	<script type="text/javascript" src="dojoroot/dojo/dojo.js"
		djConfig="parseOnLoad:true, isDebug:true" />

 

How to start

    dojo.addOnLoad is a fundamental aspect of using dojo, and is very important to remember. Without it, you cannot be sure all the necessary content has been loaded before you own code begins to execute.

 

More Than Just Dojo

    Dojo has a package system built-in to load all the code you need, and is controlled by dojo.require(). This function allows us to pull in parts of the Dojo Toolkit not provided for in the Base dojo.js, such as Drag and Drop, additional animations, Dijit widgets, DojoX projects, or even your own code.

 

    For example, to load the code needed to use the TitlePane widget, and a dijit Button into your page include the modules dijit.TitlePane and dijit.form.Button:

 

	dojo.require("dijit.form.Button");
	dojo.require("dijit.TitlePane");
	dojo.addOnLoad(function()
	{
    		dojo.byId("testHeading").innerHTML = "We're on our way!";
    		console.log("onLoad fires after require() is done"); 
	});  

 

    Each "module" has its own dojo.require()'s, and knows not to load code it already has. Code executed by dojo.addOnLoad doesn't run until after your dojo.require()'s are all finished loading, making it that much safer and more convenient to use.

 

DOM Magic

    A really nice tool Dojo provides is dojo.query. It's a great way to parse all or portions of the Document Object Model (DOM) and access selections of nodes. It really deserves its own book. Each of the following sections will touch on how to use dojo.query more closely, though grasping its potential is as simple as seeing it used:

 

	dojo.require("dojo.NodeList-fx");
	dojo.addOnLoad(function()
	{
		// our dom is ready, get the node:
		dojo.query("#testHeading")
		 // add "testClass" to its class="" attribute
		.addClass("testClass")
		 // and fade it out after 500 ms
		.fadeOut({ delay:500 }).play();
	});
 

    dojo.query returns an instance of a dojo.NodeList, a synthetic super Array of domNodes. It supports most CSS3 selectors and executes code against the whole list of results. To demonstrate this, we're going to need something more than a single heading, so add some content to our DOM:

 

	<h1 id="testHeading">Dojo Skeleton Page</h1>
	<a class="link" href="#">First link</a>
	<a class="link" href="#">Second Link</a>    
	<p class="para">First paragraph</p>
	<p class="para">Second paragraph</p>
	<p class="para">Third paragraph</p>

	and use a different query:

	dojo.require("dojo.NodeList-fx");
	dojo.addOnLoad(function()
	{
		// get each element with class="para"
		dojo.query(".para")
		.addClass("testClass")
		.fadeOut({ delay: 1000 }).play();
	});

 

     All three <p> elements should turn red and fade out after a second delay. The full list of things dojo.NodeList does is impressive, some of which we'll touch on in later sections of this guide.


Events

    The next important concept we are going to cover is interacting with our page. We've already set the heading to some alternate text, but what if we wanted to do something more interesting? Perhaps change it to something else when the user clicks on it? dojo.connect is the one-stop solution for all your event needs:

 

	dojo.addOnLoad(function()
	{
    		var node = dojo.byId("testHeading");
    		dojo.connect(node,"onclick",function(){
			node.innerHTML = "I've been clicked";    	
    		});	    
	});

 

     A convenient way to do the above using dojo.query would be:

 

	dojo.addOnLoad(function(){
    		dojo.query("#testHeading")
		.style("cursor","pointer")
		.connect("onclick",function(){
	    		this.innerHTML = "I've been clicked";    	
		});	    
	});
 

    We can also connect to methods of specific objects and execute them in the same scope. This is useful as you get into declaring classes in Dijit, or creating Animations. Let's create a really simple object with some methods and watch them interact:

 

	var mineObj = {
		aMethod: function(){ 
		console.log('running A');   
		}, 
	bMethod: function(){ 
		console.log('running B'); 
		}    
	}; 
	var otherObj = { 
		cMethod: function(){ 
		console.log('running C');            
		} 
	}; 
	dojo.addOnLoad(function()
	{ 
		// run bMethod() whenever aMethod() gets run 
		dojo.connect(mineObj,"aMethod",mineObj,"bMethod"); 

		// run an entirely different object's method via a separate connection 
		dojo.connect(mineObj,"bMethod",otherObj,"cMethod"); 

		// start chain of events
		mineObj.aMethod(); 
	});

 

     You should see "running A B C" on separate lines in the console.

 

Some examples:

 

       //When obj.onchange(), do ui.update():
	dojo.connect(obj, "onchange", ui, "update");
	dojo.connect(obj, "onchange", ui, ui.update); // same

	//Using return value for disconnect:
	var link = dojo.connect(obj, "onchange", ui, "update");
	...
	dojo.disconnect(link);

	//When onglobalevent executes, watcher.handler is invoked:
	dojo.connect(null, "onglobalevent", watcher, "handler");

	//When ob.onCustomEvent executes, customEventHandler is invoked:
	dojo.connect(ob, "onCustomEvent", null, "customEventHandler");
	dojo.connect(ob, "onCustomEvent", "customEventHandler"); // same

	//When ob.onCustomEvent executes, customEventHandler is invoked with the same scope (this):
	dojo.connect(ob, "onCustomEvent", null, customEventHandler);
	dojo.connect(ob, "onCustomEvent", customEventHandler); // same

	//When globalEvent executes, globalHandler is invoked with the same scope (this):
	dojo.connect(null, "globalEvent", null, globalHandler);
	dojo.connect("globalEvent", globalHandler); // same
 

Some gloss: dojo animations

     Dojo has a powerful animation system, with several pre-made animations for a lot of common use cases. Adding some visual flair to your projects has never been easier, and typically makes the user experience a lot more interesting.

 

    All animations use a single "magic object" as its only parameter. The most important being the node: attribute, a DOM Node on which to apply our animation. Some parameters are optional, and some are for advanced usage. A common setup would look something similar to:

 

	dojo.addOnLoad(function()
	{ 
    		var animArgs = {
		node: "testHeading",
		duration: 1000, // ms to run animation
		delay: 250 // ms to stall before playing
    		};
    		dojo.fadeOut(animArgs).play();
	});

 

    All Animation functions in Base Dojo return a new instance of a dojo._Animation, an object with several common methods and events used for control. The methods used have common names like play(), stop(), pause(), and gotoPercent(). As seen above, we've create a fadeOut Animation and called play() on the returned dojo._Animation, starting the action immediately after creation.

 

Base Animations:

    Animations included in base dojo.js are: fadeIn, fadeOut, animateProperty, and a shorthand version of animateProperty simply called anim. dojo.animateProperty is very powerful, and is the foundation for most advanced animations and other animations in Dojo Core.

 

	dojo.addOnLoad(function()
	{
		var anim1 = dojo.fadeOut({ node: "testHeading", duration:700 });
		var anim2 = dojo.animateProperty({
			node: "testHeading", delay: 1000,
			properties:{
				// fade back in and make text bigger
				opacity: { end: 1 }, fontSize: { end:19, unit:"pt"}
			}
		}); 
		anim1.play();
		anim2.play();	
	});
 

    As seen, dojo.animateProperty will fade the element back in via its opacity property and simultaneously make the text larger. You can animate most any CSS property this way.

 

    dojo.anim is very similar to dojo.animateProperty in function, though differs in several important ways. Instead of using a "magic object" of parameters for all options, dojo.anim accepts ordered parameters. In the first position, a DOM Node or string ID of a DOM Node, acting as the node: parameter. The Second argument passed is an object identical to the properties:  object passed to animateProperty: A dictionary of properties and values to animate.

 

    The most notable difference between anim and animateProperty (and all other Base Animations) is that anim immediately calls play() on the returned Animation instance. This sacrifices a small amount of flexibility when doing advanced animations, but is very convenient for simple situations.

 

	dojo.addOnLoad(function()
	{
		// show it:
		dojo.anim("testHeading", { opacity:0 }, 700);
		dojo.anim("testHeading", {
			opacity: 1, fontSize: { end:19, unit:"pt" }
		}, null, null, 1000); 
	});
 

    The above sample recreates the previous animateProperty example using dojo.anim  The order of parameters passed to dojo.anim are: node, properties, duration, easing, onEnd, and delay. These match names of properties passed to animateProperty, and can be set to null to omit.

 

Additional FX:

    A lot can be done visually with the base animations, especially animateProperty. To keep the size of the base dojo.js down, all the additional animations and tools have been packaged into a single module: dojo.fx which can be optionally called in via dojo.require. Adding the module to your code provides several additional animation methods: dojo.fx.combine, dojo.fx.chain, dojo.fx.wipeIn, dojo.fx.wipeOut and dojo.fx.slideTo.

 

	dojo.require("dojo.fx");
	dojo.addOnLoad(function()
	{
		// slide the node to 75,75
		dojo.fx.slideTo({
			node:"testHeading",
			top:75, left:75
		}).play(); // and play it
	});
 

    dojo.fx.chain and dojo.fx.combine are very useful, too. They run animations in parallel or in sequence, and return a single instance of dojo._Animation to use:

 

	dojo.require("dojo.fx");
	dojo.addOnLoad(function()
	{
    		var anim = dojo.fadeOut({ node: "testHeading" });
    		var anim2 = dojo.fx.slideTo({ node: "testHeading", top:75, left:75 });
    		var result = dojo.fx.combine([anim,anim2]);
    		result.play();	    
	});
 

Animation Events:

    Each dojo._Animation has a series of "events" to tie into for more advanced usage. Going back to the one-stop-event-shop dojo.connect, we can connect to specific actions of the animation and do other things. The most common are onEnd and beforeBegin.

 

	dojo.addOnLoad(function()
	{
    		var anim = dojo.fadeOut({ node: "testHeading" });
    		dojo.connect(anim,"onEnd",function(){
			console.log(" the animation is done ");
    		});
    		dojo.connect(anim,"beforeBegin",function(){
			console.log(" the animation is about to start ");
    		});
    		anim.play();
	});
 

    These events are especially helpful when you want to do things like changing out content while a node is hidden and then fading it back in:

 

	dojo.addOnLoad(function()
	{
    		var anim = dojo.fadeOut({ node: "testHeading" });
    		dojo.connect(anim,"onEnd",function(){
			dojo.byId("testHeading").innerHTML = "replaced after fade!";
			dojo.fadeIn({ node:"testHeading" }).play();
    		});
    		anim.play();
	});
 

    Conveniently, you can pass the event functions as properties to the animation. Using dojo.connect to setup the functions gives us a lot more power, and is typically safer for advanced uses, but sometimes dojo.connect is unnecessary, and it's just lots easier to wrap it all in:

 

	dojo.addOnLoad(function()
	{
		var anim = dojo.fadeOut({
    			node: "testHeading",
    			onEnd: function(){
				dojo.byId("testHeading").innerHTML = "replaced ... ";
				dojo.fadeIn({ node: "testHeading" }).play();
    			}
		}).play();
	});
 

animateProperty:

    Probably the most powerful of the base animations, dojo.animateProperty allows us to easily animate multiple CSS properties simultaneously.

 

    Being a dojo._Animation, animateProperty uses the same arguments as other animations. With the additional object properties: we can define any style property of a node from start: to end:, and optionally using a unit: attribute.

 

    Manipulating our header element to use a new font color, size, and overall opacity is as easy as:

 

	dojo.addOnLoad(function()
	{
		var anim = dojo.animateProperty({
			node:"testHeading",
			duration:700,
			properties: {
				// javascript css names are camelCase (not hyphenated)
				fontSize: { start:12, end:22, unit:"pt" },
				opacity: { start:1, end:0.5 },
				color: { start: "#000", end:"#FFE" }
			},
			delay:100 // be careful of this trailing comma, it breaks IE.
		});
	anim.play();
	});
 

dojo.query Animations:

    Dojo provides another convenient module, dojo.NodeList-fx, which adds additional methods to dojo.query for the available dojo.fx animations. To enable these methods, simply add in the required module:

 

	dojo.require("dojo.NodeList-fx");
	dojo.addOnLoad(function()
	{
		dojo.query("#testHeading").fadeOut().play();
	});
 

    The above gives us the same effect as calling dojo.fadeOut directly, but dojo.query here makes an animation for each of the NodeList elements and combines them into a single dojo._Animation. This can be useful when you have groups of like nodes you want to easily affect (in this case, all the nodes with class="fadeNode").

 

	dojo.require("dojo.NodeList-fx");
	var fadeThem = function(){
		dojo.query(".fadeNode").fadeOut().play();
	}
	dojo.addOnLoad(function(){
		dojo.connect(dojo.byId("testHeading"),"onclick",fadeThem);
	});
 

    Unlike other dojo.query() chains, the NodeList-fx methods return an instance of dojo._Animation, thus preventing further chaining.

 

Animation easing:

    All Animations support an easing: property (or in the case of dojo.anim, the fourth parameter). This function adjusts the values of the animation as it progresses to provide enhanced control over the curve.

 

    Dojo provides a collection of easing functions in an optional module dojo.fx.easing. To use them, simply dojo.require() the module, and pass an easing: parameter to your animation:

 

	dojo.require("dojo.fx.easing");
	dojo.addOnLoad(function()
	{
		dojo.animateProperty({
			node:"testHeading",
			properties:{
				fontSize:72
			},
			easing: dojo.fx.easing.backOut
		}).play();
	});
 

    The function dojo.fx.easing.backOut will create the effect of over-shooting the end value very slightly, and revert back to the final value. The "Out" indicated the effect applies towards the end of the Animation. Alternately, dojo.fx.easing.backIn applies near the beginning of the animation. There are approximately 37 easing functions provided in the module, with a variety of effects.

 

Ajax: Simple Transports

    Ajax is an acronym for "Asynchronous JavaScript and XML", a technology employed to send and receive data on the fly. It can be used to update sections of a website from any number of remote sources and send data to the server and pass responses back and forth, all without ever refreshing the webpage.

 

    Having been versed on some essential Dojo methods, we'll move on to the bread and butter of Ajax: XMLHttpRequest (or XHR for short). Dojo has several XHR methods available using common HTTP verbs: POST, GET, PUT, and DELETE.

 

    To prepare, we need to create a file with some text to load in. Create a file named sample.txt  in your js/ folder with sample text:

 

	I am a <em>remote</em> file.
	We used Ajax to put this text in our page.
 

    And modify the index.html to have some basic markup and style:

	<style type="text/css">
		#container {
			border:1px dotted #b7b7b7;
			background:#ededed;
			width:75px;
			height:55px;
		}
	</style>
	<div id="container" class="box">
		<div id="content">
			I am some Inner Content.
			I am going to be replaced
		</div>
	</div>
 

Getting Data:

    The first stepping stone is dojo.xhrGet, which will return the contents of a GET call on a URL. The XHR methods share a lot of common parameters. Most important are the url:  (our destination) and handleAs: (how we handle what is coming back). When the data arrives, it will be passed to the load: function we define:

 

	var init = function()
	{
		var contentNode = dojo.byId("content");
		dojo.xhrGet({
			url: "js/sample.txt",
			handleAs: "text",
			load: function(data,args){
				// fade out the node we're modifying
				dojo.fadeOut({
					node: contentNode,
					onEnd: function(){
				  		// set the data, fade it back in
				  		contentNode.innerHTML = data; 
				  		dojo.fadeIn({node: contentNode}).play();    
					}
				}).play();
			},
			// if any error occurs, it goes here:
			error: function(error,args){
				console.warn("error!",error);
			}
		});
	}; 
	dojo.addOnLoad(init);
 

    You will notice we've combined techniques above. The content will fade out, be replaced by the received data, and fade back in using methods we've learned before. It was almost too easy.

 

    A single handle: argument can be used instead of load: and error:, handling both success and failure cases in a common function:

 

	var init = function()
	{
		dojo.xhrGet({
			url: "js/sample.txt",
			handleAs: "text",
			handle: function(data,args){
				if(typeof data == "error"){
					console.warn("error!");
					console.log(args);
				}else{
					// the fade can be plugged in here, too
					dojo.byId("content").innerHTML = data;
				}
			}
		});
	};
	dojo.addOnLoad(init);
 

    XHR has limitations. The big one is that url: is not cross-domain: you can't submit the request outside of the current host (eg: to url:"http://google.com"). It is a known limitation and a common mistake when getting excited about Ajax. Dojo provides alternatives like dojo.io.iframe and dojo.io.script for more advanced usage.

 

Sending Data:

    All Dojo XHR methods are bi-directional. The only difference is the method. Using dojo.xhrPost, we use the POST method, embedding the data in the request (as opposed to the query string as with dojo.xhrGet). The data can be set directly as an object passed to the content: parameter:

 

	dojo.addOnLoad(function()
	{
		dojo.xhrPost({
			url:"submit.html",
			content: {
				"key":"value",
				"foo":42,
				"bar": {
					"baz" :"value"    
				}
			},
			load: function(data,ioargs){
				console.log(data);
			}
		});
	});
 

    Or more commonly, conveniently converted from a form: parameter. First, make a simple unobtrusive form in the index.html:

 

	<form id="mainForm" action="sample.jsp" method="post">
		<label for="firstName">Name: </label>
		<input type="text" id="firstName" name="firstName" value="Enter Name" />
		<input type="submit" value="submit" />
	</form>
 

    Then, add in some JavaScript to submit the form by using dojo.connect to listen to the native onSubmit event and then post the contents of the form to an alternate URL:

 

	var formSubmit = function(e)
	{
		// prevent the form from actually submitting
		e.preventDefault(); 
		// submit the form in the background	
		dojo.xhrPost({
			url: "sample.jsp",
			form: "mainForm",
			handleAs: "text",
			handle: function(data,args){
				if(typeof data == "error"){
					console.warn("error!",args);
				}else{
					// show our response 
					console.log(data);
				}
			}
		});
	};
	dojo.addOnLoad(function()
	{
		var theForm = dojo.byId("mainForm");
		// another dojo.connect syntax: call a function directly	
		dojo.connect(theForm,"onsubmit",formSubmit);
	}); 
 

    An example sample.jsp would look like:

 

	<% response.setContentType("text/plain"); %>
	Hello <%= request.getParameter("firstName") %> , welcome to the world of Dojo!
 

Object Data:

    Getting text back from the server is nice, but the really great stuff comes when you start passing JavaScript objects around. Using a different handleAs: attribute, we can alter how Dojo handles the response data. Make a new file named sample.json to load:

 

	{
		foo: "bar",
		name: "SitePen",
		aFunction: function()
		{
			alert("internal function run");	    
		},
		nested:
		{
	    		sub: "element",
	    		another: "subelement"
		}
	}
 

    We'll target our xhrPost url: to the new file, and supply a handleAs: "json" parameter to convert the response data to an actual object we can use:

 

	var postData = function()
	{
		dojo.xhrPost({
			url: "sample.json",
			handleAs: "json",
			load: function(data,ioargs){
		  		// success: set heading, run function
		  		dojo.byId("testHeading").innerHTML += " by: "+data.name;
		  		if(data.aFunction && data.aFunction()){
		    			// we just ran data.aFunction(). should alert() ... 
		  		} 
			}
		});
	};
	dojo.addOnLoad(postData);
 

    This allows us to send literally any kind of data back and forth across the wire, without ever interrupting the user experience.

论坛首页 Web前端技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics