cssAudio - Activefile-genericCSS - ActiveGeneric - ActiveHTML - ActiveImage - ActiveJS - ActiveSVG - ActiveText - Activefile-genericVideo - ActiveLovehtmlicon-new-collectionicon-personicon-teamlog-outoctocatpop-outspinnerstartv

Pen Settings

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

Quick-add: + add another resource

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

Quick-add: + add another resource

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

            
                <link href="https://fonts.googleapis.com/css?family=Lato" rel="stylesheet">
<script
  src="https://code.jquery.com/jquery-3.1.1.min.js"
  integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8="
  crossorigin="anonymous"></script>
  <script src="http://benalman.com/code/projects/jquery-throttle-debounce/jquery.ba-throttle-debounce.js"
   type="application/javascript"></script>
    <script src="script/relative-size-list.js" type="application/javascript"></script>    
<h1>Relative-Sized List</h1>
    <div id="list-container">
        <div id="Mayor and Council" class="o-row salaries-and-wages">
          <span class="o-measure" style="font-size: 337px;">
             <span class="o-cash">$</span> 
              <span class="o-value">18</span>
            </span>
          <span class="o-detail">
            <p class="o-l1">MAYOR</p>
            <p class="o-l2">Salaries and Wages</p>
            <p class="o-total">Total: $178,428<p>
          </span>
        </div>
        <div id="Mayor and Council" class="o-row part-time-temporary-wages">
          <span class="o-measure" style="font-size:32px;">    
              <span class="o-cash">$</span>
            <span class="o-value">1</span>
          </span>
          <span class="o-detail">
            <p class="o-l1">MAYOR</p>
            <p class="o-l2">Part Time & Temporary Wages</p>
            <p class="o-total">Total: $14,000<p>
          </span>
        </div>
    </div>
            
          
!
            
              body {
  font-family: "Lato", sans-serif;
}
div {
  display: block;
}

p {
  margin: 0 0 1em 0;
}

.o-row {
  /* vertical-align:  bottom; */
  display: block;
  /* align-items: baseline; */
}

.o-row span {
  display: inline-block;
}

.o-row .o-measure{
  width: 75%;
  text-align: right;
  margin-bottom: 10px;
  font-weight: 300;
  position: relative;

}

.o-value {
  border-bottom: 1px solid #ddd;
}
.o-row .o-detail{
  width: 23%;
  margin-left: 1%;
}

p.o-l1{
  text-transform: uppercase;
  font-size: 12px;
  color: #5C828A;
  margin-bottom: 0px;
  font-weight: 700;
}

p.o-l2{
  font-size: 22px;
  margin-bottom: 3px;
  text-transform: capitalize;
}

p.o-total{
  font-size: 18px;
  margin-bottom: 0;
  color: #999;
}

.o-cash {
    color: #bbb;
    font-size: 50%;
    vertical-align: top;
    top: 0.4em;
    position: relative;
    margin-right: 2px;
}
            
          
!
            
              /* Takes in a 2-level hierarchical set of data, and renders a series of divs
showing all of the measures sized proportinally to each other based on their 
value compared to the grand total. This ensures that the largest spending item
will have the largest font.
*/

/* Example data */
sampleData = [{
  "agency": "Mayor and Council",
  "fund": "GENERAL FUND",
  "lob": "Mayors Office",
  "program": "MAYOR",
  "key": "BUDGET-VACANCY DISCOUNT",
  "value": "47"
}, {
  "agency": "Mayor and Council",
  "fund": "GENERAL FUND",
  "lob": "Mayors Office",
  "program": "MAYOR",
  "key": "SALARIES AND WAGES",
  "value": "178"
}, {
  "agency": "Mayor and Council",
  "fund": "GENERAL FUND",
  "lob": "Mayors Office",
  "program": "MAYOR",
  "key": "PART TIME & TEMPORARY WAGES",
  "value": "1400"
}, {
  "agency": "Development Services",
  "fund": "GENERAL FUND",
  "lob": "Code Enforcement",
  "program": "ABANDONED BUILDINGS",
  "key": "HEALTH AND WELFARE INSURANCE",
  "value": "133"
}, {
  "agency": "Development Services",
  "fund": "GENERAL FUND",
  "lob": "Code Enforcement",
  "program": "ABANDONED BUILDINGS",
  "key": "RETIREMENT PENSION CONTRIB",
  "value": "35"
}, {
  "agency": "City Manager",
  "fund": "GENERAL FUND",
  "lob": "Policy & Executive Leadership",
  "program": "CITY MANAGERS OFFICE",
  "key": "TRAINING/EDUCATION",
  "value": "9"
}];

//Read the json data file
//https://api.jquery.com/jQuery.getJSON/

function getRootElement() {
  return $("#list-container");
}

function getResizeElements(rootElement) {
  return rootElement.find(".o-measure");
}

function readData(callback) {
  $.getJSON("./data/sampleData.json")
    .done(function(results) {
      callback(results);
    })
    .fail(function(jqxhr, textStatus, error) {
      output("Had a problem getting the data: " + error);
    });
}

