Showing posts with label bootstrap. Show all posts
Showing posts with label bootstrap. Show all posts

Wednesday, July 6, 2016

Tooltips sans Bootstrap

If you're using Bootstrap to do tooltips, you're doing it wrong, and your site's performance probably shows you're doing it wrong.

Here's an example I've seen recently

HTML
<a href="#" data-toggle="tooltip" title="Get you some of that">   <h2>GYSOT</h2> </a>

Here are a few problems with this approach:

  1. if you're doing it this way you're not only using the Bootstrap CSS file (which to be honest is a little bloated) but you're also using Bootstrap JS
  2. your code is not semantic
  3. your code has a few extra accessibility issues

It would be more semantic if the markup was something like…
HTML
<h2 data-toggle="tooltip" title="Get you some of that">GYSOT</h2>

...but that doesn't really solve any performance issues your users are experiencing because of all the extra goodies Bootstrap includes that you're not using – and it doesn't solve the accessibility issues. That's why it would be nicer if we wrote the markup as…

HTML
<h2 aria-label="Get you some of that" class="has-tooltip">GYSOT</h2>

Here's the thing. Not only is this less markup, but here's all the CSS you'll need to put in a tooltip.

CSS
.has-tooltip {   display:inline;   position:relative; } .has-tooltip:focus:after, .has-tooltip:hover:after {   background:rgba(200, 200, 200, .9);   border:0.1em solid rgb(128, 128, 128);   border-radius:0.3em;   bottom:1.3em;   color:rgb(0, 0, 0);   content:attr(aria-label);   left:0;   padding:0.1em;   position:absolute;   z-index:1; } .has-tooltip:focus:before, .has-tooltip:hover:before {   border-color:rgb(128, 128, 128) transparent;   border-left:0.3em solid transparent;   border-right:0.3em solid transparent;   border-top:0.3em solid rgb(128, 128, 128);   bottom:1em;   content:"";   left:50%;   position:absolute;   z-index:2; }


It really is that simple to display a tooltip directly above some text and to make the content of the tooltip directly accessible.

A couple little notes - if you want to display the tooltip on the bottom of the you'll have to adjust the bottom values for both the before and after pseudoclass rules and if you want to display the tooltip with a different font or font-size you'll have to adjust those on the before and after pseudoclasses as well. All the detailed customization aside, this should get to started down the road to ditching Bootstrap.

Happy coding.

Saturday, June 21, 2014

Pick a better date

This post is content previously published on www.cathmhaol.com

In January I wrote an entry about how to integrate eternicode's datepicker, called "Pick a date". Because of a question on LinkedIn, I revisited that entry, and wondered what might make it a little better. Granted, it's probably not necessarily that difficult to think of a few ways – some technical and some related to the style. So, let's improve the styling of the date interface – something a little more "mobile friendly" – something like the lovely rebeccapurple sample shown here. I'll also take just a moment to point out that this method works well only if the date input is a single input. If your date input is broken into parts with number inputs for day, month, and year, you'll want to find a different approach.

HTML

Note: you might want to open the original post in another window so you can compare the code side-by-side.

Let's start by using the "date" type, because native support is always going to be better – and we can do this easily, because the input type fall-back is "text", which is what we used originally. To add to that, we're going to layer the styled elements, because frankly, the simple input box is boring...useful, but boring.

Of course the styled version only improves the interface if the date is populated – if you don't pre-populate the dates, you may want to use the associated CSS (opened and closed classes are shown here) and/or JavaScript to hide the styled interface until there's a value available, or you may wish to give the user other messaging. Also note that we're using the aria-hidden attribute on the styled block. We're doing that because it may be confusing to have the same date repeated and it's really just a styled version of the input, so it's unnecessary for ARIA devices. With these thoughts in mind, here's the HTML we'll be working with.

