4
\$\begingroup\$

I developed this tickets booking system recently to showcase HTML5 features. I would like you to review it from the UI/UX perspective. Also if there is something more that I can add.

Is it good enough?

JS/HTML5/CSS3:

$(document).ready(function(){
        
        // first check the movies already booked
        checkMoviesBooked();
        
        // apply jQuery UI Redmond theme to 'Book Tickets' button
        $("#submit").button();
        
        // calculateTotalPrice on keyup or on change of movie/date/tickets
        $("#movie_name, #date, #tickets_quantity").keyup(calculateTotalPrice);
        $("#movie_name, #date, #tickets_quantity").change(calculateTotalPrice);
        
        // on form submit
        $("#book_tickets").submit(function(event){
            
            // prevent on submit page refresh
            event.preventDefault();
            
            // check locally stored data
            if(window.localStorage){
                var moviesListJson = localStorage.getItem('movies_list');
                var movies_list = moviesListJson ? JSON.parse(moviesListJson) : [];
                var movie = $("#movie_name").val();
                movies_list.push(movie);
                localStorage.setItem('movies_list', JSON.stringify(movies_list));
            }
            
            // clear the form					
            $( '#book_tickets' ).each(function(){
                this.reset();
            });
    
            // reset (enter data first) message
            $("#total_price").html("(enter data first)");
            
            // update movies booked list
            checkMoviesBooked();
        });
        
        // set minimum date in datepicker as today
        var today = new Date().toISOString().split('T')[0];
        document.getElementsByName("date")[0].setAttribute('min', today);
        
    });
    
    function calculateTotalPrice(){
        if($("#tickets_quantity").val() != "" && $("#movie_name").val() != "" && $("#date").val() != ""){
            if(window.Worker){
                // create web worker
                var blob = new Blob(
                    [document.querySelector("#worker").textContent],
                    {type: 'text/javascript'});
                var worker = new Worker(window.URL.createObjectURL(blob));
                
                worker.onmessage = function(event){
                    $("#total_price").html(event.data);
                }
                worker.onerror = function(errorObject){
                    $("#total_price").html("Error: " + errorObject.message);
                }
                
                // get date
                var date = new Date($('#date').val());
                
                // get day
                var day = date.getDay();
                
                // get number of booked shows
                var number_booked_shows;
                if(window.localStorage){
                    // check if movies_list is present already
                    if(localStorage.getItem('movies_list')){
                        var movieListJson = localStorage.getItem('movies_list');
                        var movies_list = JSON.parse(movieListJson);
                        number_booked_shows = movies_list.length;                    
                    }
                    else
                        number_booked_shows = 0;
                }
                
                // send JSON data to worker						
                var jsonData = {'day': day,  'number_booked_shows': number_booked_shows, 'tickets_quantity': Number($("#tickets_quantity").val())};
                worker.postMessage(jsonData);
            }
        }
    }
    
    // fetch details of movies booked
    function checkMoviesBooked(){
        $("#movies_list").html("<span id='none'>(none)</span>");
        if(window.localStorage){
            if(localStorage.getItem('movies_list')){
                $("#none").remove();
                var movieListJson = localStorage.getItem('movies_list');
                var movies_list = JSON.parse(movieListJson);
                var sr_no = 0;
                $.each(movies_list,function(key,value){
                    $("#movies_list").append(++sr_no + ". " + value + "<br>");
                });
            }
        }
    }
html{
  height: 100%;
}

body{
  font-family: "Arial", Helvetica, sans-serif;
  position: relative;
}

#container{
  text-align: center;
  position: relative;
  height: 100%;
}

#movies_booked, #form{
  display: inline-block;
  width: 40%;
  height: 100%;
  margin: 0 auto;
  vertical-align:text-top;
}

fieldset, #movies_booked{
  border:1px solid #AED0EA;
  border-radius:8px;
  box-shadow:0 0 10px #D7EBF9;
}

legend, #disount_title{
  color: #2779AA;
  font-size: 120%;
  text-align: center;
  background-color: white;
}

p{
  overflow: hidden;
}

label{
  width: 50%;
  text-align: right;
  float: left;
  clear: both;
  color: #2779AA;
}

p input, p select{
  width: 40%;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
  float: left;
  margin-left: 5%;
  margin-right: 5%;
  color: #2779AA;
}

#theaters, #total_price, #movies_list, li, #perTicketPrice, #note{
  color: #2779AA;
}

p span, li{
  text-align: left;
}

#submit_wrapper{
  text-align: center;
}

#submit{
  font-size: 13px;
}

#discount, #perTicketPrice{
  text-align: left;
}

