Search code examples
javascripthtmlqtipqtip2plottable

How to make qtip2 respond to dynamic user input


I have the following working code. It takes the input data and display the histogram based on a threshold.

"use strict";

var histograms,
  thresholds = [];

var input_data = [{
  "threshold": 1.5,
  "histograms": [{
    "sample": "Sample1",
    "values": [{
      "score": 6.7530200000000002,
      "celltype": "Bcells"
    }, {
      "score": 11.432763461538459,
      "celltype": "DendriticCells"
    }, {
      "score": 25.823089615384621,
      "celltype": "Macrophages"
    }, {
      "score": 9.9911211538461551,
      "celltype": "gdTCells"
    }, {
      "score": 7.817228076923076,
      "celltype": "StemCells"
    }, {
      "score": 17.482806923076922,
      "celltype": "StromalCells"
    }, {
      "score": 29.335427692307697,
      "celltype": "Monocytes"
    }, {
      "score": 28.914959615384621,
      "celltype": "Neutrophils"
    }, {
      "score": 13.818888461538467,
      "celltype": "NKCells"
    }, {
      "score": 9.5030688461538464,
      "celltype": "abTcells"
    }]
  }]
}, {
  "threshold": 2,
  "histograms": [{
    "sample": "Sample1",
    "values": [{
      "score": 5.1335499999999996,
      "celltype": "Bcells"
    }, {
      "score": 16.076072499999999,
      "celltype": "DendriticCells"
    }, {
      "score": 46.182032499999998,
      "celltype": "Macrophages"
    }, {
      "score": 6.5895700000000001,
      "celltype": "gdTCells"
    }, {
      "score": 5.3218800000000002,
      "celltype": "StemCells"
    }, {
      "score": 53.643625,
      "celltype": "StromalCells"
    }, {
      "score": 85.1618225,
      "celltype": "Monocytes"
    }, {
      "score": 55.559129999999996,
      "celltype": "Neutrophils"
    }, {
      "score": 7.6717524999999984,
      "celltype": "NKCells"
    }, {
      "score": 6.3277800000000006,
      "celltype": "abTcells"
    }]
  }]
}];

processData(input_data);

function processData(data) {
  histograms = data[0].histograms.map(function(data) {
    return {
      title: data.sample,
      dataset: new Plottable.Dataset(),
      dataByThreshold: {},
      load: function(threshold) {
        this.dataset.data(this.dataByThreshold[threshold]);
      }
    };
  });

  data.forEach(function(data) {
    var threshold = data.threshold;
    thresholds.push(threshold);
    data.histograms.forEach(function(histogram, i) {
      histograms[i].dataByThreshold[threshold] = histogram.values;
    });
  });

 

  // Here we generalize the slide bar maximum threshold
  $('#threshold').attr('max', thresholds.length - 1);
  updateDatasets(thresholds[0]);
  buildPlots();
  updateThreshold();



}

 

$('#threshold').change(updateThreshold);

function updateThreshold() {

  // This is where the user input updating slider
  // takes place and where the QTIP is in action.

  var thresholdIndex = parseInt($('#threshold').val(), 10);
  $("#foldchange_threshold").html(thresholds[thresholdIndex]);
  updateDatasets(thresholds[thresholdIndex]);
  $(".tooltipped rect").qtip({
    overwrite: true,
    position: {
      my: "bottom middle",
      at: "top middle"
    },
    style: {
      classes: "qtip-light"
    }
  });
}

function updateDatasets(threshold) {
  histograms.forEach(function(histogram) {
    histogram.load(threshold);
  });
}

function buildPlots() {
  var $histogramContainer = $('#sample-histograms');

  histograms.forEach(function(histogram, index) {
    var elementId = "sample-histogram-" + index;

    
    $(document.createElementNS('http://www.w3.org/2000/svg', 'svg'))
      .css({
        width: '200px',
        height: '200px',
        display: 'inline-block'
      })
      .attr('id', elementId)
      .appendTo($histogramContainer);

    plotSampleHistogram(histogram.title, histogram.dataset, '#' + elementId);
  });

}

