{"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

The markup<\/h3>\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

\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

We’ll be including most of the scripts inside the <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
  1. Click on the tab name to edit.<\/li>\n
  2. Display input text with the tab name filled in by default.<\/li>\n
  3. If the name is changed, save it. Otherwise, revert to the old name.<\/li>\n<\/ol>\n

    We’ll also limit the ability to rename a tab to just the currently selected tab<\/strong> only.<\/p>\n

    Step 1: Click Tab Name To Edit.<\/h4>\n

    For this, we’ll be taking advantage of LiveQuery to bind the click events to the selected tab’s span.<\/p>\n

    \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

    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

    \n
    \nfunction 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

    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

    \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

    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

    \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

    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>\n

    \n
    \nfunction 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

    And that’s it! To make the validation message appears a bit more nicer, we could apply the polygonal CSS<\/a> technique as mentioned by the fine guys in Filament, as per below :<\/p>\n

    \n
    \n#rename_tab_form { position: relative; }\n#rename_tab_form .error-wrapper { position: absolute; top: -37px; padding: 5px 10px; background: #9a0000; border-top: 1px solid #740000; border-bottom: 1px solid #740000; width: auto; }\n#rename_tab_form .error-wrapper .error { padding: 0; color: #fefefe; }\n#rename_tab_form .error-pointer { width:0; height:0; position: absolute; bottom: -10px; border-left: 6px solid transparent; border-right: 6px solid transparent; border-top: 10px solid #9a0000; border-bottom: 0; }<\/code>\n<\/pre>\n<\/div>\n","protected":false},"excerpt":{"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 […]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_uag_custom_page_level_css":"","footnotes":""},"categories":[3],"tags":[9,10,11,25,29],"uagb_featured_image_src":{"full":false,"thumbnail":false,"medium":false,"medium_large":false,"large":false,"1536x1536":false,"2048x2048":false},"uagb_author_info":{"display_name":"Yassir Yahya","author_link":"https:\/\/octalforty.com\/author\/yassir\/"},"uagb_comment_info":1,"uagb_excerpt":"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…","_links":{"self":[{"href":"https:\/\/octalforty.com\/wp-json\/wp\/v2\/posts\/81"}],"collection":[{"href":"https:\/\/octalforty.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/octalforty.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/octalforty.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/octalforty.com\/wp-json\/wp\/v2\/comments?post=81"}],"version-history":[{"count":1,"href":"https:\/\/octalforty.com\/wp-json\/wp\/v2\/posts\/81\/revisions"}],"predecessor-version":[{"id":530,"href":"https:\/\/octalforty.com\/wp-json\/wp\/v2\/posts\/81\/revisions\/530"}],"wp:attachment":[{"href":"https:\/\/octalforty.com\/wp-json\/wp\/v2\/media?parent=81"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/octalforty.com\/wp-json\/wp\/v2\/categories?post=81"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/octalforty.com\/wp-json\/wp\/v2\/tags?post=81"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}