Cascading Style Sheets for LabVIEW Developers

This post is part of the LabVIEW Web Services Series, which covers topics from install LabVIEW to creating an AJAX web app powered by LabVIEW.

Cascading Style Sheets, CSS, tell the web browser how to display the HTML elements. CSS defines attributes like width, height, color, borders, orientation, font, font size, etc. By carefully adding styles to individual HTML elements we can recreate LabVIEW front panel controls.

In a previous post about creating a vertical progress bar I demonstrated very basic CSS and HTML to create a crude Vertical Progress Bar. In this post I will go in depth and show how to create a more exact match to the LabVIEW look. Of course, I will link to the code and you can have a go at customizing the control yourself.

    There are many great tutorials on learning CSS and HTML, so I will not recreate them here and instead give you a short list.
  • Mozilla Developer Network has all the technical specifications for the current and up coming web technologies.
  • Code School HTML and CSS The first few lessons are free and a great resource to learn quickly.
  • CSS Tricks is one of my favorite sites and I learn plenty from this site on a weekly basis.

GET ON WITH IT!

If you want to check out the final Vertical Progress Bar then head over to my sketch on codepen.io.

See the Live Demo

This code is just a quick sketch and should not be used on a production site. It does demonstrate all the necessary elements of a working HTML control. The slider responds to mouse dragging events, as well as touch screen dragging events. The buttons update the progress bar fill level and the indicator number. There is also a scale that lines up with the fill level. These are all created with HTML and CSS and manipulated with JavaScript.

I think the best explanation is demonstration so go check out the working demo, fork it, then extend and customize it. Leave a comment with a link to your version and let us know what features you have added.

The HTML

<div id="slide1" class="slide">
  <div class="slide-label">Slide</div>
  <div class="slide-ticks">
    <table>
      <tr>
        <td>100-</td>
      </tr>
      <tr>
        <td>75-</td>
      </tr>
      <tr>
        <td>50-</td>
      </tr>
      <tr>
        <td>25-</td>
      </tr>
      <tr>
        <td>0-</td>
      </tr>
    </table>
  </div>
  <div class="slide-border">
    <div class="slide-fill" style="top:30px;height:70px;">
    </div>
    <div class="slide-pointer" style="top:30px" ></div>
  </div>
  <div class="slide-indicator">
    <div class="slide-indicator-controls">
      <div class="slide-indicator-button increment no-highlight">+</div>
      <div class="slide-indicator-button decrement no-highlight">-</div>
    </div>
    <div class="slide-indicator-numeric slide-indicator-control">
      70
    </div>
  </div>
</div>

<button id="button1" style="float:left;">Update Global</button>

<div id="slide2" class="slide">
  <div class="slide-label">Global Slide Value</div>
  <div class="slide-ticks">
    <table>
      <tr>
        <td>100-</td>
      </tr>
      <tr>
        <td>75-</td>
      </tr>
      <tr>
        <td>50-</td>
      </tr>
      <tr>
        <td>25-</td>
      </tr>
      <tr>
        <td>0-</td>
      </tr>
    </table>
  </div>
  <div class="slide-border">
    <div class="slide-fill" style="top:30px;height:70px;">
    </div>
  </div>
  <div class="slide-indicator">
    <div class="slide-indicator-numeric">
      70
    </div>
  </div>
</div>

The CSS

body {
  background-color:#d7d7d7;
  font-family: Tahoma, sans-serif;
}

button {
  cursor: pointer;
}

.slide {
  float:left;
  margin-left:40px;
  margin-bottom:15px;
}

.slide-label {
  margin-bottom:5px;
}

.slide-ticks {
  position:relative;
  text-align:right;
  line-height:20px;
  float:left;
  margin-left:-40px;
  margin-top:-10px;
  width:25px;
  height:100px;
}

.slide-border {
  position:relative;
  width:25px;
  height:100px;
  border:1px solid #333;
  border-radius:5px;
  background-color:#777;
  box-shadow:inset 0 0 2px #333;
  margin-bottom:15px;
}