//Set some options specifying the L1, L2, measure, and total columns
function getOptions() {
  // hard-coded
  // TODO: make these read from some input
  var options = {
    L1: "program",
    L2: "key",
    measure: "value"
  };
  return options;
}

// master function to begin after data retrieval
function processData(data) {
  var options = getOptions();
  var structured = structureData(data, options);
  var sorted = sortData(structured);
  var subTotaled = subTotalData(sorted);
  var rootElement = getRootElement();

  rootElement = renderList(rootElement, subTotaled);
  resizeList();
}

// Create a new array of elements with L1, L2, measure structure
function structureData(data, options) {
  // get only the attributes you need
  var l1Key = options.L1;
  var l2Key = options.L2;
  var measureKey = options.measure;

  var structuredData = data.map(function(element) {
    var l1 = element.hasOwnProperty(l1Key) ? element[l1Key] : null;
    var l2 = element.hasOwnProperty(l2Key) ? element[l2Key] : null;
    var measure = element.hasOwnProperty(measureKey) ? parseFloat(element[measureKey]) : null;

    return {
      L1: l1.toString().toLowerCase(),
      L2: l2.toString().toLowerCase(),
      measure: measure
    };
  });

  return structuredData;
}

// Sort data by L1 ASC, measure DESC
// assumes the data is already structured with L1, L2 & measure
function sortData(data) {
  var sortedData = data.sort(function(a, b) {
    // compare L1 first, then measure descending
    if (a.L1 < b.L1) {
      return -1;
    } else if (a.L1 > b.L1) {
      return 1;
    } else { // a.L1 == b.L1
      return b.measure - a.measure; // descending order
    }
  });

  return sortedData;
}

// calculates the total at the L1 group level
// adds a new attribute to each element called "subtotal"
function subTotalData(data) {
  subTotals = {};
  for (i = 0; i < data.length; i++) {
    var key = data[i].L1;
    var value = data[i].measure;

    if (!subTotals.hasOwnProperty(key))
      subTotals[key] = 0;
    subTotals[key] += value;
  }

  // apply subtotals to each element
  var subTotaled = data.map(function(element) {
    element.l1SubTotal = subTotals[element.L1];
    return element;
  });

  return subTotaled;
}

//Render the elements in the data as a series of divs with the 'o-row' class applied
function renderList(rootElement, data) {
  data.forEach(function(element) {
    rowDiv = $("<div class='o-row'></div>");
    rowDiv.className = "o-row";
    rowDiv.id = element.L2;

    spanMeasure = $("<span class='o-measure'></span>");
    spanMeasure.append("<span class='o-cash'>$</span");
    spanMeasure.append("<span class='o-value'>" + element.measure.toLocaleString() + "</span>");

    spanDetail = $("<span class='o-detail'></span>");
    spanDetail.append("<p class='o-l1'>" + element.L1 + "</p>");
    spanDetail.append("<p class='o-l2'>" + element.L2 + "</p>");
    spanDetail.append("<p class='o-total'>Total: $" + element.l1SubTotal.toLocaleString() + "</p>");

    rowDiv.append(spanMeasure);
    rowDiv.append(spanDetail);
    rootElement.append(rowDiv);
  });

  return rootElement;
}

// Resize the list based on window size
function resizeList() {
  rootElement = getRootElement();
  elementsToResize = getResizeElements(rootElement);

  var maxWidth = 1800;
  var defaultScaler = 10.5; //scaler - multiplication factor for fonts
  var minScaler = 2.2;

  var newWidth = Math.min($(window).width(), maxWidth); // sets max for width calc
  var scaler = Math.max(defaultScaler * newWidth / maxWidth, minScaler); // min scale

  elementsToResize.each(function(k, v) {
    var valSpan = $(v).children('.o-value')[0];
    var value = $(valSpan).text();
    var fontSize = getFontSize(value, scaler);
    $(v).css('font-size', fontSize + 'px');
  });

}

function getFontSize(val, scaler) {
  var minSize = 11; // minimum font size

  var pc = String(val);
  var str = pc.replace(',', ''); // "100.01"
  var val = Number(str); // 100.01
  var roundNum = Math.round(val);
  var periodCount = (str.match(/\./g) || []).length;
  var numeralCount = (str.match(/[0-9]/g) || []).length;
  var nonNumerals = Math.floor((String(roundNum).length - 1) / 3) + periodCount; // count of periods and commas

  var size = Math.sqrt((val) / (.7 * ((.56 * numeralCount) + (.27 * nonNumerals)))); // font size function
  var fontSize = scaler * size;

  return Math.max(fontSize, minSize);

}

// helpers
function output(message) {
  alert(message);
}


// run on load
$(function() {
  // find root element
  // TODO: make root element dynamic
  // readData(processData);
  processData(sampleData);
  // resize fonts when window resizes
  $(window).resize($.debounce(250, resizeList));
});
            
          
!
999px
Loading ..................

Console