#dateNote{
  font-size: 9px;
}
<div id="container">
  <div id="form">
    <form id="book_tickets">
      <fieldset>
        <legend>Booking Details</legend>

        <p>
          <label for="movie_name">Movie</label>
          <select id="movie_name" name="movie_name" required autofocus>
            <option></option>
            <option value="Movie 1">Movie 1</option>
            <option value="Movie 2">Movie 2</option>
            <option value="Movie 3">Movie 3</option>
          </select>
        </p>

        <p>
          <label for="theaters">Theaters</label>
          <select id="theaters" required>
            <option></option>
            <option value="Theater 1">Theater 1</option>
            <option value="Theater 2">Theater 2</option>
            <option value="Theater 3">Theater 3</option>
          </select>
        </p>

        <p>
          <label for="date">Date<br/><span id="dateNote">Firefox does not have a HTML5 datepicker <a href="https://support.mozilla.org/en-US/questions/986096">yet</a>.</span></label>
          <input type="date" name="date" id="date" min="today" required />
        </p>

        <p>
          <label for="email">Email</label>
          <input type="email" name="email" id="email" required />
        </p>

        <p>
          <label for="tickets_quantity"># Tickets</label>
          <input type="number" min="1" name="tickets_quantity" id="tickets_quantity" required />
        </p>

        <p>
          <label>Total Price</label>
          <span id="total_price">(enter data first)</span>
        </p>
        <div id="submit_wrapper">
          <input type="submit" id="submit" value="Book Tickets" />
        </div>
      </fieldset>
    </form>
    <p id="perTicketPrice">Per ticket price = &#x20b9; 100.00</p>
    <p id="discount">
      <span id="disount_title">Discounts:</span>
    <ul>
      <li>5% discount if show is on weekday</li>
      <li>10% discount if number of booked shows >= 10</li>
    </ul>
    </p>
</div>

<fieldset id="movies_booked">
  <legend>Movies Booked Till Date</legend>
  <span id="movies_list"></span>
</fieldset>		    	

</div>


<script id="worker" type="javascript/worker">
        self.onmessage = function msgWorkerHandler(event){
            var jsonString = event.data;

            var day = jsonString.day;
            var number_booked_shows = jsonString.number_booked_shows;
            var tickets_quantity = jsonString.tickets_quantity;

            // set price of each ticket as Rs. 100
            var totalPrice = tickets_quantity * 100;

            // 5% discount if on weekday
            if(day > 0 && day < 6){
                totalPrice = totalPrice - 0.05 * totalPrice;
            }

            // 10% discount if number of booked shows >= 10
            if(number_booked_shows >= 10){
                totalPrice = totalPrice - 0.10 * totalPrice;
            }

           	postMessage("&#x20b9; " + totalPrice);
        }
</script>

\$\endgroup\$
  • \$\begingroup\$ It seems quite good but you even can make your code more optimized and with nice look UI by using Bootstrap CSS. You can find it here Bootstrap \$\endgroup\$ – Bazzinga... May 23 '14 at 10:36
6
\$\begingroup\$

UX (User Experience)

I'll start with a few possible improvements to the User Experience functionality.

  1. In order to offer the best possible interface you have to provide the same or similar experience in all browsers, simply listing something isn't supported, in my mind, is lazy and not a great UX/UI option. I'd look into webshims or a similar polyfill. Basically, you should provide a fallback or functional date picker in browsers that don't support the HTML5 feature. Check the caniuse.com and html5please.com to see support levels and polyfill options for all these HTML5 features.

  2. There is no mobile optimized content or media queries. There is an increasing number of movie goers that are utilizing mobile devices in order to purchase tickets. Not providing mobile optimized layout is a huge draw back to the UX for half the people that would utilize this tool. (source)

  3. I'd look into Flying Focus to provide tabbed and arrow key users the ability to see where they are going when tabbing through fields.

  4. I'd look into <datalist>'s (webshims provides support for it) to use in place of your <select>'s, but since support is shaky for this HTML5 feature you could also provide a search box in the dropdown similar to Select2 in browsers that don't support it or if you choose not to polyfill.


UI (User Interface)

To me, this tool is a bit bland, it has a fairly antiquated look/feel. I'll offer a few suggestions that may help you achieve a better UI. That said, it doesn't look bad, just old.

  • Since <legend> is difficult to style, I would visually hide it (see .visuallyhidden style below) and apply an <h2> or some other form of heading that is easier to style.

css

.visuallyhidden {
     border: 0;
     clip: rect(0,0,0,0);
     clip: rect(0 0 0 0);
     height: 1px;
     overflow: hidden;
     position: absolute;
     width: 1px;
     margin: -1px;
     padding: 0}

html

<legend class="visuallyhidden">Booking Details</legend>
<h2>Booking Details</h2>
  • A possible option to bolster the appearance is to provide some contrast using color, having the border to separate the modules is fine, but perhaps you could look into using background as an additional way to ease some of that excessive white color. This Fiddle is a quick example of using color to separate items in a little more up to date fashion.

  • I'd recommend using a <div> to do the styling for the form areas, and having the <fieldset>'s styles removed. (see fieldset normalize & reset below)

css

/* from http://necolas.github.io/normalize.css/ */
fieldset {
    border: 1px solid #c0c0c0;
    margin: 0 2px;
    padding: 0.35em 0.625em 0.75em}
    /* remove default styles */
    fieldset {
        border: 0;
        margin: 0;
        padding: 0}
  • I'd add a greater padding on the form fields to offer interaction with touch screens. The code below should suffice.

    input,select{padding:7px}

  • I'd also add some transitions transition:all .2s ease; to ease changes to different form states. :focus, :hover, onmouseenter, onmouseleave or something of the sort.

  • I'd also take advantage of the placeholder option and tabindex attributes. A good rule of thumb is the <label> is for the descriptor, the placeholder is for an example.

  • I won't touch base on the semantics too much, but I will say you should add ARIA support.

I like the idea, I just think the concept fell a little short. Providing a simple method of searching movie options in a easy to use form is great; however, the concept isn't used very often due to people being more visual these days, using forms for searchable content works in some cases, but I'm not entirely sure it is suitable for this particular instance.

Take a look at Flixter, I love the UI/UX on both the mobile and desktop versions. It's very intuitive and easy to navigate, seeing what's playing shows the actual movie cover instead of having a bland form to interact with.


Links list:

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.