<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: linear-gradient(35deg, green, #af6 17px, white 20px);
background: 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: linear-gradient(35deg, red, #fa6 17px, white 20px);
background: 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
Keyboard Shortcuts
Shortcut | Action |
---|---|
ctrl + [num] | Toggle nth panel |
ctrl + 0 | Close focused panel |
ctrl + enter | Re-render output. If console visible: run JS in console |
Ctrl + l | Clear the console |
ctrl + / | Toggle comment on selected lines |
ctrl + ] | Indents selected lines |
ctrl + [ | Unindents selected lines |
tab | Code complete & Emmet expand |
ctrl + shift + L | Beautify code in active panel |
ctrl + s | Save & lock current Bin from further changes |
ctrl + shift + s | Open the share options |
ctrl + y | Archive Bin |
Complete list of JS Bin shortcuts |
JS Bin URLs
URL | Action |
---|---|
/ | Show the full rendered output. This content will update in real time as it's updated from the /edit url. |
/edit | Edit the current bin |
/watch | Follow a Code Casting session |
/embed | Create an embeddable version of the bin |
/latest | Load the very latest bin (/latest goes in place of the revision) |
/[username]/last | View the last edited bin for this user |
/[username]/last/edit | Edit the last edited bin for this user |
/[username]/last/watch | Follow the Code Casting session for the latest bin for this user |
/quiet | Remove analytics and edit button from rendered output |
.js | Load only the JavaScript for a bin |
.css | Load only the CSS for a bin |
Except for username prefixed urls, the url may start with http://jsbin.com/abc and the url fragments can be added to the url to view it differently. |