Editable org chart

Using Google Charts to build an org chart from an unordered list

by frumbert

HTML

<script src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<h1>Organizationr</h1>
<p>Fill in your org chart using the bulleted list below (it's editable). Hit tab to indent, shift-tab to outdent. The chart updates as you type. Nodes must have unique names.</p>
<main>
  <aside>
    <ul id="bullets">
      <li>CEO</li>
      <ul>
        <li>Sales</li>
        <li>Marketing</li>
        <ul>
          <li>Truth department</li>
          <li>Lies department</li>
          <li>Political Spin department</li>
        </ul>
        <li>Accounting</li>
        <li>QA</li>
        <li>Production</li>
        <ul>
          <li>Coders</li>
          <li>Hipsters</li>
          <ul>

            <li>Artists</li>
            <li>Musos</li>
          </ul>
        </ul>
      </ul>
    </ul>
  </aside>
  <article>
    <div id="chart_div"></div>
  </article>
</main>

CSS

body {
  background-color: #189082;
  color: #fff;
  font-size: 16px;
  line-height: 1.25;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
  font-style: normal;
  font-weight: 400;
}

main {
  display: flex;
  color: #000;
}

main > article {
  overflow: auto;
  flex: 1;
  background-color: rgba(255, 255, 255, 1);
  border-radius: 0 5px 5px 0;
}

main > aside {
  background-color: rgba(255, 255, 255, .75);
  border-radius: 5px 0 0 5px;
  min-width: 20%;
}

JavaScript

// from https://developers.google.com/chart/interactive/docs/gallery/orgchart
google.charts.load('current', {
  packages: ["orgchart"]
});
google.charts.setOnLoadCallback(setup);

function setup() {
  var bullets = document.getElementById('bullets');
  bullets.contentEditable = true;
  bullets.onkeydown = function(e) {
    if (e.keyCode == 9 && !e.shiftKey) {
      document.execCommand('indent', true, null);
      e.preventDefault();
    } else if (e.keyCode == 9 && e.shiftKey) {
      document.execCommand('outdent', true, null);
      e.preventDefault();
    }
    setTimeout(drawChart, 1);
  }
  drawChart();
}

function addRecord(arr, node, parent) {
  var mytext = node.childNodes[0].nodeValue;
  arr.push([mytext, parent, ""]);
  if (node.nextElementSibling && node.nextElementSibling.nodeName.toLowerCase() == "ul") {
    [].forEach.call(node.nextElementSibling.querySelectorAll(":scope > li"), function(el, index) {
      addRecord(arr, el, mytext);
    });
  }
}

function drawChart() {
  var data = new google.visualization.DataTable(),
    chart = [];
  data.addColumn('string', 'Name');
  data.addColumn('string', 'Manager');
  data.addColumn('string', 'ToolTip');
  var bullets = document.getElementById('bullets').querySelectorAll(":scope > li");
  [].forEach.call(bullets, function(el, index) {
    addRecord(chart, el, "");
  });
  data.addRows(chart);
  var chart = new google.visualization.OrgChart(document.getElementById('chart_div'));
  chart.draw(data, {
    allowHtml: true
  });
}