Blog Archives

Use of bootbox.js for creating multiple prompts one after another

Bootbox.js is a small and handy JavaScript library which allows one to create dialog boxes using Twitter’s Bootstrap modals programmaticallly.

Basic Example:

  bootbox.prompt("What is your name?", function(result) {                
    if (result === null) {                                             
      // notify some error message
    } else {
      // do some stuff with result
    }
  });

Recently there is a requirement in my project which requires prompting user with few questions and based on user’s response process further stuff.

My first choice was to use bootbox.js’s prompt function and I went with it.

But then after some time I started to feel like “Oops! I selected wrong library here.”. I don’t know how to call another botbox.js prompt in the callback of first one without any redundancy.
Moreover, I need to store the result of each prompt’s response for later processing purpose.

Then, my team lead suggested me to use Underscore’s partial function and gave me pseudo code.

Underscore’s partial function basically defines a function named _.partial which accepts as a parameter a function and arbitrarily no. of arguments. The return value of _.partial is a new function that when called will pass both its own parameters and the initially provided arguments to the original function.

Basic Example of _.partial:

  var add = function(a, b) {
    return a + b;
  };

  var increment = _.partial(add, 1);

We can then call the increment function like this:

  var six = increment(5); // 6

For the add function in the above example, the parameter a is fixed to
the value 1. The first argument that’s passed to increment will be passed to add
as the b parameter.

The same technique I used in the implementation of invoking multiple bootbox.js prompts on
successive prompt’s callback.

Here is how I implemented it:

    var survey_pair, survey_pairs, survey_answers = {};

    // Survey type : Question
    survey_pairs = {
        name: 'What is your name?',
        age: 'How old are you (in number only)?',
        color: 'What is your favourite color?',
        movie: 'What is your favourite movie?'
    };

    function displaySurveyResults(responses) {
        var survey_results_div = $('#survey_results');
        survey_results_div.html('');
        _.each(responses, function(value, key) {
            survey_results_div.append('<li><strong>' + key + '</strong>: ' + value + '</li>');
        });
    }

    function getFirstSurvey() {
        var first_key, first_prompt = {};
        first_key = _.first(_.keys(survey_pairs));
        if (first_key) {
            first_prompt[first_key] = survey_pairs[first_key];
            delete survey_pairs[first_key];
        }
        return first_prompt;
    }

    // main functions
    function showSurvey(survey, callback) {
        // Making survey's type as fixed argument to partial
        var new_callback = _.partial(callback, _.keys(survey)[0]);

        // Calling bootbox.prompt with survey's question and
        // partial callback which has survey's type already
        // and user response will be given through the new_callback
        bootbox.prompt(_.values(survey)[0], new_callback);
    }

    function surveyCallback(survey_type, survey_response) {
        survey_answers[survey_type] = survey_response;
        survey_pair = getFirstSurvey();
        if (survey_pair && !_.isEmpty(survey_pair)) {
            showSurvey(survey_pair, surveyCallback);
        } else {
            displaySurveyResults(survey_answers);
        }
    }

    // Get first question and initiate survey
    survey_pair = getFirstSurvey();
    if (survey_pair && !_.isEmpty(survey_pair)) {
        showSurvey(survey_pair, surveyCallback);
    } else {
        displaySurveyResults(survey_answers);
    }

Here in showSurvey, I am fixing first argument of callback which refer to surveyCallback passed from showSurvey (line no. 53).
The second argument i.e. survey_response of surveyCallback(or callback in showSurvey)
will refer to bootbox.js prompt’s result(or user’s response).

Rest logic is simple. In surveyCallback I am setting survey’s response in some variable
survey_answers here and then fetching next first survey and repeating same iteration.

Click here for working demo.

Shrinking issue in jQuery Splitter on screen resize

After using jQuery splitter, everything was working as according to my requirement.
But as you never get your destination smoothly I also got stuck by one issue.

The issue was when I minimize the screen the three vertical panes doesn’t resize as proportion to their width ratio as shown below:

Right Pane shrinked

Right Pane shrinked

It looks like limitation of the jQuery Splitter plugin. Same issue found in Demo Site.

