Overflow Report

by Kenneth Luplau-Brøgger

HTML

<div class="outer">
  <div class="inner">
    <button>
      Trigger
    </button>
    <div class="test">
      Hey
    </div>
  </div>
</div>
<div id="result"></div>

CSS

.outer {
  position: relative;
  width: 500px;
  height: 500px;
  background: yellow;
  overflow: hidden;
}

.inner {
  position: absolute;
  width: 500px;
  height: 500px;
  background: blue;
}

.test {
  position: absolute;
  top: 500px;
  width: 400px;
  height: 100px;
  background: black;
  color: #fff;
}

JavaScript

const $element = document.querySelector(".test");
const $result = document.querySelector("#result");
const $trigger = document.querySelector("button");

const getRect = (element) => element.getBoundingClientRect();

const isOverflowingParents = ($element, $parent) => {
  const rect1 = getRect($element);
  const rect2 = getRect($parent);

  const {
    overflow
  } = window.getComputedStyle($parent);

  // Is parent overflowing hidden
  if (overflow !== "visible") {
    const overlapping = {
      top: (rect1.top - rect2.top).toFixed(4),
      bottom: (rect2.bottom - rect1.bottom).toFixed(4),
      left: (rect1.left - rect2.left).toFixed(4),
      right: (rect2.right - rect1.right).toFixed(4)
    };

    // Return first overlapping encounter
    if (Object.keys(overlapping).map((direction) => overlapping[direction]).filter((overlaps) => overlaps < 0).length > 0) {
      return overlapping;
    }
  }

  // Check grand parent recursive
  if ($parent.parentElement) {
    return isOverflowingParents($element, $parent.parentElement);
  }

  // Return false if no overlapping or parents
  return false;
}

function mapObject(object, fn) {
  return fn(object)
}

const getSafePosition = ($element, $parent, $trigger) => {
  const overflows = isOverflowingParents($element, $element.parentElement);

  if (overflows) {
    const {
      top,
      right,
      bottom,
      left
    } = overflows;

    if (top && bottom > $element.offsetHeight) {
      return "bottom";
    }

    if (right && left > $element.offsetWidth) {
      return "left";
    }

    if (bottom && top > $element.offsetHeight) {
      return "top";
    }

    if (left && right > $element.offsetWidth) {
      return "right";
    }

    return false;
  }
}

$result.innerHTML = JSON.stringify(getSafePosition($element, $element.parentElement));