Patrick Desjardins Blog
Patrick Desjardins picture from a conference

Quick Add Html Button with CSS3 and JQuery

Posted on: 2015-08-04

This is a small JavaScript and CSS control that allows to have some edition from a textbox in a fashion that this one is only a button to minimize space and once clicked become a more extended version. The goal is to have a way to add information in a quick way without taking too much space. This is perfect for situation where adding information is not the main task. The use case is that a user want to add information which require a text box. The user click the button to open the edition which reveal a text box. The button does not need to stay there because we want to save space so the button transforms into a closing button which cancel the operation if required. When the user starts to type, a save button is added. From there, the user can click the cancel button or hit save. In both case, the text box is rolling back and the user interface come back to the initial state. This design is intended to be minimal. There is not label. This is why we need to use a watermark, also known as placeholder in the text box. Since we want to limit button, the expand button and cancel (collapse) button is the same. To make sure that the user knows what the button is doing we display a tooltip but also we have an animation that transform visually the button to its intend.

To keep it simples, let's start with a division that will hold all our Html elements.

<div id="container"> </div> 

Next, we need to create our three main controls. The first one is the input where the user will write any information that will be saved. The second one is the toggle button that expand or collapse the input box and the third one is the save button that will be displayed only when the user save.

var $controlContainer = $('#container');

var $inputBox = $("<input>") 
  .attr('id', 'inline-editor-textbox') 
  .attr('type', 'text') .attr('value', '') 
  .attr('maxlength', 20 ) 
  .attr('style', 'display:none') 
  .attr('placeholder', "This is some watermark") 
  .addClass('hide-input-inline') ; 
$inputBox.appendTo($controlContainer);

var $buttonToggle = $("<button>")
  .attr('id', 'toggleButton')
  .attr('title', 'Open')
  .attr('type', 'button'); 
var $spanIcon = $('<span>')
 .addClass("glyphicon glyphicon-plus"); 

$spanIcon.appendTo($buttonToggle); 
$buttonToggle.appendTo($controlContainer);

var $buttonOk = $("<button>")
 .attr('id', 'okButton')
  .attr('style', 'display:none')
  .attr('title', 'Ok') 
  .addClass('hide-ok-button')
  .attr('type', 'button'); 

var $spanIconOk = $('<span>')
  .addClass("glyphicon glyphicon-save"); 

$spanIconOk.appendTo($buttonOk); 
$buttonOk.appendTo($controlContainer);

The next step is to add events. The toggle will open and close the input. It will also change the icon of the button from a + to a x. The okay button will save the information and collapse the input if everything is fine. The keyup is there to show the save button if the text box contains text.

$buttonToggle.click({ input: $inputBox, buttonToggle: $buttonToggle, buttonOk: $buttonOk},onToggle); 
$buttonOk.click({ input: $inputBox, buttonToggle: $buttonToggle, buttonOk: $buttonOk},onOk); 
$inputBox.keyup( { input: $inputBox, buttonOk: $buttonOk }, onKeyUp); 

The toggle function has reference to some of our controls. This is a great way to pass reference without having to communicate through JQuery selector. Depending of the state of the toggle, which is the class "hide-input-inline" we make it appears or not. We add a delay because we have CSS3 animation and we want everything to be smooth together.

function onToggle(jqueryEvent) { 
  var data = jqueryEvent.data; 
  var $input = data.input; 
  var $buttonToggle = data.buttonToggle; 
  var $buttonOk = data.buttonOk;

var isHidden = $input.hasClass("hide-input-inline"); 
if (isHidden) { 
  $input.fadeIn(); 
  $input.removeClass("hide-input-inline"); 
  $input.addClass("show-input-inline"); 
  $input.val(''); 
  $buttonToggle.addClass("add-to-close"); 
  $buttonToggle.attr('title', 'Close'); 
} else { 
  $input.removeClass("show-input-inline"); 
  $input.addClass("hide-input-inline")
   .delay(600)
   .fadeOut(); 
  $buttonToggle.removeClass("add-to-close"); 
  $buttonOk.addClass("hide-ok-button"); 
  $buttonOk.removeClass("show-ok-button").delay(600).fadeOut(); 
  $buttonToggle.attr('title', 'Open'); 
  } 
} 

The key up is to check the length but we could also check for "Enter" to be pressed and invoke the Ok code. The code is adding or removing classes, with a delay to let CSS animation to be executed.

function onKeyUp(jqueryEvent) { 
  var data = jqueryEvent.data; 
  var $input = data.input; 
  var $buttonOk = data.buttonOk; 
  if ($input.val().length === 0) { 
    $buttonOk.removeClass("show-ok-button"); 
    $buttonOk.addClass("hide-ok-button").delay(600).fadeOut(); 
    htmlElementWithIconState($buttonOk, true); 
  } else { 
    $buttonOk.fadeIn(); 
    $buttonOk.addClass("show-ok-button"); 
    $buttonOk.removeClass("hide-ok-button"); 
    htmlElementWithIconState($buttonOk, false); 
  } 
} 

This helper method is used to mark the save icon into a disable state. At this moment, this is not really used but if validation is added we could still have the button add without having to remove it until the user fix the problem.


function htmlElementWithIconState($element, disabled) { 
  $element.prop("disabled", disabled); 
  $element.find('.glyphicon').css('opacity', (disabled === true) ? .4 : 1); 
} 

Finally, we have the okay button hit which close up the text box and every buttons. In real life, a callback into a real save or okay code would be executed. If successful than close, otherwise keep everything open for the user to adjust his or her input.

function onOk(jqueryEvent) { 
  var data = jqueryEvent.data; 
  var $input = data.input; 
  var $buttonOk = data.buttonOk; 
  onToggle(jqueryEvent); 
} 
#toggleButton, #okButton { 
   padding:0 2px 4px 2px; 
   margin-left:3px; 
   height: 22px; 
   width: 22px; 
   vertical-align: top; 
}

#toggleButton span.glyphicon { 
  transition: all 1000ms ease; 
}

#okButton { 
  transition: all 1000ms ease;
}

.hide-input-inline { 
  width:0; 
  transition: width 1000ms ease-in-out; 
}

.show-input-inline { 
  width:210px; 
  transition: width 1000ms ease-in-out; 
}

.add-to-close span { 
  transform: rotate(135deg); 
}

.hide-ok-button { 
  opacity:0;
}

.show-ok-button { 
  opacity:1; 
}

#inline-editor-textbox::-webkit-input-placeholder { 
  /* WebKit browsers */ 
  color:#7f7f7f
} 

#inline-editor-textbox { 
  border: none; 
  background-color: #E2E2E2; 
  color:#7f7f7f; 
  padding-left:4px; 
  height:22px;/*To fix the size of the button since we do not have any border*/ 
 }
 }

#inline-editor-textbox:focus { outline: none; background-color:white; } 

Interactive Demo

https://jsfiddle.net/mrdesjardins/tLpu8kfr/3/