Skip welcome & menu and move to editor
Welcome to JS Bin
Load cached copy from
 
<!doctype html>
<meta charset="utf-8" />
<title> scrollIntoView polyfill of the CSSOM-view proposal </title>
<style>
html {
  height: 300%;
  width: 300%;
}
div#some {
  position: absolute;
  background: black;
  width: 2px;
  height: 2px;
}
iframe {
  position: absolute;
  width: 40px;
  height: 40px;
  border: 0;
}
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>
<div id="some"></div>
<div id="results"></div>
<iframe id="frame" src="http://jsbin.com/3/ojoguy/3"></iframe>
 
////////////////////////////////////////////////////////////////////////////////
// Author: Thaddee Tyl 2012.
// The following work is under CC0.
function scrollIntoView (options) {
  var window = this.ownerDocument.defaultView;
  
  // Traditional scrollIntoView.
  
  if (options === undefined || options === true
                            || options === false) {
    this.scrollIntoView(options);
    return;
  }
  
  // Fetch positional information.
  //
  // 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 rect = this.getBoundingClientRect(),
      topToBottom = rect.bottom,
      bottomToTop = rect.top - window.innerHeight,
      leftToRight = rect.right,
      rightToLeft = rect.left - window.innerWidth,
      xAllowed = true,  // We allow one translation on the x axis,
      yAllowed = true;  // and one on the y axis.
  
  // Read options.
  //
  // We have the following options:
  // - float vertical = 0.5        (from 0 to 1).
  // - float horizontal = 0.0      (from 0 to 1).
  // - boolean notIfViewed = true
  
  if (options.vertical === undefined)  options.vertical = 0.5;
  if (options.horizontal === undefined)  options.horizontal = 0.5;
  if (options.evenIfViewed === undefined)  options.evenIfViewed = false;
  
  // Whatever `horizontal` and `vertical` are,
  // the behavior is the same if the box is (even partially) visible.
  
  if (topToBottom > 0 && topToBottom <= this.offsetHeight) {
    if (yAllowed) {
      window.scrollBy(0, topToBottom - this.offsetHeight);
      yAllowed = false;
    }
  } else 
  if (bottomToTop < 0 && bottomToTop >= -this.offsetHeight) {
    if (yAllowed) {
      window.scrollBy(0, bottomToTop + this.offsetHeight);
      yAllowed = false;
    }
  }
  
  if (leftToRight > 0 && leftToRight <= this.offsetWidth) {
    if (xAllowed) {
      window.scrollBy(leftToRight - this.offsetWidth, 0);
      xAllowed = false;
    }
  } else 
  if (rightToLeft < 0 && rightToLeft >= -this.offsetWidth) {
    if (xAllowed) {
      window.scrollBy(rightToLeft + this.offsetWidth, 0);
      xAllowed = false;
    }
  }
  
  // If we want it positioned in the viewport,
  // and the box is completely hidden,
  // then we position it explicitly.
  if (yAllowed && (options.evenIfViewed? true:
      (topToBottom <= 0 || bottomToTop >= 0))) {
    window.scroll(window.scrollX,
           window.scrollY + rect.top
           - (window.innerHeight - this.offsetHeight) * options.vertical);
  }
  if (xAllowed && (options.evenIfViewed? true:
      (leftToRight <= 0 || rightToLeft <= 0))) {
    window.scroll(window.scrollX + rect.left
           - (window.innerWidth - this.offsetWidth) * options.horizontal,
           window.scrollY);
  }  
  
  if (window.parent !== window) {
    // We are inside a scrollable element.
    
    var frame = window.frameElement;
    scrollIntoView.call(frame, options);
  }
}
// Hook the polyfill.
Element.prototype._scrollIntoView = scrollIntoView;
// 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 below bottom right.
  // Do not resize the window during the tests.
  // In comments on the right, I am talking about that square box.
  
  scroll(innerWidth / 2, innerHeight + 2);  // Just above the viewport.
  some._scrollIntoView({});
  asserteq(scrollY, Math.floor(innerHeight / 2) + 1,
           'Test 1: element hidden above.');
  
  scroll(innerWidth / 2, innerHeight + 2);  // Just above the viewport.
  some._scrollIntoView({vertical:0});
  asserteq(scrollY, innerHeight,
           'Test 2: element hidden above, vertical=0.');
  
  scroll(innerWidth / 2, innerHeight + 2);  // Just above the viewport.
  some._scrollIntoView({vertical:1});
  asserteq(scrollY, 2,
           'Test 3: element hidden above, vertical=1.');
  
  scroll(innerWidth / 2, innerHeight + 2);  // Just above the viewport.
  some._scrollIntoView({horizontal:0});
  asserteq(scrollX, innerWidth,
           'Test 4: element hidden above, horizontal=0.');
  
  scroll(innerWidth / 2, innerHeight + 2);  // Just above the viewport.
  some._scrollIntoView({horizontal:1});
  asserteq(scrollX, 2,
           'Test 5: element hidden above, horizontal=1.');
  
  scroll(innerWidth / 2, innerHeight + 1);  // Just along the viewport.
  some._scrollIntoView({});
  asserteq(scrollY, innerHeight,
           'Test 6: element partially visible above.');
  
  scroll(innerWidth / 2, innerHeight - 1);  // In the viewport.
  some._scrollIntoView({vertical:0});
  asserteq(scrollY, innerHeight - 1,
           'Test 7: element visible above.');
  
  scroll(innerWidth / 2, innerHeight - 1);  // In the viewport.
  some._scrollIntoView({evenIfViewed:true, vertical:0});
  asserteq(scrollY, innerHeight,
           'Test 8: element visible above, evenIfViewed=true.');
  
  // Huge, window-sized box.
  some.style.width = innerWidth + 'px';
  some.style.height = innerHeight + 'px';
  // Viewport is at the bottom right of the box.
  scroll(2 * innerWidth, 2 * innerHeight);
  some._scrollIntoView({});
  asserteq(scrollX, innerWidth,
           'Test 9: element bigger than viewport (x)');
  asserteq(scrollY, innerHeight,
           'Test 10: element bigger than viewport (y)');
  
  // Iframe tests.
  
  some.style.height = '0px';
  scroll(0, 0);
  
  var frame = id('frame'),
      fwindow = frame.contentWindow;
  
  frame.style.top = window.innerHeight + 'px';
  frame.style.left = window.innerWidth + 'px';
  
  fwindow.addEventListener('load', function frameLoad() {
    var some = fwindow.document.getElementById('some');
    some._scrollIntoView({});
    asserteq(window.scrollX, Math.floor(window.innerWidth / 2) + 20,
             'Test 11: scrolling from an iframe (x)');
    asserteq(window.scrollY, Math.floor(window.innerHeight / 2) + 20,
             'Test 12: scrolling from an iframe (y)');
    asserteq(fwindow.scrollX, Math.floor(fwindow.innerWidth / 2) + 1,
             'Test 13: scrolling inside an iframe (x)');
    asserteq(fwindow.scrollY, Math.floor(fwindow.innerHeight / 2) + 1,
             'Test 14: scrolling inside an iframe (y)');
    
    scroll(0, 0);
  }, false);
  
}());
Output 300px

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

Dismiss x
public
Bin info
anonymouspro
0viewers