Weekly Widget 7 – Computes and Sliders

by Justin Meyer

Weekly Widget 7 – Computes and Sliders

Justin Meyer Learn why can.compute is the last API you will ever need as we explore using it.

posted in Development on April 24, 2013 by Justin Meyer

Computes are amazing, especially when consumed by low-level widgets. They are so amazing, I want to see them become an interoperable standard the same way that deferreds have become.

To demonstrate compute’s awesome power, I’ll build a slider first without computes and then with computes. But first a bit of widgeting theory:

IRUL (pronounced “I rule”)

Almost every widget that operates on a value needs to expose four common APIs to make it generally useful:

  • Initialize the widget with a value
  • Read the current value of the widget
  • Update the widget’s value
  • Listen to when the widget’s value changes

UI frameworks tend to have a standard IRUL approach. Take jQueryUI:


$(".slider").slider({value: 5})


$(".slider").slider("value") //-> 5





Without Computes

The following shows a basic percent slider built with CanJS and jQuery++ that exposes a similar IRUL as jQueryUI:

The slider operates on numbers between 0 and 1. Lets see its IRUL:


slider = new Slider("#slider",{
  value: 0


slider.value() //-> 0




$("#slider").bind("change", function(ev){


This slider api is serviceable, but it’s little verbose if you need to cross-bind the control’s value to the value of an object’s property. For example, consider hooking this slider value up to a task’s progress:

var slider = new Slider("#slider",{
  value: project.attr('progress')

// when the slider changes, the "progress" property updates
  project.attr('progress',slider.value() )

// when the "progress" property changes, update the slider's value
project.bind("progress",function(ev, newVal){
  slider.value( newVal )

Nine lines of code to setup and cross-bind a value to a control … Yuck! Making matters worse, if the control was removed, you MUST make sure to call project.unbind("progress") or you will have a memory leak.

Using Compute

Instead, by making the slider accept value as a can.compute you can turn those 9 lines into 3:

var slider = new Slider("#slider",{
  value: project.compute('progress')

This is because a compute is 3 API’s in one. A compute lets you:

  • read its value compute()
  • update its value compute(newValue)
  • listen to value changes compute.bind("change", handler)

Here’s that slider:

Translating values

In weekly widget 3, I showed how to use computes to translate a pagination observe’s limit and offset values into pageNum and pageCount values that the NextPrev widget needed.

Similarly, our application might contain task objects with a “progress” property ranging from 0 to 100. However, our abstract slider control requires a value ranging from 0 up to 1. We need a layer to translate from one format to the other.

We can create a compute function that translates the task’s progress values into values our slider needs. We create a compute with a getter/setter function like:

var task = new can.Observe({progress: 50}); // 50

var progress = can.compute(function(newValue){
  if(arguments.length) { // setter
    task.attr('progress', newValue * 100)
  } else {
    return task.attr('progress') / 100

new Slider("#slider",{
  value: progress

Similar to the example in the previous section, the slider will use the progress compute for all 4 parts of IRUL. To read the current value, it uses the getter by calling progress(). After changing the value, it sets the value by calling the setter with progress(newVal). And it binds on progress’ change internally, so if the compute’s value ever changes, the slider will update itself. Magical!

Check it out:

Computes derived from the DOM

What if we wanted to cross bind a compute to something other than an observe? Say … an HTML5 video element? With the upcoming CanJS 1.1.6 release, you can create a compute from any object’s value with:

can.compute(object, property, updatingEventName)

I’ll use this to create a computes for a video element’s time and duration properties and hook them up to the slider like:

var video = document.getElementById("myvideo");
// create a compute from currentTime property
var time = can.compute(video,"currentTime","timeupdate")
// create a compute for the duration
var duration = can.compute(video,"duration","durationchange");

var progress = can.compute(function(newPercent){
  // can only do anything if duration is ready
  var duration = duration();
  if(typeof duration == "number" && !isNaN(duration)){
    if(arguments.length){  // treat as a setter function
     time(newPercent * duration)
    } else { // treat as a getter function
     return time() // duration;

new Slider("#slider",{
  value: progress

Check it out:

If we update the slider to take a min and max value also as computes, we can create the slider even more succinctly:

var video = document.getElementById("myvideo");

new Slider("#slider",{
  value: can.compute(video,"currentTime","timeupdate"),
  min: can.compute(0),
  max: can.compute(video,"duration","durationchange")

Check it out:


can.compute is powerful, but its most important feature is simplifying IRUL APIs. By accepting a compute, a widget provides a single way to initialize, read, update, and listen to changes of a value.

I’d like to see computes become an interoperable standard the same way deferreds have become. If someone wants to do a lot of good, they will work with us and the Knockout folks to create a compute specification. That way, a widget made in CanJS would work with Knockout’s computes and vice-versa.

@getsetbro suggested I build a tree widget, so look out for that soon. Keep those widget suggestions coming.

blog comments powered by Disqus

Using CanJS 2.2 with StealJS and NPM

David Lueckeposted in Development, Open Source on April 23, 2015 by David LueckeCanJS can be used in many different module formats like AMD and CommonJS and package managers like Bower and NPM. In this post we will talk about using CanJS with the new StealJS and NPM.

Set Algebra with can-set

Justin Meyerposted in Development, Open Source on April 22, 2015 by Justin Meyercan-set is a client and server side JavaScript utility that makes set algebra easy. This article talks about the design goals of can-set and introduces a few cases where you might find it useful.

CanJS 2.2 Release

Justin Meyerposted in Development, Open Source, Uncategorized on April 5, 2015 by Justin MeyerCanJS 2.2 is out. It's awesome. This article covers the top 10 enhancements added since 2.1. Some of the improvements include Browserify and StealJS support, can-EVENT arguments, observable promises, and in-page automatically rendered templates. The article includes a lot of good JSBins to learn from too.

Contact Us
(312) 620-0386 | contact@bitovi.com
 or cancel