Javascript: Difference between revisions

From Bitpost wiki
(Created page with "=== Basics === {| class="wikitable" ! Jquery |}<!-- ================================================================================================================...")
 
No edit summary
Line 4: Line 4:




{| class="mw-collapsible mw-collapsed wikitable"
! Bootstrap
|-
|
{| class="mw-collapsible mw-collapsed wikitable"
! Single Page Application
|-
| css:
<pre>
  .mdm-scrollable-div
  {
    height: 100%;
    overflow: auto;
  }
</pre>
html:
<pre>
  <-- header -->
  <div class="frame" id="frame1top">
    <div class="mdm-scrollable-div">
      ...
    </div>
  </div>
  <-- footer -->
</pre>
js:
<pre>
  // We need to adjust layout on resize, in ready handler, and programmatically as needed.
  $(window).resize(adjustLayout).resize();
  $( document ).ready(function() {
    // We need to adjust in ready handler, on resize, and programmatically as needed
    adjustLayout();
    /* your other page load code here*/
  });
  function adjustLayout(){
      // ----------------------
      // Fundamental dimensions
      var hh = $('#theheader').outerHeight();
      var fh = $('#thefooter').outerHeight();
      var workspace_height = $(window).height() - hh - fh;
      var workspace_width = $(window).width();
      // ----------------------
     
      var cols = 1;
      var col_gap = 16;
     
      // Margin is based on percent of one column width, so it goes to zero before column width does.  :-)
      // Width is based on total width minus margins, then split between columns.
      var margin = ($(window).width() / cols) * .04;
      var w = ($(window).width() - (margin * (cols + 1))) / cols;
     
      var h1 = workspace_height;
     
      $('#frame1top').css({
          display:'block',
          position:'absolute',
          left: margin * 1 + w * 0,
          top: hh,
          width: w,
          height: h1
      });
  }
</pre>
|}
{| class="mw-collapsible mw-collapsed wikitable"
! Button handlers
|-
| Buttons can be represented with labels; in that case, use a click handler:
<pre>
    jQuery('<label id="'+cycle.apsID+'" class="btn">MahButt</label>')
    .appendTo('#'+chartid+'-apsbar')
    .on('click', function(){
      $(location).attr('href','/v1/parameters/'+this.id+'.html');
    });
</pre>
You can also get the checked state, and prevent the button from being pressed, among other things:
<pre>
      jQuery('<label id="'+cycle.run+'" ...
      .on('click', function(e){
        var run = this.id;
        // Look for the (label->input)
        if (this.firstChild.checked)
        ...
        // To prevent checked/pressed state if desired:
        e.stopPropagation();
      } 
</pre>
Button bars are represented by labels wrapped around inputs:
<pre>
    <div class="btn-group live-buttons" data-toggle="buttons"><div class="btn-group"><label class="btn"><input type="checkbox">text...
</pre>
In that case use a change handler on the input:
<pre>
      var label = jQuery('<label class="btn btn-sm btn-'+color+'"></label>').appendTo(action_button_bar);
      var input = jQuery('<input class="run-'+cmd+'" type="checkbox" autocomplete="off" value="'+cycle.run+'">').appendTo(label)
      .on('change', cycle.owned? function(){
        patchPick(this.value,'{ "action" : "run-hold"      }');
      } : function() {
        patchPick(this.value,'{ "action" : "run-buy"        }');
      });
      var text = jQuery('<span class="glyphicon glyphicon-'+glyph+'"></span> <span class="hidden-xs">'+cmd+'</span>').appendTo(label);
</pre>
If you just need clicks from the button bar, you do NOT NEED input:
<pre>
    <div class="btn-group live-buttons" data-toggle="buttons"><div class="btn-group"><label class="btn">text...
</pre>
Then you can put the change handler right on the label:
<pre>
    var applybar = jQuery('<div id="applybar-'+cycle.run+'" class="btn-group pull-right" data-toggle="buttons" />');
    var apply_button = jQuery('<label id="apply-'+cycle.run+'" class="btn btn-sm btn-moneygreen"><span class="glyphicon glyphicon-ok"></span><span class="hidden-xs"> Apply</span></input></label>')
    .on('click', function(e) {   
   
        // Do work
        e.stopPropagation();
    })
    .appendTo(applybar)
    ;
</pre>
|}
{| class="mw-collapsible mw-collapsed wikitable"
! Programmatically pressing a button
|-
| You MUST DO TWO THINGS:
* Set the active class on the button/label
* Set the input to checked
<label class="btn active">
  <input type="checkbox" autocomplete="off" checked>
  Click me
</label>
|}
{| class="mw-collapsible mw-collapsed wikitable"
! Handling the UNCHECK event on a pushbutton
|-
| Again you MUST SET BOTH active and checked STATES PROPERLY when creating the button (see above).  Do not set either if the button is unpressed; set both if it is pressed.
Then you can use a single change event:
  $("input[type='checkbox'][class='run-hold-on-buy'    ]").change(function() { patchPick(this.value,'{ "action" : "'+(this.checked?'hold-on-buy-on'    :'hold-on-buy-off'    )+'"}');  });
|}
{| class="mw-collapsible mw-collapsed wikitable"
! Collapsible panel
|-
|
* Use a unique id on content div, data-target to connect panel header to content, mdm-panel-collapser so javascript can find icon, abt-scrollable-panel for margin.
* HTML for each panel (starts collapsed)
<pre>
    <div class="panel panel-default abt-scrollable-panel">
      <div class="panel-heading collapsed" data-toggle="collapse" data-target="#my-content-block-one">Software development skills<span class="mdm-panel-collapser text-muted glyphicon glyphicon-chevron-down pull-right"></span></div>
      <div class="collapse" id="my-content-block-one">
        <div class="mdm-panel-body">
 
        <!-- CONTENT, can include another nested panel, just add .mdm-nested-panel to class; example: -->
 
          <div class="panel panel-default mdm-scrollable-panel mdm-nested-panel">
            <div class="panel-heading collapsed mdm-job-panel-heading" data-toggle="collapse" data-target="#toshiba-job"><p><strong>Senior Developer and Offshore Manager</strong><i> - Toshiba Global Commerce Solutions, Inc.</i><span class="mdm-panel-collapser text-muted glyphicon glyphicon-chevron-up pull-right"></span></p><p><small>April 2014 – August 2015</small></p></div>
            <div class="collapse in" id="toshiba-job">
              <div class="mdm-panel-body">
                <!-- SUBCONT(IN)ENT im so funny -->
              </div>
            </div>
          </div>
         
        </div>
      </div>
    </div>
</pre>
* For an expanded panel, simply change icon from down to up, and add "in" to content div:
<pre>
    ... glyphicon-chevron-up ...
    <div class="collapse in mdm-panel-body" id="collapseOrderItems1">
</pre>
* Define Javascript collapser once on load
  $('.collapse').on('hide.bs.collapse show.bs.collapse',
    toggleCollapser
  );
* CSS
    .mdm-nested-panel {
      margin-top: 1em;
      margin-left: 1em;
    }
    collapse.mdm-panel-body collapsing.mdm-panel-body {
        margin: 1em;
    }
* Common reusable function:
  function toggleCollapser(e) {
      $(e.target)
          .prev('.panel-heading')
          .find('.mdm-panel-collapser')
          .toggleClass('glyphicon-chevron-down glyphicon-chevron-up');
 
    // Prevent bubble up to any parent collapsers.
    // This allows nested collapsers, whoop.
    e.stopPropagation();
  }
|}
|}
<!--
===========================================================================================================================================================================================================================================================================================
-->
{| class="mw-collapsible mw-collapsed wikitable"
! D3
|-
|
{| class="mw-collapsible mw-collapsed wikitable"
! ALWAYS initialize incoming data
|-
| Hard-earned data-import lessons:
* tsv() will do complete date formatting, so when you remove it to use JSON directly, you HAVE TO convert JSON timestamps of ANY kind to full Javascript date objects:
  var data = [{"date":"2017-04-01T04:00:00.000Z",...
  // MDM WE MUST MASSAGE DATA HERE
  // JSON VALUES ARE ALWAYS STRINGS, we need to change to Javascript DATE!
  // Incoming datasets need a bit of massaging.
  // We do that in a function so we can reuse it on incoming dataset updates.
  function initializeDataset(dataset) {
      // TIME ON X
      // Convert date strings to actual date values.
      // MDM MAKE SURE this matches the incoming format.
      // Adding date so we can run the chart across days without trouble.
      // var parseDate = d3.time.format("%H:%M:%S %m-%d-%Y").parse;
      // var parseDate = d3.timeParse("%Y %b %d");
      var parseDate = d3.utcParse("%Y-%m-%dT%H:%M:%S.%LZ");
      dataset.forEach(function(d) {
          // there is NO SKIPING THIS STEP, you have to get valid Javascript date objects out of JSON strings
          d.date = parseDate(d.date);
          // we WOULD have to divide all data by zero,
          // but we already grabbed post-data that was already converted
          // This WORKS but makes data / 10000 (very small numbers)
          //for (var i = 1, n = dataset.columns.length; i < n; ++i) d[dataset.columns[i]] = d[dataset.columns[i]] / 100;
      });
  }
  initializeDataset(data);
* tsv() will create an ARRAY but it also jams in a columns PROPERY; it takes two steps to duplicate that:
  var data = [
    {"date":"2015-06-15T04:00:00.000Z","Google Chrome":0.48090000000000005,...},
    {"date":"2015-06-22T04:00:00.000Z","Google Chrome":0.48979999999999996,...),
  ];
  data.columns = ["date","Google Chrome","Internet Explorer",...  ];
|}
{| class="mw-collapsible mw-collapsed wikitable"
! ALWAYS set range and domain properly
|-
| You have to know your display size (range) and the min and max of your data (domain), on each axis.  From [https://bost.ocks.org/mike/bar/ Mike's docs]:
D3’s scales specify a mapping from data space (domain) to display space (range).
        D3’s scales can also be used to interpolate many other
        types of display-space values, such as paths, color spaces
        and geometric transforms.
       
          var x = d3.scale.linear()
              .domain([0, d3.max(data)])
              .range([0, 420]);
 
        Although x here looks like an object, it is also a function
        that returns the scaled display value in the range
        for a given data value in the domain.
        For example, an input value of 4 returns 40, and an input value of 16
        returns 160. To use the new scale, simply replace the
        hard-coded multiplication by calling the scale function:
       
        d3.select(".chart")
          .selectAll("div")
            .data(data)
          .enter().append("div")
            .style("width", function(d) { return x(d) + "px"; })
            .text(function(d) { return d; });
Watch out for Mike's examples where he (trickily) doesn't bother to set a domain because his data is in the [0..1] set, and that's the default domain, apparently.
|}
|}<!--
===========================================================================================================================================================================================================================================================================================
-->
{| class="wikitable"
{| class="wikitable"
! [[Jquery]]
! [[Jquery]]

Revision as of 14:52, 3 March 2018

Basics

Bootstrap
Single Page Application
css:
  .mdm-scrollable-div
  {
    height: 100%; 
    overflow: auto;
  }

html:

  <-- header -->

  <div class="frame" id="frame1top">
    <div class="mdm-scrollable-div">
      ...
    </div>
  </div>

  <-- footer -->

js:

  // We need to adjust layout on resize, in ready handler, and programmatically as needed.
  $(window).resize(adjustLayout).resize();

  $( document ).ready(function() {

    // We need to adjust in ready handler, on resize, and programmatically as needed
    adjustLayout();

    /* your other page load code here*/

  });

  function adjustLayout(){

      // ----------------------
      // Fundamental dimensions
      var hh = $('#theheader').outerHeight();
      var fh = $('#thefooter').outerHeight();
      var workspace_height = $(window).height() - hh - fh;
      var workspace_width = $(window).width(); 
      // ----------------------
      
      var cols = 1;
      var col_gap = 16;
      
      // Margin is based on percent of one column width, so it goes to zero before column width does.  :-)
      // Width is based on total width minus margins, then split between columns.
      var margin = ($(window).width() / cols) * .04;
      var w = ($(window).width() - (margin * (cols + 1))) / cols;
      
      var h1 = workspace_height;
      
      $('#frame1top').css({
          display:'block', 
          position:'absolute',
          left: margin * 1 + w * 0,
          top: hh,
          width: w,
          height: h1
      });
  }
Button handlers
Buttons can be represented with labels; in that case, use a click handler:
    jQuery('<label id="'+cycle.apsID+'" class="btn">MahButt</label>')
    .appendTo('#'+chartid+'-apsbar')
    .on('click', function(){
      $(location).attr('href','/v1/parameters/'+this.id+'.html');
    });

You can also get the checked state, and prevent the button from being pressed, among other things:

      jQuery('<label id="'+cycle.run+'" ...
      .on('click', function(e){
        var run = this.id;
        // Look for the (label->input)
        if (this.firstChild.checked)
        ...
        // To prevent checked/pressed state if desired:
        e.stopPropagation();
      }  

Button bars are represented by labels wrapped around inputs:

    <div class="btn-group live-buttons" data-toggle="buttons"><div class="btn-group"><label class="btn"><input type="checkbox">text...

In that case use a change handler on the input:

      var label = jQuery('<label class="btn btn-sm btn-'+color+'"></label>').appendTo(action_button_bar);
      var input = jQuery('<input class="run-'+cmd+'" type="checkbox" autocomplete="off" value="'+cycle.run+'">').appendTo(label)
      .on('change', cycle.owned? function(){
        patchPick(this.value,'{ "action" : "run-hold"       }');
      } : function() {
        patchPick(this.value,'{ "action" : "run-buy"        }');
      });
      var text = jQuery('<span class="glyphicon glyphicon-'+glyph+'"></span> <span class="hidden-xs">'+cmd+'</span>').appendTo(label);

If you just need clicks from the button bar, you do NOT NEED input:

    <div class="btn-group live-buttons" data-toggle="buttons"><div class="btn-group"><label class="btn">text...

Then you can put the change handler right on the label:

    var applybar = jQuery('<div id="applybar-'+cycle.run+'" class="btn-group pull-right" data-toggle="buttons" />');
    var apply_button = jQuery('<label id="apply-'+cycle.run+'" class="btn btn-sm btn-moneygreen"><span class="glyphicon glyphicon-ok"></span><span class="hidden-xs"> Apply</span></input></label>')
    .on('click', function(e) {    
    
        // Do work

        e.stopPropagation();
    })
    .appendTo(applybar)
    ;
Programmatically pressing a button
You MUST DO TWO THINGS:
  • Set the active class on the button/label
  • Set the input to checked
<label class="btn active">
  <input type="checkbox" autocomplete="off" checked>
  Click me
</label>
Handling the UNCHECK event on a pushbutton
Again you MUST SET BOTH active and checked STATES PROPERLY when creating the button (see above). Do not set either if the button is unpressed; set both if it is pressed.

Then you can use a single change event:

  $("input[type='checkbox'][class='run-hold-on-buy'     ]").change(function() { patchPick(this.value,'{ "action" : "'+(this.checked?'hold-on-buy-on'     :'hold-on-buy-off'     )+'"}');  });
Collapsible panel
  • Use a unique id on content div, data-target to connect panel header to content, mdm-panel-collapser so javascript can find icon, abt-scrollable-panel for margin.
  • HTML for each panel (starts collapsed)
    <div class="panel panel-default abt-scrollable-panel">
      <div class="panel-heading collapsed" data-toggle="collapse" data-target="#my-content-block-one">Software development skills<span class="mdm-panel-collapser text-muted glyphicon glyphicon-chevron-down pull-right"></span></div>
      <div class="collapse" id="my-content-block-one">
        <div class="mdm-panel-body">
  
         <!-- CONTENT, can include another nested panel, just add .mdm-nested-panel to class; example: -->
  
          <div class="panel panel-default mdm-scrollable-panel mdm-nested-panel">
            <div class="panel-heading collapsed mdm-job-panel-heading" data-toggle="collapse" data-target="#toshiba-job"><p><strong>Senior Developer and Offshore Manager</strong><i> - Toshiba Global Commerce Solutions, Inc.</i><span class="mdm-panel-collapser text-muted glyphicon glyphicon-chevron-up pull-right"></span></p><p><small>April 2014 – August 2015</small></p></div>
            <div class="collapse in" id="toshiba-job">
              <div class="mdm-panel-body">
                <!-- SUBCONT(IN)ENT im so funny -->
              </div>
            </div>
          </div>
          

        </div>
      </div>
    </div>
  • For an expanded panel, simply change icon from down to up, and add "in" to content div:
    ... glyphicon-chevron-up ...
    <div class="collapse in mdm-panel-body" id="collapseOrderItems1">
  • Define Javascript collapser once on load
 $('.collapse').on('hide.bs.collapse show.bs.collapse', 
   toggleCollapser
 );
  • CSS
   .mdm-nested-panel {
     margin-top: 1em;
     margin-left: 1em;
   }
   collapse.mdm-panel-body collapsing.mdm-panel-body {
       margin: 1em;
   }
  • Common reusable function:
 function toggleCollapser(e) {
     $(e.target)
         .prev('.panel-heading')
         .find('.mdm-panel-collapser')
         .toggleClass('glyphicon-chevron-down glyphicon-chevron-up');
 
   // Prevent bubble up to any parent collapsers.
   // This allows nested collapsers, whoop.
   e.stopPropagation();
 }
D3
ALWAYS initialize incoming data
Hard-earned data-import lessons:
  • tsv() will do complete date formatting, so when you remove it to use JSON directly, you HAVE TO convert JSON timestamps of ANY kind to full Javascript date objects:
 var data = [{"date":"2017-04-01T04:00:00.000Z",...
 // MDM WE MUST MASSAGE DATA HERE
 // JSON VALUES ARE ALWAYS STRINGS, we need to change to Javascript DATE!
 // Incoming datasets need a bit of massaging.
 // We do that in a function so we can reuse it on incoming dataset updates.
 function initializeDataset(dataset) {
     // TIME ON X
     // Convert date strings to actual date values.
     // MDM MAKE SURE this matches the incoming format.
     // Adding date so we can run the chart across days without trouble.
     // var parseDate = d3.time.format("%H:%M:%S %m-%d-%Y").parse;
     // var parseDate = d3.timeParse("%Y %b %d");
     var parseDate = d3.utcParse("%Y-%m-%dT%H:%M:%S.%LZ");
     dataset.forEach(function(d) {
         // there is NO SKIPING THIS STEP, you have to get valid Javascript date objects out of JSON strings
         d.date = parseDate(d.date);
         // we WOULD have to divide all data by zero,
         // but we already grabbed post-data that was already converted
         // This WORKS but makes data / 10000 (very small numbers)
         //for (var i = 1, n = dataset.columns.length; i < n; ++i) d[dataset.columns[i]] = d[dataset.columns[i]] / 100;
     });
 }
 initializeDataset(data);

  • tsv() will create an ARRAY but it also jams in a columns PROPERY; it takes two steps to duplicate that:
  var data = [
    {"date":"2015-06-15T04:00:00.000Z","Google Chrome":0.48090000000000005,...},
    {"date":"2015-06-22T04:00:00.000Z","Google Chrome":0.48979999999999996,...),
 ];
 data.columns = ["date","Google Chrome","Internet Explorer",...  ];
ALWAYS set range and domain properly
You have to know your display size (range) and the min and max of your data (domain), on each axis. From Mike's docs:

D3’s scales specify a mapping from data space (domain) to display space (range).

       D3’s scales can also be used to interpolate many other 
       types of display-space values, such as paths, color spaces 
       and geometric transforms.
       
         var x = d3.scale.linear()
             .domain([0, d3.max(data)])
             .range([0, 420]);
 
       Although x here looks like an object, it is also a function 
       that returns the scaled display value in the range 
       for a given data value in the domain. 
       For example, an input value of 4 returns 40, and an input value of 16 
       returns 160. To use the new scale, simply replace the 
       hard-coded multiplication by calling the scale function:
       
       d3.select(".chart")
         .selectAll("div")
           .data(data)
         .enter().append("div")
           .style("width", function(d) { return x(d) + "px"; })
           .text(function(d) { return d; });

Watch out for Mike's examples where he (trickily) doesn't bother to set a domain because his data is in the [0..1] set, and that's the default domain, apparently.

Jquery
Vega-lite