.slide-fill {
  position:absolute;
  width:25px;
  background-color:blue;
 
}

.slide-pointer {
  position:absolute;
  margin-top:-6px;
  left:2px;
  width:33px;
  height:14px;
  background-color:#eee;
  border-radius:10px 5px 5px 10px;
  background-image: linear-gradient(to bottom, #fff, #777);filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=#eee, endColorstr=#707070);
  box-shadow:1px 1px 1px 1px black;
  cursor: move;
}

.slide-indicator {
  margin-left:-25px;
}

.slide-indicator-numeric {
  float:left;
  width:50px;
  padding:2px;
  border:2px inset lightgray;
  box-shadow:inset 0 0 2px black;
  background-color:lightgray;
}

.slide-indicator-control {
  background-color:white;
}

.slide-indicator-controls {
  float:left;
  margin-right:3px;
}

.slide-indicator-button {
  height:12px;
  width:18px;
  line-height:11px;
  text-align:center;
  border:1px solid gray;
  border-radius:2px;
  box-shadow:1px 1px 1px 1px black;
  background-color:lightgray;
  cursor: pointer;
}

.no-highlight {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -khtml-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

The JavaScript

//button
var slide = $("#slide1 .slide-pointer");
var fill = $("#slide1 .slide-fill");
var indicator = $("#slide1 .slide-indicator-numeric");
var up = $("#slide1 .increment");
var down = $("#slide1 .decrement");
var button = $("#button1");
var height;
var max = 100;
var min = 0;

//Copy Control Value to Indicator Value
$(button).on("click", function( event, ui ) {
  height = parseInt($(slide).position().top);
  $("#slide2 .slide-fill").css({"top":height, "height":max-height});
  $("#slide2 .slide-indicator-numeric").text(max-height);
});

//Increment control value by 1
$(up).on("click", function( event, ui ) {
  height = parseInt($(slide).position().top);
  height = height - 1;
  if(height < min){ height = min; }
  if(height > max){ height = max; }
  $(slide).css({'top':height})
  $(fill).css({'top':height, 'height':max-height});
  $(indicator).text(max-height);
});
//decrement control value by 1
$(down).on("click", function( event, ui ) {
  height = parseInt($(slide).position().top);
  height = height + 1;
  if(height < min){ height = min; }
  if(height > max){ height = max; }
  $(slide).css({'top':height})
  $(fill).css({'top':height, 'height':max-height});
  $(indicator).text(max-height);
});

//Update control value based on slider position
$(slide).draggable({
  axis: 'y',
  containment: '#slide1',
  stop: function(event, ui) {
    if(ui.position.top>max) {  
      $(slide).animate({"top": "100px"},600);
    }
    else if(ui.position.top<0) {
      $(slide).animate({"top": "0px"},600);
    }                                             
  }
}).on( "drag", function( event, ui ) {
  height = parseInt($(slide).position().top);
  if(height < min){ height = min; }
  if(height > max){ height = max; }
    $(fill).css({'top':height, 'height':max-height});
    $(indicator).text(max-height);
});

//Enable Touch Pad Devices
var startY;
slide[0].addEventListener('touchstart', function(e) {
  e.preventDefault();
  height = parseInt($(slide).position().top);
  startY = e.targetTouches[0].pageY;
}, false);
slide[0].addEventListener("touchmove", function(e) {
  e.preventDefault();
  var diffY = e.changedTouches[0].pageY - startY;
  var newHeight = height + diffY;
  if(newHeight < min){ newHeight = min; }
  if(newHeight > max){ newHeight = max; }
  slide.css({'top':newHeight});
  $(fill).css({'top':newHeight, 'height':max-newHeight});
  $(indicator).text(max-newHeight);
}, false);
slide[0].addEventListener("touchend", function(e) {
}, false);

Read More