function plotSampleHistogram(title, dataset, targetElement) {
  var xScale = new Plottable.Scales.Category(),
    yScale = new Plottable.Scales.Linear(),
    colorScale = new Plottable.Scales.Color();


    var xAxis = new Plottable.Axes.Numeric(xScale, "bottom"),
        yAxis = new Plottable.Axes.Numeric(yScale, "left"),
        titleLabel = new Plottable.Components.TitleLabel(title);

    yScale.domainMin(0);

    var plot = new Plottable.Plots.Bar()
      .addDataset(dataset)
      .x(function(d) { return d.celltype; }, xScale)
      .y(function(d) { return d.score; }, yScale)
      .attr("fill", function(d) { return d.celltype; }, colorScale)
      .attr("title", function(d) { return '<div class="bartip">' + d.celltype + " (" + d.score.toFixed(2) + ') </div>'; })
      .addClass("tooltipped");



    new Plottable.Components.Table([
        [null, titleLabel],
        [yAxis, plot],
        [null, xAxis]
    ]).renderTo(targetElement);
}

function drawHistogramLegend(targetElement) {
    new Plottable.Components.Legend(colorScale)
        .renderTo(targetElement);
}
<html>

<head>
  <link href="https://cdnjs.cloudflare.com/ajax/libs/plottable.js/1.15.0/plottable.css" rel="stylesheet" />
  <link href="https://cdnjs.cloudflare.com/ajax/libs/qtip2/2.2.1/basic/jquery.qtip.css" rel="stylesheet" />


</head>

<body>

  <!-- Display the sliding bar -->
  <input id="threshold" type="range" min="0" max="1" step="1" value="0" />
  <br>

  <!-- Show foldchange threshold -->
  <div id="foldchange_threshold" style="display: inline-block; align:center;"></div>

 

  <!-- Show histograms -->
  <div id="sample-histograms"></div>

  <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/plottable.js/1.15.0/plottable.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/qtip2/2.2.1/basic/jquery.qtip.js"></script>
 



</body>

</html>

If you run the code you can notice that the histogram will change as you drag the slider. AND a tool tip will appear if you place your mouse in one of the histogram bar.

The problem I'm facing is that the value of the tool tip does not change upon the user input to the sliding bar. For example with threshold=1.5 the Bcell score is 6.75 and threshold=2, the score is 5.13. This is not reflected in the tool tip. How can resolve this issue?