To solve this issue, I need to do following workaround.
This really helped me to meet my requirement properly.

   <script type="text/javascript">
      function resizeClassView(){
        var main_div_width, left_pane_width, right_pane_width,
            first_left_pane_width, second_left_pane_width, left_splitter, right_splitter;

        main_div_width = $(window).width() - 72;
        left_pane_width = (0.67 * main_div_width);
        right_pane_width = main_div_width - left_pane_width;
        first_left_pane_width = (0.5 * left_pane_width);
        second_left_pane_width = (left_pane_width - first_left_pane_width);
        left_splitter = $($('.vsplitter')[0]);
        right_splitter = $($('.vsplitter')[1]);
        $('#main_div').width(main_div_width + 'px');
        $('#left_pane').width(left_pane_width - 2 + 'px');
        $('#first_left_pane').width(first_left_pane_width - 5 + 'px');
        $('#second_left_pane').width(second_left_pane_width + 'px');
        $('#right_pane').width(right_pane_width + 'px');
        left_splitter.css('left', first_left_pane_width - 7 + 'px');
        right_splitter.css('left', left_pane_width - 5 + 'px');
      }

      $(document).ready(function(){
        var main_div_width = '100%';
        $('#main_div').width(main_div_width).height(500).split({
          orientation: 'vertical',
          limit: 3,
          position: '67%'
        });
        $('#left_pane').width($('#left_pane').width()).height(500).split({
          orientation: 'vertical',
          limit: 3,
          position: '50%'
        });

        // handling widow resize event
        $(window).resize(function() {
          resizeClassView();
        });
      });

   </script>

I handled window resize event and calculated and applied each div’s width and slider left attribute.

Split ‘div’ into three equal parts vertically

There has been a requirement in my project where I have to split a particular div into three equal parts vertically.
It must possess following properties:

  1. Each part should have a slider to resize its width
  2. One should be able to display two parts at a time or complete one part at a time
  3. The content within resizing part should also resize as according to slider movement

I googled for the possible available plugins.
Out of many I found jQuery splitter plugin interesting and decided to go with it.

The Demo Site demonstrates partially what I need but I know I need to do some tweak by myself to make this plugin work exactly what I want.

So following are the steps to how to use this plugin especially in my scenario:

  1. First include this plugin in your html file as :
      <script type='text/javascript' src='../lib/js/jquery-1.9.0.min.js'></script>
      <script type='text/javascript' src='../lib/js/jquery.splitter-0.14.0.js'></script>
      <link rel='stylesheet' type='text/css' href='../lib/css/jquery.splitter.css'>
      ....
    

    NOTE: jQuery should be loaded first as jQuery splitter depends on jQuery.

    You can get jquery.splitter-0.14.0.js(or latest version) and jquery.splitter.css in its Git Repo under js and css folders respectively.

  2. In html body, create div’s structure as bellow:
      <div id="main_div">
        <div id="left_pane">
          <div id="first_left_pane">
            <!-- your left content goes here --></div>
          <div>
          <div id='second_left_pane'>
            <!-- your middle content goes here --->
          </div> 
        </div>
        <div id='right_pane'>
          <!-- your right content goes here --->
        </div>
      </div>
    
  3. In script tag, call splitter method as:

      $('#main_div').width(800).height(500).split({
        orientation: 'vertical',
        limit: 3,
        position: '67%'
      });
      $('#left_pane').width($('#left_pane').width()).height(500).split({
        orientation: 'vertical',
        limit: 3,
        position: '50%'
      });
    

    Here, I am calling splitter method ‘split’ 2nd time to split the ‘left_pane’ div into two equal parts.

    That’s it. Three simple steps and you get the div splitted into approximately three equal parts.

  4. Optionally, if you want to apply extra CSS to slider bar you can do as:

      .splitter_panel .vsplitter {
        position: absolute;
        right: 0;
        top: 0;
        bottom: 0;
        width: 6px !important;
        background-color: gray;
        cursor: ew-resize;
        line-height: 15px;
        font-size: 14px;
        overflow: hidden;
      }
    
      .splitter_panel .vsplitter:before {
        content: ' * * * ';
        color: white;
        position: absolute;
        top: 50%;
      }
    

Following is the screenshot of what it appears after following above steps successfully:

Three Panes Vertically

Three Panes Vertically

Re-initialization of datasets in typeahead.js

typeahead.js is one of autocomplete search plugin inspired by twitter.com.

I have been using it quite from a while in my projects and had wonderful experience so far until I encountered with one requirement.

Requirement: Remove the selected data from dataset and don’t show it in next autosearch suggestions.

I used $(‘.typeahead’).typeahead(‘destroy’); as suggested but it didn’t work as expected.

It reverts the input element back to its original state but again adding typeahead feature to this same input with new dataSets doesn’t remove the autocomplete suggestions with new dataSets.

While googling for few hours for the ideal solution I found one good solution out of the discussion made in this github issue thread.

So following is solution I used and it works like charm!!! 🙂

Steps I followed for the solution:

  • Define a function to enable typeahead functionality as:
      function enable_auto_complete(name, dataSets){
        $('#typeahead_input_element').typeahead({
          name: name,
          local: dataSets
        });
      }  
    
  • Initialize the input with typeahead for first time with some name.
  • Remove the selected name from global ‘dataSets’.
  • Destroy typeahead feature as :
      $('#typeahead_input_element').typeahead('destroy')
    
  • Initialize the input again with typeahead but with different name and updated dataSets.

Working JSFiddle Link.