<fieldset>
  <legend>Date Range</legend>
  <div class="date">
    <div aria-hidden="true" class="styled closed">
      <span class="date-number">01</span>
      <span class="date-name">Thu</span>
      <span class="date-month">January</span>
      <span class="date-year">1970</span>
    </div>
    <div class="input opened">
      <label for="dtfrom">From:</label>
      <input id="dtfrom" type="date" value="1970-01-01" />
    </div>
  </div>
  <div class="date">
    <div aria-hidden="true" class="styled closed">
      <span class="date-number">31</span>
      <span class="date-name">Wed</span>
      <span class="date-month">December</span>
      <span class="date-year">2014</span>
    </div>
    <div class="input opened">
      <label for="dtto">To:</label>
      <input id="dtto" type="date" value="2014-12-31"/>
    </div>
  </div>
</fieldset>
<!-- don't add the 'inline-calendar" or "backToTop" elements if you're not displaying the calendar inline -->
<div id="inline-calendar"></div>
<a aria-hidden="true" name="backToTop">back to top</a>

Now that you have the HTML under control, go ahead and add the code for the datepicker – you can get that code from the "Pick a date" blog entry – just make sure that it's added unobtrusively. If you're going to be using a date range, go ahead and port over any other code you need for setting the range, e.g., the setValidRangeBegin, setValidRangeEnd, or setRange functions. Also, remember that we're only adding the eternicode if the "date" input is not supported, so add a check around the datepicker creation statement – that's the code that looks something like

$('#dtfrom').datepicker({
  language: strLanguageCode
  startDate: datStart,
  endDate: datEnd,
  autoclose: true,
  forceparse: true
})

If you're using modernizr, you can use if (!Modernizr.inputtypes.date), which is relatively simple. If you're not using modernizr for other purposes, don't add it just for this – write your own checker instead. If you're unclear about how to do this, check out "Detecting HTML5 Features" for a description of how modernizr does it.

CSS

Now that we have our HTML constructed, let's add the CSS that will enable us to show this lovely stylized sample. Note that in our HTML we have defaulted the style to have the input block open and the styled block closed – this will handle any user-agents where JavaScript is not running (which happens more often than you might think).

body {
  background:#639; /* use the code, because not all browsers use rebeccapurple */
  color:#fafafa;
  font-size:15px;
}

.date {
  display:inline-block;
  margin:0;
  padding:0;
  overflow:auto;
  text-align:center;
  width:175px;
}
.date > .styled {
  border:1px solid #fafafa;
  border-radius:0.5em;
  cursor:pointer;
  display:inline-block;
  margin:0.5em 0.25em;
  padding:0.2em;
  position:relative;
  width:auto;
  z-index:10;
}
.date > .styled.closed {
  clip:rect(0, 0, 0, 0);
  position:absolute;
}
.date > .styled > .date-number {
  font-size:1.5em;
}
.date > .styled > .date-name {
  font-size:1.5em;
}
.date > .styled > .date-month {
  font-size:0.8em;
}
.date > .styled > .date-month:after {
  content:", ";
}
.date > .styled > .date-year {
  font-size:0.8em;
}
.date > .input {
  clip:rect(0, 0, 0, 0);
  position:absolute;
}
.date > .input.opened {
  position:relative;
}

Of course your style needs may be different. and in fact probably are. Just be sure that you don't fall victim to little gotchas like lacking sufficient contrast between the background and foreground colors. Consider the code provided here an example, not hard, fast rules.

JavaScript

Now that we have the HTML and CSS, let's turn our attention to the JavaScript that handles switching the focus from the styled presentation to the input. This approach allows us to continue to use the focus handlers created by datepicker or the native implementation. Here I'm using jQuery because most everyone is familiar with it – I'm not using it to recommend that you use jQuery, in fact there are a lot of good reasons not to use it (which I won't go into in this entry).

$('.date .styled').click(function() {
  var $element = $(this)
    , $input = $element.next().find('input[type="date"]')
  ;
  $input.focus();
});

That code – which shifts focus to the input when the styled element is clicked – is simple enough. There are, of course, other ways of doing it. For example, you could add an attribute that links the two so you're not dependent on proximity in the code, but I'm trying to show a simple example here, and that solution adds unnecessary complexity.

Next, we're going to add listeners to the focus and blur events of the inputs, which will be executed alongside the datepicker listeners. First, we attach simple code that will just add the 'closed' and 'opened' classes to the elements to hide the stylized date and show the input.

