<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, width=device-width">
<title>JS Bin</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.6/hammer.min.js"></script>
<script src="https://rawgithub.com/marcj/css-element-queries/master/src/ResizeSensor.js"></script>
<script src="https://rawgithub.com/marcj/css-element-queries/master/src/ElementQueries.js"></script>
</head>
<body ontouchstart="">
<div class="container">
<div class="fieldlist">
<div class="field f1">
<div class="fieldcontent">
<h1>Short question with input</h1>
<input type="text" />
</div>
</div>
<div class="field f2">
<div class="fieldcontent">
<h1>Medium question with input</h1>
<p> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
</p>
<input type="text" />
</div>
</div>
<div class="field f3">
<div class="fieldcontent">
<h1>Long question with input</h1>
<p> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
</p>
<p> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
</p>
<p> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
</p>
<input type="text" />
</div>
</div>
<div class="field f4">
<div class="fieldcontent">
<h1>Short question with text area</h1>
<textarea></textarea>
</div>
</div>
<div class="field f5">
<div class="fieldcontent">
<h1>Medium question with text area</h1>
<p> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
</p>
<textarea></textarea>
</div>
</div>
<div class="field f6">
<div class="fieldcontent">
<h1>Long question with text area</h1>
<p> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
</p>
<p> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
</p>
<p> Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
</p>
<textarea></textarea>
</div>
</div>
</div>
<div class="buttons">
<button id="previous">previous</button>
<button id="next">next</button>
</div>
</div>
<script src="http://jsconsole.com/remote.js?EED0E1A8-1548-4951-AAAA-48C9C92F96CE"></script>
</body>
</html>
html, body {
height: 100%;
margin: 0;
padding:0;
color:white;
}
body {
overflow:hidden; /* prevent pull to refresh chrome mobile */
}
/* "embed" style container */
/*
.container {
height:400px;
width: 200px;
margin: auto;
}
*/
/* "fullscreen" style container */
.container {
height:100%;
}
.fieldlist {
/* field list is only scrollable via JS */
height:100% !important;
overflow: hidden !important;
background-color: black;
font-family:consolas;
}
.field {
/* field is scrollable via native scroll,
but cannot have other positional styles attached:
use .fieldContent */
height:100% !important;
overflow-y:auto !important;
margin:0 !important;
padding: 0 !important;
overflow-scrolling: touch;
transform: translateZ(0px);
transform: translate3d(0,0,0);
perspective: 1000;
}
.fieldcontent {
margin: 20px;
}
.f1 {
background-color: #73BEC8
}
.f2 {
background-color: #E4BA3F
}
.f3 {
background-color: #CB732B
}
.f4 {
background-color: #89BC62
}
.f5 {
background-color: #C384C5
}
.f6 {
background-color: #C75875
}
.buttons {
position:absolute;
bottom: 0;
right: 0;
}
.buttons button {
font-size: 20px;
}
var fieldList = document.getElementsByClassName('fieldlist')[0]
var FIELD_HEIGHT = null
var fields = Array.prototype.slice.call( document.getElementsByClassName('field') )
var currentNode = fields[0]
var hammer = new Hammer.Manager(
fieldList,
{touchAction: 'pan-y'}
)
hammer.add(new Hammer.Swipe({
direction: Hammer.DIRECTION_VERTICAL,
enable: function (whatever, ev) {
return canSwipe(ev)
}
}))
hammer.on("swipe", function(ev) {
if (canSwipe(ev)){
snapToPoint(ev.direction)
}
})
resolveFieldHeight()
document.getElementById('previous').onclick = function () {
if (hasFieldAbove()) {
snapToPoint(Hammer.DIRECTION_DOWN)
}
}
document.getElementById('next').onclick = function () {
console.log('next')
if (hasFieldBelow()) {
snapToPoint(Hammer.DIRECTION_UP)
}
}
new ResizeSensor(fieldList, function () {
resolveFieldHeight()
resetScrollPosition()
})
function resolveFieldHeight () {
fieldList.style.height = null // reset fieldList.clientHeight to calculated height
fieldList.style.height = fieldList.clientHeight + 'px' // on chrome/android, maintain keyboard-closed client-height when keyboard open
FIELD_HEIGHT = fieldList.clientHeight
}
function resetScrollPosition () {
fieldList.scrollTop = fields.indexOf(currentNode) * FIELD_HEIGHT
}
function canSwipe (ev) {
if (!ev) return true
var enabled = ev.direction === Hammer.DIRECTION_UP && canSwipeUp()
|| ev.direction === Hammer.DIRECTION_DOWN && canSwipeDown()
return enabled
}
function hasFieldBelow () {
return fields.indexOf(currentNode) !== fields.length - 1
}
function hasFieldAbove () {
return fields.indexOf(currentNode) !== 0
}
function canSwipeUp () {
var canSwipeUp = currentNode.scrollHeight - currentNode.scrollTop <= currentNode.clientHeight + 5
&& hasFieldBelow()
return canSwipeUp
}
function canSwipeDown () {
return currentNode.scrollTop === 0
&& hasFieldAbove()
}
function blurInputs () {
Array.prototype.forEach.call(document.querySelectorAll('input[type=text], textarea'), function(input) {
input.blur()
})
}
function snapToPoint(direction){
blurInputs()
var newScrollPos = null
var newField = null
if (direction == Hammer.DIRECTION_UP) {
newField = fields[fields.indexOf(currentNode) + 1]
} else {
newField = fields[fields.indexOf(currentNode) - 1]
}
newScrollPos = fields.indexOf(newField) * FIELD_HEIGHT
currentNode = newField
animatedScrollTo(fieldList, newScrollPos, 200, resetFieldScrolls)
}
function resetFieldScrolls () {
fields.forEach(function(field) {
field.scrollTop = 0
})
}
function logScrolls () {
var thingsToLog = [ // getClientRects, getBoundingClientRect
'scrollTop',
'clientHeight',
'offsetHeight',
'clientTop',
function (el) {
var dimensions = el.getBoundingClientRect()
return {
'gbcr.bottom': dimensions.bottom,
'gbcr.height': dimensions.height,
'gbcr.top': dimensions.top
}
}
]
var log = {}
log.fieldList = {}
thingsToLog.forEach(function (thingToLog) {
if (typeof thingToLog === 'function') {
Object.assign(log.fieldList, thingToLog(fieldList))
} else {
log.fieldList[thingToLog] = fieldList[thingToLog]
}
})
fields.forEach(function (field, index) {
elementLog = {}
thingsToLog.forEach(function (thingToLog) {
if (typeof thingToLog === 'function') {
Object.assign(elementLog, thingToLog(field))
} else {
elementLog[thingToLog] = field[thingToLog]
}
})
log['f' + index] = elementLog
})
console.table(log)
}
;(function (window) {
var requestAnimFrame = (function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||function(callback){window.setTimeout(callback,1000/60);};})();
var easeInOutQuad = function (t, b, c, d) {
t /= d/2;
if (t < 1) return c/2*t*t + b;
t--;
return -c/2 * (t*(t-2) - 1) + b;
};
var animatedScrollTo = function (element, to, duration, callback) {
var start = element.scrollTop,
change = to - start,
animationStart = +new Date();
var animating = true;
var lastpos = null;
var animateScroll = function() {
if (!animating) {
return;
}
requestAnimFrame(animateScroll);
var now = +new Date();
var val = Math.floor(easeInOutQuad(now - animationStart, start, change, duration));
if (lastpos) {
if (lastpos === element.scrollTop) {
lastpos = val;
element.scrollTop = val;
} else {
animating = false;
}
} else {
lastpos = val;
element.scrollTop = val;
}
if (now > animationStart + duration) {
element.scrollTop = to;
animating = false;
if (callback) { callback(); }
}
};
requestAnimFrame(animateScroll);
};
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
module.exports = animatedScrollTo;
} else {
window.animatedScrollTo = animatedScrollTo;
}
})(window);
window.addEventListener('load', function() {
var maybePreventPullToRefresh = false;
var lastTouchY = 0;
var touchstartHandler = function(e) {
if (e.touches.length != 1) return;
lastTouchY = e.touches[0].clientY;
// Pull-to-refresh will only trigger if the scroll begins when the
// document's Y offset is zero.
maybePreventPullToRefresh =
window.pageYOffset == 0;
}
var touchmoveHandler = function(e) {
var touchY = e.touches[0].clientY;
var touchYDelta = touchY - lastTouchY;
lastTouchY = touchY;
if (maybePreventPullToRefresh) {
// To suppress pull-to-refresh it is sufficient to preventDefault the
// first overscrolling touchmove.
maybePreventPullToRefresh = false;
if (touchYDelta > 0) {
console.log()
e.preventDefault();
return;
}
}
}
document.addEventListener('touchstart', touchstartHandler, false);
document.addEventListener('touchmove', touchmoveHandler, false); });
/*! iNoBounce - v0.1.0
* https://github.com/lazd/iNoBounce/
* Copyright (c) 2013 Larry Davis <lazdnet@gmail.com>; Licensed BSD */
(function(global) {
// Stores the Y position where the touch started
var startY = 0;
// Store enabled status
var enabled = false;
var handleTouchmove = function(evt) {
// Get the element that was scrolled upon
var el = evt.target;
// Check all parent elements for scrollability
while (el !== document.body) {
// Get some style properties
var style = window.getComputedStyle(el);
if (!style) {
// If we've encountered an element we can't compute the style for, get out
break;
}
var scrolling = style.getPropertyValue('-webkit-overflow-scrolling');
var overflowY = style.getPropertyValue('overflow-y');
var height = parseInt(style.getPropertyValue('height'), 10);
// Determine if the element should scroll
var isScrollable = scrolling === 'touch' && (overflowY === 'auto' || overflowY === 'scroll');
var canScroll = el.scrollHeight > el.offsetHeight;
if (isScrollable && canScroll) {
// Get the current Y position of the touch
var curY = evt.touches ? evt.touches[0].screenY : evt.screenY;
// Determine if the user is trying to scroll past the top or bottom
// In this case, the window will bounce, so we have to prevent scrolling completely
var isAtTop = (startY <= curY && el.scrollTop === 0);
var isAtBottom = (startY >= curY && el.scrollHeight - el.scrollTop === height);
// Stop a bounce bug when at the bottom or top of the scrollable element
if (isAtTop || isAtBottom) {
evt.preventDefault();
}
// No need to continue up the DOM, we've done our job
return;
}
// Test the next parent
el = el.parentNode;
}
// Stop the bouncing -- no parents are scrollable
evt.preventDefault();
};
var handleTouchstart = function(evt) {
// Store the first Y position of the touch
startY = evt.touches ? evt.touches[0].screenY : evt.screenY;
};
var enable = function() {
// Listen to a couple key touch events
window.addEventListener('touchstart', handleTouchstart, false);
window.addEventListener('touchmove', handleTouchmove, false);
enabled = true;
};
var disable = function() {
// Stop listening
window.removeEventListener('touchstart', handleTouchstart, false);
window.removeEventListener('touchmove', handleTouchmove, false);
enabled = false;
};
var isEnabled = function() {
return enabled;
};
// Enable by default if the browser supports -webkit-overflow-scrolling
// Test this by setting the property with JavaScript on an element that exists in the DOM
// Then, see if the property is reflected in the computed style
var testDiv = document.createElement('div');
document.documentElement.appendChild(testDiv);
testDiv.style.WebkitOverflowScrolling = 'touch';
var scrollSupport = 'getComputedStyle' in window && window.getComputedStyle(testDiv)['-webkit-overflow-scrolling'] === 'touch';
document.documentElement.removeChild(testDiv);
if (scrollSupport) {
enable();
}
// A module to support enabling/disabling iNoBounce
var iNoBounce = {
enable: enable,
disable: disable,
isEnabled: isEnabled
};
if (typeof module !== 'undefined' && module.exports) {
// Node.js Support
module.exports = iNoBounce;
}
if (typeof global.define === 'function') {
// AMD Support
(function(define) {
define(function() { return iNoBounce; });
}(global.define));
}
else {
// Browser support
global.iNoBounce = iNoBounce;
}
}(this));
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. |