Responsive Infinite Heirarchal List

by skibulk

HTML

<script src="https://code.jquery.com/jquery-2.2.4.js"></script>
<div id="heirarchy-outer">
  <div id="heirarchy-inner"></div>
</div>

CSS

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
}

#heirarchy-outer {
  height: 400px;
  margin: 40px;
  overflow-y: scroll;
  overflow-x: hidden;
}

.level {
  width: 100%;
}

.level .level {
  margin-left: 10%;
  margin-right: -10%;
}

.content {
  border: 1px solid lightgray;
  padding: 5px 10px;
  margin-right: 10%;
}

JavaScript

var $heirarchy_outer = $('#heirarchy-outer'),
  $heirarchy_inner = $('#heirarchy-inner'),
  $prevEl = $heirarchy_inner,
  $nextEl;

// Generate Example Heirarchal Content
function addRow(i) {
  $nextEl = $('<div class="level"><div class="content">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi consectetur fringilla nisi vel tempor.</div></div>');
  $prevEl.append($nextEl);
  $prevEl = $nextEl;
}

for (var i = 0; i < 30; i++) {
  addRow(i);
}

// Scroll State Data
var prev_width = $heirarchy_outer.width(),
  target_el = $('.content').first(),
  target_spacer = 0,
  target_margin = 0;

// Check if element is inside parent's top and bottom boundaries 
function isScrolledIntoView(child, parent) {
  child = child.getBoundingClientRect();
  parent = parent.getBoundingClientRect();

  // Return true if child is partially visible
  // return (child.bottom >= parent.top) && (child.top <= parent.bottom);

  // Return true if child is completely visible
  return (child.top >= parent.top) && (child.bottom <= parent.bottom);
}

// On Scroll-Y, Automatically calculate scroll-X
$heirarchy_outer.scroll(function(event) {

  // Check for intentional scrolling (not from resizing)
  if (prev_width == $heirarchy_outer.width()) {

    // For each node in the heirarchy
    $('.content').each(function(index, item) {

      // Check if the node is scrolled into view
      if (isScrolledIntoView(item, $heirarchy_outer[0])) {

        // Save scroll state data
        target_el = item;
        target_spacer = item.getBoundingClientRect().top - $heirarchy_outer[0].getBoundingClientRect().top;
        return false;
      }
    });

  } else {

    // Automatic scrolling (from resizing)
    prev_width = $heirarchy_outer.width();
  }

  // Already at the correct position?
  var margin = target_el.getBoundingClientRect().left - $heirarchy_inner[0].getBoundingClientRect().left;
  if (margin != target_margin) {
    target_margin = margin;
    console.log('scroll-x');

    //...