$('.date .input input[type="date"]').focus(function() {
  var $element = $(this)
    , $input = $element.closest('.input')
    , $styled = $input.prev()
  ;

  // transition the visibility
  $styled.addClass('closed');
  $input.addClass('opened');
});

Then we add code that is slightly more complex, but does basically the same thing as the focus handler in reverse. The difference being that if the date input is empty, we do not open the stylized date and close the input – because that would simply show an empty block – and we only update the stylized date parts if we have a valid date.

$('.date .input input[type="date"]').blur(function() {
  var $element = $(this)
    , $input = $element.closest('.input')
    , $styled = $input.prev()
    , dt
  ;

  if ($element.val()) {
      dt = Cathmhaol.Calendar.getLocalizedDate($element.val());
      // update the values in the styled block if we have a valid date
      if (dt.date && dt.dayName && dt.monthName && dt.year) {
        $styled.find('.date-number').text(dt.date);
        $styled.find('.date-name').text((dt.dayName || '').substr(0, 3));
        $styled.find('.date-month').text(dt.monthName);
        $styled.find('.date-year').text(dt.year);
      }

      // transition the visibility
      $input.removeClass('opened');
      $styled.removeClass('closed');
  }
});

You'll also notice that I'm using the Calendar library from cathmhaol.com – you can pick that up from either js.cathmhaol.com or from the github repo – but minify it before using it in a production environment. You should also feel free to modify the section that updates the stylized date using a different library, or native JavaScript you've written.

The final step is to add code into your window load event handler that removes the 'closed' class from the stylized date and the 'opened' class from the input – classes that we had included for those instances when JavaScript either is not available or has crashed due to some error – something like the following code.

$('.date .input.opened').removeClass('opened');
$('.date .styled.closed').removeClass('closed');

Once that's done, you should have a fully functional stylized date.

Happy coding!

Saturday, February 8, 2014

Easy POSH forms (mobile modal windows)

This post is content previously published on www.cathmhaol.com

In the switch to 'mobile first', one of the things we've had to address is all of those modal windows we created. In a mobile device, how do we create those easily? Let's use a straightforward example that builds on the datepicker we discussed in the last post.

