Skip welcome & menu and move to editor
Welcome to JS Bin
Load cached copy from
 
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title> JS Bin </title>
  
<style>
  html {
    height: 300%;
    width: 300%;
  }
  div#some {
    position: absolute;
    background: black;
    width: 2px;
    height: 2px;
  }
  div.success, div.fail {
    border-radius: 3px 0px 3px 4px;
    max-width: 12cm;
    padding: 7px;
    margin: 5px;
  }
  div.success {
    border-left: green 2px solid;
    border-bottom: green 1px solid;
    background: -moz-linear-gradient(35deg, green, #af6 17px, white 20px);
    background: -webkit-linear-gradient(35deg, green, #af6 17px, white 20px);
    background: linear-gradient(35deg, green, #af6 17px, white 20px);
    box-shadow: inset 3px -3px 3px -3px #4a0;
  }
  div.fail {
    border-left: red 2px solid;
    border-bottom: red 1px solid;
    background: -moz-linear-gradient(35deg, red, #fa6 17px, white 20px);
    background: -webkit-linear-gradient(35deg, red, #fa6 17px, white 20px);
    background: linear-gradient(35deg, red, #fa6 17px, white 20px);
    box-shadow: inset 3px -3px 3px -3px #a40;
  }
</style>
  
</head>
<body>
  
  <h1><code> scrollIntoViewIfNeeded </code></h1>
  
  <div id=some></div>
  <div id=results></div>
  
</body>
</html>
 
////////////////////////////////////////////////////////////////////////////////
function scrollIntoViewIfNeeded(centered) {
  centered = centered === undefined? true: !!centered;
  
  // The following are always from the {top, bottom, left, right}
  // of the viewport, to the {top, …} of the box.
  // Think of them as geometrical vectors, it helps.
  // The axes are directed downwards and towards the right.
  
  var topToBottom = this.offsetTop + this.offsetHeight - scrollY,
      bottomToTop = - innerHeight + topToBottom - this.offsetHeight,
      leftToRight = this.offsetLeft + this.offsetWidth,
      rightToLeft = - innerWidth + leftToRight - this.offsetWidth,
      xAllowed = true,  // We allow one translation on the x axis,
      yAllowed = true;  // and one on the y axis.
  
  // Whatever `centered` is, the behavior is the same if the box is
  // (even partially) visible.
  
  if ((topToBottom > 0 || !centered) && topToBottom <= this.offsetHeight) {
    if (yAllowed) {
      scrollBy(0, topToBottom - this.offsetHeight);
      yAllowed = false;
    }
  } else 
  if ((bottomToTop < 0 || !centered) && bottomToTop >= -this.offsetHeight) {
    if (yAllowed) {
      scrollBy(0, bottomToTop + this.offsetHeight);
      yAllowed = false;
    }
  }
  
  if ((leftToRight > 0 || !centered) && leftToRight <= this.offsetWidth) {
    if (xAllowed) {
      scrollBy(leftToRight - this.offsetWidth, 0);
      xAllowed = false;
    }
  } else 
  if ((rightToLeft < 0 || !centered) && rightToLeft >= -this.offsetWidth) {
    if (xAllowed) {
      scrollBy(rightToLeft + this.offsetWidth, 0);
      xAllowed = false;
    }
  }
  
  // If we want it centered, and the box is completely hidden,
  // then we center it explicitly.
  
  if (centered) {
    
    if (yAllowed && (topToBottom <= 0 || bottomToTop >= 0)) {
      scroll(scrollX, this.offsetTop + this.offsetHeight / 2 - innerHeight / 2);
    }
  
    if (xAllowed && (leftToRight <= 0 || rightToLeft <= 0)) {
      scroll(this.offsetLeft + this.offsetWidth / 2 - innerWidth / 2, scrollY);
    }
  }
}
if (!Element.prototype.scrollIntoViewIfNeeded) {
  Element.prototype.scrollIntoViewIfNeeded = scrollIntoViewIfNeeded;
}
// Tests.
function asserteq (a, b, test) {
  var bool = a ===b,
      box = document.createElement('div');
  box.textContent = test;
  if (!bool)  console.error('Assertion error', test || '',
                            a, 'is not', b);
  if (test) {
    if (!bool)  box.className = 'fail';
    else  box.className = 'success';
  }
  document.getElementById('results').appendChild(box);
}
(function () {
  
  var id = document.getElementById.bind(document),
      some = id('some');
  
  some.style.top = innerHeight + 'px';
  some.style.left = innerWidth + 'px';
  // The tests start with a black 2x2 pixels square at bottom right.
  // Do not resize the window during the tests.
  
  scroll(innerWidth / 2, innerHeight + 2);  // Just above the viewport.
  some.scrollIntoViewIfNeeded();
  asserteq(scrollY, Math.ceil(innerHeight / 2) + 1,
         'Test 1: element completely hidden above.');
  
  scroll(innerWidth / 2, innerHeight + 1);  // On the top edge.
  some.scrollIntoViewIfNeeded();
  asserteq(scrollY, innerHeight,
         'Test 2: element partially visible above.');
  
  scroll(innerWidth / 2, 0);  // Just below the viewport.
  some.scrollIntoViewIfNeeded();
  asserteq(scrollY, Math.ceil(innerHeight / 2) + 1,
         'Test 3: element completely hidden below.');
  
  scroll(innerWidth / 2, 1);  // On the bottom edge.
  some.scrollIntoViewIfNeeded();
  asserteq(scrollY, 2,
         'Test 4: element partially visible below.');
  
  
  scroll(innerWidth / 2, innerHeight + 2);  // Just above the viewport.
  some.scrollIntoViewIfNeeded(false);
  asserteq(scrollY, innerHeight,
         'Test 5: not centered, element completely hidden above.');
  
  scroll(innerWidth / 2, innerHeight + 1);  // On the top edge.
  some.scrollIntoViewIfNeeded(false);
  asserteq(scrollY, innerHeight,
         'Test 6: not centered, element partially visible above.');
  
  scroll(innerWidth / 2, 0);  // Just below the viewport.
  some.scrollIntoViewIfNeeded(false);
  asserteq(scrollY, 2,
         'Test 7: not centered, element completely hidden below.');
  
  scroll(innerWidth / 2, 1);  // On the bottom edge.
  some.scrollIntoViewIfNeeded(false);
  asserteq(scrollY, 2,
         'Test 8: not centered, element partially visible below.');
  
  scroll(0, 0);  // Look at the results.
}());
  
Output

You can jump to the latest bin by adding /latest to your URL

Dismiss x
public
Bin info
foopro
0viewers