Posted in Dojo Cookies
Exploring URLs client side
URLs are one of the basic building blocks of the Web, and in this article we’re going to learn some techniques to explore them client-side using Dojo.
window.location
The window.location
object describes the URL of the current page, and has a number of attributes that Javascript code can examine to figure out where we are and how we got there. Lets see what the window.location
object has to say about the following URL:
https://www.example.com:8000/search?q=dojo&other=1#test
Property | Description | Example |
---|---|---|
hash | the part of the URL that follows the # symbol, including the # symbol. | #test |
host | the host name and port number. | www.example.com:8000 |
hostname | the host name (without the port number). | www.example.com |
href | the entire URL. | https://www.example.com:8000/search?q=dojo&other=1#test |
pathname | the path (relative to the host). | /search |
port | the port number of the URL. | 8000 |
protocol | the protocol of the URL. | https: |
search | the part of the URL that follows the ? symbol, including the ? symbol. | ?q=dojo&other=1 |
The Query String
Server-side technologies like PHP or Django often use HTTP GET parameters to perform specific actions. Lets take a Google search URL for the text “dojo”: https://www.google.co.nz/search?hl=en&q=dojo&btnG=Search&meta=
The GET parameters in that URL are q, hl, btnG, and meta, and they form the query string (?hl=en&q=dojo&btnG=Search&meta=
). Now, obviously the server does something useful with those parameters (like search for dojo) but we can also access them client-side via the dojo.queryToObject() function:
var queryParams = dojo.queryToObject(window.location.search.slice(1)); // we use slice(1) to strip the leading "?" // queryParams is now an object with a key for each GET parameter // eg. { q:"dojo", hl:"en", btnG="Search", meta:"" }
Once we have this function we can do some useful things, particularly with static HTML pages. Lets say we had a set of 3 tabs in a page. Rather than having a PHP script just to handle the menial task of setting the current tab, we can pick it up client-side from the query string.
See the first example.
So, url_1.html?tab=2
will select the third tab (with index 2)
dojo.addOnLoad(function() { // see if we have a ?tab=N parameter and set the current tab based on it if (window.location.search) { // convert our query string into an object (use slice to strip the leading "?") var queryParams = dojo.queryToObject(window.location.search.slice(1)); // get the tab index: parseInt will convert undefined/rubbish to NaN var tabIndex = parseInt(queryParams["tab"]); var tabStrip = dijit.byId("myTabs"); if (isNaN(tabIndex) || tabIndex >= tabStrip.getChildren().length) { // default to the first tab tabIndex = 0; } // set the current tab tabStrip.selectChild(tabStrip.getChildren()[tabIndex]); } });
We can then pass around URLs (Email, Twitter, IM, blogs) that point to the exact tab we want. And if we have links on the page that swap to a specific tab we can add a little bit of progressive enhancement…
// enhance our inter-tab links to prevent page reloading dojo.query("a.tabLink").connect("onclick", function(e) { // we store the target tab index in the "tag" attribute var targetIndex = parseInt(e.target.getAttribute("tag")); var newTab = dijit.byId("myTabs").getChildren()[targetIndex]; if (newTab) { // select the new tab dijit.byId("myTabs").selectChild(newTab); // cancel the event (prevent the browser from following the link) dojo.stopEvent(e); } });
See more <a class="tabLink" href="?tab=1" tag="1">exciting cookie flavours</a> now!
Each link will now not load the page if a user clicks it “normally”, but will still work how you intended if the link is opened in a new tab or window, or bookmarked for later.
See the second example.
What about making the browser URL bar copyable?
Normally to change the page to somewhere different we write to window.location (eg. ). But the
#hash
part of the URL is updatable by the client without reloading the page. So we can set it to #tabA
and #tabB
as the user changes what they’re looking at. Then if they copied it to the clipboard all we’d need to do is look at the hash attribute of the window.location
object in a very similar way to how we looked at the search attribute above:
dojo.addOnLoad(function() { // set the tab based on any URL at load-time if (window.location.hash) { var hash = window.location.hash.slice(1); // strip leading "#" var newTab = dijit.byId(hash) || dijit.byId("myTabs").getChildren(0); // default to 1st tab dijit.byId("myTabs").selectChild(newTab); } // update the URL hash each time the tab changes dojo.connect(dijit.byId("myTabs"), "selectChild", function(tab) { // update the URL hash to the current tab window.location.hash = "#" + dijit.byId("myTabs").selectedChildWidget.id; }); });
See the third example
Note that when the URL is changed via window.location.hash, an entry is added into the browser history. Unfortunately in this example we’re not checking for the user hitting “back” or “forward”, so it won’t select the right tab. For more information on handling the back button gracefully, see dojo.back.
dojo._Url
window.location
only applies to the URL of the current page. But hidden inside Dojo base is a URL handler that can do very similar stuff for any URL. Its not in the public API, but it hasn’t changed in a long while, and maybe it’ll become public soon. Basically you can instantiate it via new dojo._Url("https://example.com/foo/bar/?arg=123")
and it builds an _Url object you can explore to get the hostname, scheme, query string, path, etc.
var u = new dojo._Url("https://example.com/foo/bar/?arg=123");
Each of the attributes of the URL can be edited, and you can call toString()
to get a string URL back again. We can also use it to combine urls:
var u = new dojo._Url("https://example.com/foo/bar/", "../baz/101/"); u.toString() == "https://example.com/foo/baz/101/" var u = new dojo._Url("https://example.com/foo/bar/", "/fizz/202/pop.jpg"); u.toString() == "https://example.com/fizz/202/pop.jpg"
In addition, it’s used in the super-handy function dojo.moduleUrl()
:
// assume dojo is at /js/dojo/ var u = dojo.moduleUrl("my.catz", "images/lol/00fluffy.jpg"); u.toString() == "/js/my/catz/images/lol/00fluffy.jpg"
With this we can find the URL to any resource that’s in our Dojo package system (including cross-domain) - which is great for creating images or loading templates. In fact, Dijit’s templatePath
parameter uses dojo.moduleUrl()
to define how to find widget templates… see any widget declaration for an example.
Conclusions & Next Steps
We’ve figured out that URLs aren’t only for the server-side, we can explore them just as well client-side too. We explored the window.location
object which manipulates the current page URL, and learned some techniques for using GET parameters and #hash components for loading and navigating DHTML sites.
Then we moved onto dojo._Url
for exploring any URL, and dojo.moduleUrl()
for getting references to resources in our Javascript tree.
Earlier this month Kevin Dangoor from SitePen introduced Reinhardt: a Client-side Web Framework which includes URL dispatch and handling, utilising some of the ideas discussed above. Might be worth checking out.
As always, I’d appreciate your comments and feedback on the above!
Tags: dojo.moduleUrl