{"id":81,"date":"2010-06-09T16:38:45","date_gmt":"2010-06-09T08:38:45","guid":{"rendered":"http:\/\/www.octalforty.com\/?p=81"},"modified":"2023-12-08T11:23:07","modified_gmt":"2023-12-08T03:23:07","slug":"renaming-jquery-ui-tabs-with-validation","status":"publish","type":"post","link":"https:\/\/octalforty.com\/articles\/renaming-jquery-ui-tabs-with-validation\/","title":{"rendered":"Renaming jQuery UI Tabs With Validation"},"content":{"rendered":"
Recently, one of our projects requires me to use jQuery UI Tabs for its user interface. One of the problem was, to figure a way for the user to rename the tab and validate the input at the same time. Most of the site’s client-side validation is through jQuery Validate plugin, so that should be straight-forward.<\/p>\n
Want to see the it in action? Why not play around with the online demo<\/a> or download the source files<\/a>.<\/p>\n First off, let’s start by including jQuery, jQuery UI and jQuery livequery plugin. For this demo, I’ll point the files to Google CDN whenever possible, and start with the jQuery UI Tabs default functionality demo<\/a> markup.<\/p>\n We’ll be including most of the scripts inside the We’ll also limit the ability to rename a tab to just the currently selected tab<\/strong> only.<\/p>\n For this, we’ll be taking advantage of LiveQuery to bind the click events to the selected tab’s span.<\/p>\n Now we need to create a function to handle the “click”, and inject the form. We’ll also save the current tab name in a variable, and bind the save and cancel buttons.<\/p>\n Now that we’ve the form shown to the user and properly bind them to an event, they could either save<\/em> it, or cancel<\/em> it by clicking the cancel button or anywhere within the page. We write a function to handle these request and handle them properly. I’ve also included a check if the name is empty, it will be named as “Untitled” instead. But this could be removed based on your validation criteria later on.<\/p>\n If you’re already familiar with the Validation plugin<\/a>, this should be easy. If not, I suggest you to go through the documentation<\/a> quickly.<\/p>\n Easy eh? Now all we have to do is revisit our renameTab function and include the call for validation, on line 13 and 34 – 37 as per below:<\/p>\nThe markup<\/h3>\n
\n
<!DOCTYPE html PUBLIC \"-\/\/W3C\/\/DTD XHTML 1.0 Strict\/\/EN\" \"http:\/\/www.w3.org\/TR\/xhtml1\/DTD\/xhtml1-strict.dtd\">\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"UTF-8\" \/>\n\t\t<title>jQuery UI Tabs - Default functionality<\/title>\n\t\t<script type=\"text\/javascript\" src=\"http:\/\/ajax.googleapis.com\/ajax\/libs\/jquery\/1.4.2\/jquery.min.js\"><\/script>\n\t\t<script type=\"text\/javascript\" src=\"http:\/\/ajax.googleapis.com\/ajax\/libs\/jqueryui\/1.8.2\/jquery-ui.min.js\"><\/script>\n\t\t<script type=\"text\/javascript\" src=\"js\/jquery.livequery.js\"><\/script>\n\t\t<script type=\"text\/javascript\" src=\"http:\/\/ajax.microsoft.com\/ajax\/jquery.validate\/1.7\/jquery.validate.min.js\"><\/script>\n\n\t\t<link type=\"text\/css\" href=\"http:\/\/ajax.googleapis.com\/ajax\/libs\/jqueryui\/1.8.1\/themes\/smoothness\/jquery-ui.css\" rel=\"stylesheet\" \/>\n\t<\/head>\n\t<body>\n\t\t<div id=\"demo\">\n\t\t\t<div id=\"tabs\">\n\t\t\t\t<ul id=\"tabs-nav\">\n\t\t\t\t\t<li><a href=\"#tabs-1\"><span>Nunc tincidunt<\/span><\/a><\/li>\n\t\t\t\t\t<li><a href=\"#tabs-2\"><span>Proin dolor<\/span><\/a><\/li>\n\t\t\t\t\t<li><a href=\"#tabs-3\"><span>Aenean lacinia<\/span><\/a><\/li>\n\t\t\t\t<\/ul>\n\t\t\t\t<div id=\"tabs-1\">\n\t\t\t\t<!-- Content for the first tab will be here -->\n\t\t\t\t<\/div>\n\t\t\t\t<div id=\"tabs-2\">\n\t\t\t\t<!-- Content for the second tab will be here -->\n\t\t\t\t<\/div>\n\t\t\t\t<div id=\"tabs-3\">\n\t\t\t\t<!-- Content for the third tab will be here -->\n\t\t\t\t<\/div>\n\t\t\t<\/div><!-- \/#tabs -->\n\t\t<\/div><!-- \/.demo -->\n\t\t<script type=\"text\/javascript\"><\/script>\n\t<\/body>\n<\/html><\/code>\n<\/pre>\n<\/div>\n
The JavaScript<\/h3>\n
<script><\/code> tag on line 32. We’ll split the whole interaction to few phases so that we can see clearly what we should do. The happy path should be something like:<\/p>\n
\n
Step 1: Click Tab Name To Edit.<\/h4>\n
\n
$(function() {\n\t\/\/ Initialize tabs with the default options\n\t$(\"#tabs\").tabs();\n\t\/\/ Bind \"click\" event to the span, so that it calls the renameTab function\n\t$(\"li.ui-tabs-selected span\").livequery(\"click\", function(){\n\t\trenameTab(this);\n\t});\n});<\/code>\n<\/pre>\n<\/div>\n
Step 2: Display input text with the tab name as default value<\/h4>\n
\n
function renameTab(obj) {\n\tvar obj = $(obj),\n\t\toldName = $(obj).html(),\n\t\teditMode = '<div class=\"editable\"><form id=\"rename_tab_form\"><input type=\"text\" id=\"new_tab_name\" value=\"' + oldName + '\" name=\"new_tab_name\" maxlength=\"20\" \/><button id=\"saveRename\" class=\"btn\"><span>Save<\/span><\/button><button id=\"cancelRename\" class=\"btn\"><span>Cancel<\/span><\/button><\/form><\/div>',\n\t\tform = $(\"#rename_tab_form\");\n\t\/\/ Inject the form after the span, and then remove the span from DOM\n\tobj.after(editMode).remove();\n\t\/\/We might not need this now, but it will be useful later when we add in validation\n\t$(\"div.editable\", \"#tabs-nav\").closest(\"a\").addClass(\"editing\");\n\t$(\"#new_tab_name\").bind(\"focus\", function() {\n\t\tthis.select();\n\t}).focus();\n\n\t$(\"#saveRename\").bind(\"click\", function() {\n\t\t$(\"#rename_tab_form\").submit();\n\t\treturn false;\n\t});\n\n\t$(\"#cancelRename\").bind(\"click\", function() {\n\t\treplaceName( false, this, oldName );\n\t\treturn false;\n\t});\n\t$(\"body\").bind(\"click\", function(e) {\n\t\tvar target = e.target.id;\n\t\tif ( target != 'cancelRename' ) {\n\t\t\treplaceName(false, $(\"#cancelRename\"), oldName);\n\t\t}\n\t\treturn false;\n\t});\n\n\t$(\"#rename_tab_form\").bind(\"submit\", function(e) {\n\t\treplaceName( true, $(\"#cancelRename\"), $(\"#new_tab_name\").val() );\n\t\te.preventDefault();\n\t\treturn false;\n\t});\n}<\/code>\n<\/pre>\n<\/div>\n
Save or cancel?<\/h4>\n
\n
\/\/ replaceName (boolean, object, value)\nfunction replaceName(action, obj, val) {\n\tvar name = '';\n\n\tif (action) { \/\/ If action is \"true\", we save the data\n\t\t\/\/ You could use the .ajax() method to save the data as you see fit\n\t} else {\n\t\tname = val;\n\t}\n\n\tif ( val == '' ) {\n\t\tval = 'Untitled';\n\t}\n\n\t$(obj).parents(\"div.editable\").after('<span>'+ val +'<\/span>').remove();\n\t$(\"li.ui-tabs-selected span\",\"#tabs-nav\").closest(\"a\").removeClass(\"editing\");\n\t$(\"#saveRename, #cancelRename, body\").unbind();\n\n}<\/code>\n<\/pre>\n<\/div>\n
Using the jQuery Validation plugin<\/h4>\n
\n
$(\"#rename_tab_form\").validate({\n\terrorClass : \"error validation\",\n\tsuccess : \"valid\",\n\trules : {\n\t\tnew_tab_name : {\n\t\t\trequired : true,\n\t\t\tmaxlength : 20,\n\t\t\tminlength: 3\n\t\t}\n\t},\n\terrorElement : \"div\",\n\terrorPlacement : function (error, element) {\n\t\tvar nextSibling = element.siblings(\"div.error\"),\n\t\tcounter = 1;\n\t\tnextSibling.remove().empty();\n\t\tvar hasErrorElement = nextSibling.val();\n\t\tif (hasErrorElement == null || hasErrorElement == '') {\n\t\t\terror.insertAfter(element).wrap(\"<div class='error-wrapper'><\/div>\").parent().append(\"<div class='error-pointer'><\/div>\");\n\t\t\t\/\/ Not sure why I'm getting multiple error elements, so we need to remove this manually\n\t\t\t$(\"div.error-wrapper\").eq(0).siblings(\"div.error-wrapper\").remove();\n\t\t\treturn false;\n\t\t}\n\t},\n\tunhighlight : function() {\n\t\t$(\"div.error-wrapper\").remove();\n\t},\n\tsubmitHandler: function(form) {\n\t\treturn false;\n\t}\n});<\/code>\n<\/pre>\n<\/div>\n
\n
function renameTab(obj) {\n\tvar obj = $(obj),\n\t\toldName = $(obj).html(),\n\t\teditMode = '<div class=\"editable\"><form id=\"rename_tab_form\"><input type=\"text\" id=\"new_tab_name\" value=\"' + oldName + '\" name=\"new_tab_name\" \/><button id=\"saveRename\" class=\"btn\"><span>Save<\/span><\/button><button id=\"cancelRename\" class=\"btn\"><span>Cancel<\/span><\/button><\/form><\/div>',\n\t\tform = $(\"#rename_tab_form\");\n\n\tobj.after(editMode).remove();\n\n\t$(\"div.editable\", \"#tabs-nav\").closest(\"a\").addClass(\"editing\");\n\n\t$(\"#new_tab_name\").bind(\"focus\", function() {\n\t\tthis.select();\n\t\tvalidate();\n\t}).focus();\n\n\t$(\"#saveRename\").bind(\"click\", function() {\n\t\t$(\"#rename_tab_form\").submit();\n\t\treturn false;\n\t});\n\n\t$(\"#cancelRename\").bind(\"click\", function() {\n\t\treplaceName( false, this, oldName );\n\t\treturn false;\n\t});\n\t$(\"body\").bind(\"click\", function(e) {\n\t\tvar target = e.target.id;\n\t\tif ( target != 'cancelRename' ) {\n\t\t\treplaceName(false, $(\"#cancelRename\"), oldName);\n\t\t}\n\t\treturn false;\n\t});\n\n\t$(\"#rename_tab_form\").bind(\"submit\", function(e) {\n\t\tvar isValid = $(\"#new_tab_name\").valid();\n\t\tif ( isValid ) {\n\t\t\treplaceName( true, $(\"#cancelRename\"), $(\"#new_tab_name\").val() );\n\t\t}\n\t\te.preventDefault();\n\t\treturn false;\n\t});\n}<\/code>\n<\/pre>\n<\/div>\n