Solution

  • It looks like QTip2 modifies the "title" attribute in some way, cacheing the previous information under "oldtitle". I got it to work by switching the title to a different attribute:

    plot.attr("qtip2-title", function(d) {
      return '<div class="bartip">' + d.celltype + " (" + d.score.toFixed(2) + ') </div>';
    });
    

    Then, we tell QTip2 to look up the "qtip2-title" property for the tooltip text:

    $(".tooltipped .content rect").qtip({
    
      // ... other setup here...
    
      content: {
        text: function() {
          return $(this).attr("qtip2-title");
        }
      }
    });
    

    (Note the use of .content to avoid selecting unwanted rectangles).

    Also, I noticed that currently xAxis is an Axes.Numeric, which will not work with Scales.Category. Use Axes.Category instead.

    "use strict";
    
    var histograms,
      thresholds = [];
    
    var input_data = [{
      "threshold": 1.5,
      "histograms": [{
        "sample": "Sample1",
        "values": [{
          "score": 6.7530200000000002,
          "celltype": "Bcells"
        }, {
          "score": 11.432763461538459,
          "celltype": "DendriticCells"
        }, {
          "score": 25.823089615384621,
          "celltype": "Macrophages"
        }, {
          "score": 9.9911211538461551,
          "celltype": "gdTCells"
        }, {
          "score": 7.817228076923076,
          "celltype": "StemCells"
        }, {
          "score": 17.482806923076922,
          "celltype": "StromalCells"
        }, {
          "score": 29.335427692307697,
          "celltype": "Monocytes"
        }, {
          "score": 28.914959615384621,
          "celltype": "Neutrophils"
        }, {
          "score": 13.818888461538467,
          "celltype": "NKCells"
        }, {
          "score": 9.5030688461538464,
          "celltype": "abTcells"
        }]
      }]
    }, {
      "threshold": 2,
      "histograms": [{
        "sample": "Sample1",
        "values": [{
          "score": 5.1335499999999996,
          "celltype": "Bcells"
        }, {
          "score": 16.076072499999999,
          "celltype": "DendriticCells"
        }, {
          "score": 46.182032499999998,
          "celltype": "Macrophages"
        }, {
          "score": 6.5895700000000001,
          "celltype": "gdTCells"
        }, {
          "score": 5.3218800000000002,
          "celltype": "StemCells"
        }, {
          "score": 53.643625,
          "celltype": "StromalCells"
        }, {
          "score": 85.1618225,
          "celltype": "Monocytes"
        }, {
          "score": 55.559129999999996,
          "celltype": "Neutrophils"
        }, {
          "score": 7.6717524999999984,
          "celltype": "NKCells"
        }, {
          "score": 6.3277800000000006,
          "celltype": "abTcells"
        }]
      }]
    }];
    
    processData(input_data);
    
    function processData(data) {
      histograms = data[0].histograms.map(function(data) {
        return {
          title: data.sample,
          dataset: new Plottable.Dataset(),
          dataByThreshold: {},
          load: function(threshold) {
            this.dataset.data(this.dataByThreshold[threshold]);
          }
        };
      });
    
      data.forEach(function(data) {
        var threshold = data.threshold;
        thresholds.push(threshold);
        data.histograms.forEach(function(histogram, i) {
          histograms[i].dataByThreshold[threshold] = histogram.values;
        });
      });
    
    
    
      // Here we generalize the slide bar maximum threshold
      $('#threshold').attr('max', thresholds.length - 1);
      updateDatasets(thresholds[0]);
      buildPlots();
      updateThreshold();
    
    
    
    }
    
    
    
    $('#threshold').change(updateThreshold);
    
    function updateThreshold() {
    
      // This is where the user input updating slider
      // takes place and where the QTIP is in action.
    
      var thresholdIndex = parseInt($('#threshold').val(), 10);
      $("#foldchange_threshold").html(thresholds[thresholdIndex]);
      updateDatasets(thresholds[thresholdIndex]);
      $(".tooltipped .content rect").qtip({
        overwrite: true,
        position: {
          my: "bottom middle",
          at: "top middle"
        },
        style: {
          classes: "qtip-light"
        },
        content: {
          text: function() {
            return $(this).attr("qtip2-title");
          }
        }
      });
    }
    
    function updateDatasets(threshold) {
      histograms.forEach(function(histogram) {
        histogram.load(threshold);
      });
    }
    
    function buildPlots() {
      var $histogramContainer = $('#sample-histograms');
    
      histograms.forEach(function(histogram, index) {
        var elementId = "sample-histogram-" + index;
    
    
        $(document.createElementNS('http://www.w3.org/2000/svg', 'svg'))
          .css({
            width: '200px',
            height: '200px',
            display: 'inline-block'
          })
          .attr('id', elementId)
          .appendTo($histogramContainer);
    
        plotSampleHistogram(histogram.title, histogram.dataset, '#' + elementId);
      });
    
    }
    
    function plotSampleHistogram(title, dataset, targetElement) {
      var xScale = new Plottable.Scales.Category(),
        yScale = new Plottable.Scales.Linear(),
        colorScale = new Plottable.Scales.Color();
    
    
        var xAxis = new Plottable.Axes.Category(xScale, "bottom"),
            yAxis = new Plottable.Axes.Numeric(yScale, "left"),
            titleLabel = new Plottable.Components.TitleLabel(title);
    
        xAxis.tickLabelAngle(-90)
        yScale.domainMin(0);
    
        var plot = new Plottable.Plots.Bar()
          .addDataset(dataset)
          .x(function(d) { return d.celltype; }, xScale)
          .y(function(d) { return d.score; }, yScale)
          .attr("fill", function(d) { return d.celltype; }, colorScale)
          .attr("qtip2-title", function(d) { return '<div class="bartip">' + d.celltype + " (" + d.score.toFixed(2) + ') </div>'; })
          .addClass("tooltipped");
    
    
    
        new Plottable.Components.Table([
            [null, titleLabel],
            [yAxis, plot],
            [null, xAxis]
        ]).renderTo(targetElement);
    }
    
    function drawHistogramLegend(targetElement) {
        new Plottable.Components.Legend(colorScale)
            .renderTo(targetElement);
    }
    <html>
    
    <head>
      <link href="https://cdnjs.cloudflare.com/ajax/libs/plottable.js/1.15.0/plottable.css" rel="stylesheet" />
      <link href="https://cdnjs.cloudflare.com/ajax/libs/qtip2/2.2.1/basic/jquery.qtip.css" rel="stylesheet" />
    
    
    </head>
    
    <body>
    
      <!-- Display the sliding bar -->
      <input id="threshold" type="range" min="0" max="1" step="1" value="0" />
      <br>
    
      <!-- Show foldchange threshold -->
      <div id="foldchange_threshold" style="display: inline-block; align:center;"></div>
    
    
    
      <!-- Show histograms -->
      <div id="sample-histograms"></div>
    
      <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/plottable.js/1.15.0/plottable.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/qtip2/2.2.1/basic/jquery.qtip.js"></script>
      <script src="histo.js"></script>
    
    
    
    </body>
    
    </html>

    Hopefully this works for you! Glad to see you're using Plottable.

    P.S. We're working on getting a more detailed tooltip tutorial for the website. Stay tuned.