Since we're working with mobile, we're going to use an inline date selector, so we know a little about the design, but we also want to be able to accept the range selected (we'll use an 'OK' button) and we want to have a few other buttons - like a reset that will return the date inputs to their original values - and a 'cancel' button so we can dismiss the modal without making a change.

As always, we're going to start with the markup.

HTML

You'll notice the markup is almost exactly the same as it is in the 'Pick a date' post, with the addition of the action buttons and initial values for the date fields.

<form class="modal" method="post">
  <header>
    Travel Details
    <input class="close" type="button" value="&times;" />
  </header>
  <fieldset>
    <legend>Date Range</legend>
    <div class="date">
      <label for="dtfrom">From</label>
      <input data-original="1/1/1970" id="dtfrom" type="text" value="1/1/1970"/>
    </div>
    <div class="date">
      <label for="dtto">To</label>
      <input data-original="12/31/2013" id="dtto" type="text" value="12/31/2013"/>
    </div>
  </fieldset>
  <!-- don't bother adding this 'inline-calendar' element if you're not displaying the calendar inline -->
  <div id="inline-calendar"></div>
  <fieldset>
    <legend>Departure and Arrival Airports</legend>
    <div class="airport">
     <label for="depart">Depart</label>
     <input id="depart" type="text" />
    </div>
    <div class="airport">
     <label for="arrive">Arrive</label>
     <input id="arrive" type="text" />
    </div>
  </fieldset>
  <div class="btns">
    <input type="submit" value="OK" />
    <input id="btnReset" type="button" value="RESET" />
    <input class="close" type="button" value="CANCEL" />
  </div>
  <a aria-hidden="true" class="backToTop" name="backToTop">back to top</a>
</form>

As with the 'Pick a date' post, you'll want to be sure to include the 'back to top' link and attach a listener for the focus event so that when the focus event occurs, focus is moved to the first element in the modal form. If you haven't read the 'Pick a date' post, go read the 'Inline Implementation' section for clues about what to do with this 'back to top' link, including the CSS to hide it. If you skip this step, focus will move from your modal to the page behind it, and your user experience will degrade.

JavaScript

We're going to keep the same JavaScript as we have for the Inline Implementation in the 'Pick a date' post, but we're going to add a function to reset the date to the original values as a listener on the reset button. We also need a function to dismiss the modal that we tie to the cancel button. The functions provided assume that the buttons are the only matching elements on the page - you may need to 'find' them within the modal if you have several modals or other matching elements on the page.

$('#btnReset').on('click', function() {
  var $inputs = $('input:not(type="hidden")')
    , $input
    , $index = $inputs.length
  ;
  while ($index-- > 0) {
    $input = $($inputs[$index]);
    $input.val($input.get('data-original'));
  }
});
$('input.close').on('click', function() {
  var $container = $(event.currentTarget).closest('modal');
  if ($container) {
    $container.removeClass('in');
  }
});
Now you have JavaScript that will close the container (by removing the 'in' class) when the cancel button or the close button are clicked and JavaScript that will loop through all the non-hidden inputs and reset them. Of course if you have SELECT tags or TEXTAREA tags you'll need to reset them, and you'll also need to reset the 'checked' attribute on radio and checkbox type inputs, but this is sufficient for our example.

CSS

Now for the fun part - the CSS is going to be wrapped in a media query so that we're not modifying anything for the desktop - we want that to be a plain vanilla style. The modal window, though should animate by floating up from the bottom of the screen and should have evenly sized and spaced buttons at the bottom. To do this, we're going to use a transition for the animation and a pseudo-classes for the buttons.

.modal {
  @media (max-width: 480px) {
    border-top: 1px solid #999;
    height: 100%;
    padding: 16px;
    width: 100%;

    display: block;
    position: absolute;
    top: 100%; /* This puts the modal at the bottom of the screen */
    z-index: -1; /* This puts the modal under everything */

    background-color: #fff;
    transition: top 0.5s, z-index 0.6s;
    -webkit-transition: top 0.5s, z-index 0.6s;

    header {
      height: 44px;
      margin: 0;
      overflow: hidden; /* This will make the header 'contain' the close button */
      padding: 0;

      font-size: 20px;
      line-height: 44px; /* Making this the same as the height will make the text vertically center */
      text-align: center;
    }

    .close {
      float: right;
      height: 44px;
      margin: 0;
      width: 44px;

      font-size: 36px;
      text-align: center;
    }

    .btns {
      text-align: center;

      input {
        margin: auto 1%;
      }

      input:first-child:nth-last-child(1) {
        width: 100%;
      }
      input:first-child:nth-last-child(2), input:first-child:nth-last-child(2) ~ input {
        width: 48%;
      }
      input:first-child:nth-last-child(3), input:first-child:nth-last-child(3) ~ input {
        width: 31.3333%;
      }
      input:first-child:nth-last-child(4), input:first-child:nth-last-child(4) ~ input {
        width: 23%;
      }
      input:first-child {
        margin-left: 0;
      }
      input:last-child {
        margin-right: 0;
      }
    }

    &.in {
      top: 0;
      z-index: 10000;
    }
  }
}

A little explanation about the CSS is in order.

First, I'm using LESS. In my opinion, LESS syntax is easily understood even for those who haven't fallen into that rabbit hole, and if you haven't fallen into that particular rabbit hole, I'd encourage you to explore it - which you can do at http://lesscss.org/.

Second, there are a few lines that are documented inline. This is a practice that, if you are not doing it already, you should start. There are always at least two developers working on a project - present you and future you - and inline documentation like this will help you remember why you did something.

Third, although it's not documented inline, I'm using 44 pixels because it's really the smallest object you can poke. If you make something smaller, your users will be frustrated. Of course you should feel free to make things larger as fits your design.

Fourth, there are two animations because we're hiding the panel from the user in two ways. Using a longer duration for the z-index animation makes sure that it flows on and off the screen before dropping behind the other layers. Using a high value for the 'in' z-index makes sure that it rises to the top layer earlier in the transition duration. Although the maximum value you can use in modern browsers for a z-index is 2147483647 (32-bit), in practice there's no need for one that high - which is why I'm using 10000.

Fifth, and finally, a note about the button row of the modal window. First, keep elements that are not INPUT tags out of the button row - it's not semantic and it will degrade the user experience by altering the display. Second, the style rules are written to accommodate up to four buttons in the button row. If you find that you really need more than four buttons, use more button rows. Third, keep in mind that in order for this to work, you will need to keep the text displayed inside the buttons within reason. As you will notice, the width of the buttons is 100 divided by the number of buttons in the row minus two. The 'two' that we're subtracting is the margin on the left and right sides of the buttons. The first and last buttons in the row (left-most and right-most) have the corresponding margin removed.

Conclusion

Using this design pattern, you can easily add a modal window to your mobile devices that is easily maintained by keeping the markup simple and semantic. The pattern is also easily modified - for example, you may choose to have the modal window slide in from the left or right side, which is simply a matter of changing the left or right position and changing the transition property from top to left or right.

Just remember you can accomplish much, even on mobile, by keeping it POSH and adding a little CSS3 and a little JavaScript.

Saturday, January 25, 2014

Pick a date

This post is content previously published on www.cathmhaol.com

For the last month or so, I've been keeping my head down and trying to get some work done. Some of the effort has paid off, some hasn't, but that's as it always is. Anyway, I decided to put aside all the other topics kicking around in my head to put something down on (virtual) paper about one of the things I've been working on. So, here goes....

Recently I've been fiddling with the datepicker written by eternicode1. It's a decent product though not so terribly different than the vanilla JS libraries I wrote for the same purpose years ago2. Here's my problem - writing code to pick a date range using this sucks. The documentation is, at best, incomplete, and in some cases is downright incomprehensible. To help address that sad state of affairs, I'm going to show all the code necessary to implement the datepicker to solve the problem of wanting a start date and an end date without having odd things like a start date that's after the end date.

First, let's start off with the HTML - because that's where you should always start. People who try to get you to abandon Progressive Enhancement are evil geniuses who spend their time gently stroking a cat. Luckily for us, generating the HTML is fairly simple, even when we add in the challenge of generating something that's both semantic and accessible. One little note - I'm adding a div outside of the fieldset that collects the date range information - it will only be used in the 'inline' implementation, not the 'native' implementation of the datepicker. Enough babbling...here's the HTML.

HTML

<fieldset>
  <legend>Date Range</legend>
  <div class="date">
    <label for="dtfrom">From</label>
    <input id="dtfrom" type="text" />
  </div>
  <div class="date">
    <label for="dtto">To</label>
    <input id="dtto" type="text" />
  </div>
</fieldset>
<!-- don't add the 'inline-calendar" or "backToTop" elements if you're not displaying the calendar inline -->
<div id="inline-calendar"></div>
<a aria-hidden="true" name="backToTop">back to top</a>

Ok. Now that you have your HTML written, make sure that it works just as it is, because on this site we're always going to use Progressive Enhancement, and no matter what sort of validation bells and whistles you have on the front-end, you always need to validate data the user passes you.

Once you've gotten your HTML under control you'll need to add the datepicker, and that means you'll need to decide if you want the 'native' version - the version that displays when the input has focus, like a tooltip - or the 'inline' version - the version that displays the calendar all of the time. This is really a design decision but in my experience, the 'native' version is too small for a mobile device...so even if you opt for the 'native' version you might want to do the inline version for mobile devices. As another consideration, there are times when the 'native' implementation looks a little odd in a small viewport - the carets used to associate the calendar with the control don't always line up the way you might want. All that, of course, leads me to the point that you always want to be sure to test your code on several different devices - it's not enough to just test the most popular browsers on a desktop.

Now that you've made your design decisions and you have working HTML, you'll want to instantiate your datepicker(s). Here I'm going to use variables for a few things without showing you their values - things like a language code3 and starting and ending dates for the initial date range - and since everyone loves jQuery, I'll give the example using jQuery syntax - if you're used to vanilla JS you won't have any trouble figuring out what's going on anyway. I'm going to separate this section by implementation style, which will make it easier for you to jump to the section that matches your design choice.

Native Implementation

A snapshot of the native implementation
Native Implementation
The 'native' implementation is relatively straightforward, and you can find plenty of examples of it online. Restricting the date ranges for the two dates, beginning and ending, is a little tricky. If you're not careful and attempt to restrict the dates by removing the datepicker and re-creating it, you'll end up in an infinite loop. That means that the gotcha you need to watch out for is that you're handing the changeDate event appropriately.

In the example presented here, I'm passing in the corresponding date and if that value is null, setting the appropriate boundary date to either three (3) years ago (by passing the string -3y4) or today. You may wish to put all of this into an object and pass the entire object into the changeDate event, or you may wish to handle defaulting the date another way.

/**
 * Get the beginning date input and create a datepicker,
 * and be sure to use the changeDate event to set the
 * earliest date the ending date can start
 */
$('#dtfrom').datepicker({
  language: strLanguageCode
  startDate: datStart,
  endDate: datEnd,
  autoclose: true,
  forceParse: true
}).on('changeDate', datEnd, setValidRangeEnd);

/**
 * Get the ending date input and create a datepicker,
 * and be sure to use the changeDate event to set the
 * latest date the beginning date can end
 */
$('#dtto').datepicker({
  language: strLanguageCode
  startDate: datStart,
  endDate: datEnd,
  autoclose: true,
  forceParse: true
}).on('changeDate', datStart, setValidRangeBegin);

/**
 * Sets the ending date for the 'beginning' date
 * @return {void}
 * @param {event} event
 */
function setValidRangeBegin(event) {
  var $input = $('#dtfrom')
    , dt = (event && event.date) ? new Date(event.date) : (event.data || '-3y')
  ;
  $input.datepicker('setEndDate', dt);
}

/**
 * Sets the starting date for the 'ending' date
 * @return {void}
 * @param {event} event
 */
function setValidRangeEnd(event) {
  var $input = $('#dtto')
    , dt = (event && event.date) ? new Date(event.date) : (event.data || new Date())
  ;
  $input.datepicker('setStartDate', dt);
}

Of course you may also want to set the dates based on the values in the 'beginning' and 'ending' date fields. Doing so will require a little finesse as you'll need to set the dates when the field gets focus but before the datepicker is shown. This approach is much more difficult because there isn't a 'before the element is shown' event raised by the datepicker.

Inline Implementation

A snapshot of the inline implementation
Inline Implementation
The 'inline' implementation is a little less straightforward. Unlike the 'native' implementation, the inline implementation requires that we keep track of which date element is active so that we can set the correct element. Also, since the changeDate event applies to a single calendar, we have to handle it a little differently too.

As you'll notice from the snapshot, the inline implementation is not tied to a single text input, but is instead tied to an HTML block. When the datepicker is shown, it is contained by the HTML block with the 'inline-calendar' id in the sample HTML. If you select the inline implementation, you will need to provide this stub in your HTML.

Also, you'll likely be using that "back to top" anchor. To do this, add a line to your CSS - a[aria-hidden="true"] { position: absolute; clip: rect(0px, 0px, 0px, 0px); } - to hide the 'back to top' link, and add a focus event handler to move focus from the link to the first element in the form. You'll especially need to do this if your form should be modal, otherwise it won't be modal, focus will move to the next element. Since that listener is just about the easiest thing you can do, I won't bother adding it here. Be sure to use the clip method to hide the link, however, because if you use display none or visibility hidden, the focus event will never fire.

/**
 * Get the element used to contain the calendar,
 * and create the datepicker, assigning an event handler
 * for the changeDate event so that the value of the
 * active date element is set
 */
$('#inline-calendar').datepicker({
  language: strLanguageCode
  startDate: datStart,
  endDate: datEnd,
  forceParse: true
}).on('changeDate', setValue);

/**
 * Set a focus handler on the beginning date,
 * so that we can track which element is active
 * when focus moves to the datepicker
 */
$('#dtfrom').on('focus', setActive);

/**
 * Set a focus handler on the ending date,
 * so that we can track which element is active
 * when focus moves to the datepicker
 */
$('#dtto').on('focus', setActive);

/**
 * Return true if a value is a date
 * @return {boolean}
 * @param {variant} val
 */
function isDate(val) {
  return (val && val.getTime && !isNaN(val.getTime()));
}

/**
 * Set a class on the active element
 * @return {void}
 * @param {event} event
 */
function setActive(event) {
  var id = event.currentTarget.id
    , $active = $(event.currentTarget).closest('.date')
    , $inactive = $active.siblings().find(id == 'dtfrom' ? 'dtto' : 'dtfrom').closest('.date')
    , $toDate = $('#dtto')
    , $fromDate = $('#dtfrom')
  ;
  if ($inactive && $active) {
    $inactive.removeClass('active');
    $active.addClass('active');
  }
  if (id == 'dtfrom') {
    setRange(new Date(event.data.EARLIEST), $toDate.val(), $fromDate.val());
  } else if (id == 'dtto') {
    setRange($fromDate.val(), new Date(), $toDate.val());
  }
}

/**
 * Set the date range based on the user's selection
 * @return {void}
 * @param {date} beg - beginning date
 * @param {date} end - ending date
 * @param {date} sel - selected date
 */
function setRange(beg, end, sel) {
  // Make sure the params are dates
  var $beg = isDate(beg) ? beg : new Date(beg)
    , $end = isDate(end) ? end : new Date(end)
    , $pick = isDate(sel) ? sel : new Date(sel)
  ;
  // Set the dates
  $('#inline-calendar').datepicker('setStartDate', isDate($beg) ? $beg : beg);
  $('#inline-calendar').datepicker('setEndDate', isDate($end) ? $end : end);
  // Set the date and call the fill method so we highlight the date selected
  $('#inline-calendar').datepicker('setDate', isDate($pick) ? $pick : sel).datepicker('fill');
}
/**
 * Set the value of the active input using the selected date
 * @return {void}
 * @param {event} event
 */
function setValue(event) {
  // Use the pre-selected format by passing null into the format method
  $('div.date.active input[type="text"]').val(event.format());
}

Again, this is my preferred approach for mobile devices, because a little tweak of the zoom on the datepicker element and the calendar is as large as you need it without worrying about carets associating the calendar with a control. By creating style rules for the 'active' class you also visually indicate focus for the date field, removing the need for a caret.

Conclusion

Overall, the eternicode datepicker library is a decent library if you're already using bootstrap. I've not had any experience with the original bootstrap datepicker, so perhaps that's better documented, but I am doubtful of that considering the amount of searching I did before starting work with the library.

Just one more note - after years of implementing third-party code I've learned to start any implementation project by finding all the documentation I can about the code, whether I think it will apply to my work or not, and in fact have started more than one project by creating documentation for libraries where none existed before...though why an engineer would not document their code when tools like JavaDoc and JSDoc are available is beyond my understanding.
Give me six hours to chop down a tree, and I will spend four hours sharpening the axe.
Not Abraham Lincoln5







Notes and references

Links in the notes and references list open in a new window
  1. You can find the eternicode repo on github.
  2. There are two date-related libraries at http://js.cathmhaol.com/: Calendar and DateInput, which is similar to eternicode's datepicker fork of the bootstrap repo.
  3. The datepicker library is just one of several that uses common ISO codes for countries and languages, so it's best to become at least passingly familiar with those codes. Fortunately, you can find lists of both, country and language codes, on Wikipedia. Language codes are at http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes and the country codes are at http://en.wikipedia.org/wiki/ISO_3166-1.
  4. In the eternicode library, the startDate and endDate parameters can handle a Date object, a String formatted according to the given format, or a string representative of a delta relative to today in the format {+|-}integer{d|w|m|y}", e.g., "-1d", "+6m", "-26w", or "+1y", where "d" represents days, "w" represents weeks, "m" represents months, and "y" represents years.
  5. Although it's good advice and it's possible Mr. Lincoln said this (not putting it in writing), that's not a foregone conclusion. You can find out more about what Mr. Lincoln didn't say at http://abrahamlincolnblog.blogspot.com/2010/05/lincoln-never-said-these-life-lessons.html