Discovering jQuery’s data() function
by David Joly | Tuesday, March 23rd, 2010If you have programmed an Ajax web app, you know that manipulating the DOM from a callback function is easy. Manipulating data can be a little bit tricky though, because previously declared javascript variables are not available to the callback function. Apparently many developers, myself included, have stored data in the DOM using hidden elements in any way that seems best at the time. jQuery’s data has made this messy practice unnecessary.
Introduction
The example in this tutorial uses the Zend Framework hosted on an Apache virtual host. Please see the Zend Quickstart tutorial to configure this environment. It is assumed the reader has at least a basic understanding of the Zend Framework and jQuery. Also, please note that this code is for demonstration only. Don’t go using it in a project! The files used in this tutorial can be downloaded here.
For this tutorial, I will be walking through a set of javascript functions and jQuery to show you how jQuery data() can be used. We will be making a simple page that renders information about users, including their first and last name, their IDs, and their locations. We will also have a user browser and a form to add new users. For the sake of simplicity, no database is used in this project.
Make the Controller
Our controller will have two actions, index and adduser. The code is brutally simple here because we are pretty much not doing anything. The index action grabs an array of users from the model (in a real project this would use a database), and sends it to the view. The adduser action is simply taking the posted user data and posting it back to the client in json format.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | <?php /** * Used by Discovering jQuery's data() * 03/23/2010 * @author David Joly * @link www.zeletron.com/ */ class March232010Controller extends Zend_Controller_Action { public function init() { } public function indexAction() { $usersModel = new Application_Model_Users(); $users = $usersModel->getUsers(); $this->view->users = $users; } public function adduserAction() { //Disable View for Async Request $this->_helper->viewRenderer->setNoRender(); $this->_helper->layout->disableLayout(); //Response object to post back to client... $response = new stdClass(); if($this->getRequest()->isPost()){ $user = array( 'firstName' => $this->getRequest()->getParam('firstName'), 'lastName' => $this->getRequest()->getParam('lastName'), 'location' => $this->getRequest()->getParam('location'), 'userId' => $this->getRequest()->getParam('userId') ); $response->code = 1; $response->user = $user; } else { $response->code = 0; } echo json_encode($response); } } |
Write the XHTML
Here’s the xhtml in our view script. Please note that no data is rendered by a php loop construct, we are saving that for a javascript function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <h1>Discovering jQuery's data()</h1> <h3>Current Users</h3> <ul id="users"></ul> <h3>Add User Form</h3> <div> <label for="firstName">First Name:</label> <input val="" type="text" name="firstName" id="firstName" /> <label for="lastName">Last Name:</label> <input val="" type="text" name="lastName" id="lastName" /> <label for="location">Location:</label> <input val="" type="text" name="location" id="location" /> <button id="add-user">Add User</button> </div> <h3>User Browser</h3> <div id="user-browser"></div> <button id="previous">Previous</button> <button id="next">Next</button> |
Developing the Ajax App Client
Continuing in our view script,
22 23 24 25 26 27 28 29 | <script type="text/javascript"> //Rather than store our user data in a simple variable called "users", //we will attach our user data to the body tag. It is easily accessed //by both inline scripts and Ajax callback functions. $('body').data('users', <?php echo json_encode($this->users)?>); //Render Current Users... renderUsers(); |
We have just written our first and last bit of php in the view script that encodes our users array into an array of json user objects. On line 26 we bind this array of user objects to the body element (We can actually use any valid jQuery selector for this.). We then call a function to render our users to the DOM. Before getting to these functions, let’s add a few more necessary lines of code. Including using jQuery data() to track our current user (This will enable us to browse our users one at a time.).
31 32 33 34 35 36 37 38 39 40 | //To browse through our users we will need to track the currentUser also. $('body').data('currentUser', 0); //Show first user in browser... showUser(); //Bind functions to buttons... $('#previous').click(function(){previous();}); $('#next').click(function(){next();}); $('#add-user').click(function(){addUser();}); |
Now let’s get to the functions. Here we have functions to render our users:
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | function renderUser(user) { $('#users').append( '<li id="userId_' + user.userId + '">' +'First Name: ' + user.firstName + '<br />' +'Last Name: ' + user.lastName + '<br />' +'Location: ' + user.location + '<br />' +'</li>' ); } function renderUsers() { var users = $('body').data('users'); for(var i = 0; i < users.length; i++){ renderUser(users[i]); } } |
In the renderUsers() function we grab our user array using data(). Piece of cake eh?
Now let’s take a quick look at our browser functions. Notice how each function uses data(). If jQuery’s data() reminds you of globals, I would say I agree with you. We don’t need to pass arguments into these functions at all.
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | function showUser() { var users = $('body').data('users'); var currentUser = $('body').data('currentUser'); $('#user-browser').html( 'Viewing user ' + (currentUser + 1) + ' of ' + users.length + '<br />' +'UserId: ' + users[currentUser].userId + '<br />' +'First Name: ' + users[currentUser].firstName + '<br />' +'Last Name: ' + users[currentUser].lastName + '<br />' +'Location: ' + users[currentUser].location + '<br />' ); toggleNav(); } function previous() { var currentUser = $('body').data('currentUser'); $('body').data('currentUser', currentUser-1); showUser(); } function next() { var currentUser = $('body').data('currentUser'); $('body').data('currentUser', currentUser+1); showUser(); } function toggleNav() { //Disable Next Button if the current user is the last in the array if( $('body').data('currentUser') == $('body').data('users').length - 1){ $('#next').attr('disabled','disabled'); } else { $('#next').removeAttr('disabled'); } //Disable Previous Button if current user is the first in the array if($('body').data('currentUser') == 0){ $('#previous').attr('disabled','disabled'); } else { $('#previous').removeAttr('disabled'); } } |
Up to this point we haven’t done anything special that can benefit from data(). By rights, we could have used normal javascript variables and passed them around normally. Let’s write the addUser() function and you too will be hooked on jQuery data().
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | function addUser() { var users = $('body').data('users'); var userId = users.length; $.post('/march232010/adduser', { firstName : $('#firstName').val(), lastName : $('#lastName').val(), location : $('#location').val(), userId : userId }, function(r) { if(r.code == 1){ //Post was a success, add user to users array var users = $('body').data('users'); users.push(r.user); $('body').data('users',users); //Render new user renderUser(r.user); //Display new user in browser $('body').data('currentUser', parseInt(r.user.userId)); showUser(); } }, 'json' ); } </script> |
Notice how we both access and manipulate our users array using jQuery’s data(), pushing our newly added user onto the users array. Now, go use jQuery data() rather than storing your data in hidden DOM elements.