<html>
<head>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Code+Pro">
<link rel="stylesheet" href="https://gist.githack.com/christopherjbaker/58dde856b6e7a9f549db959a6360da7f/raw/f2a8796cd37e920a09089a520e050252b6b9614d/todomvc.css">
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div class="intro">
<p>Run the test below to see how DoneJS' view engine is faster than React's when rendering large data sets.</p>
</div>
<div class="runTest">
<button onclick="runTests()">Run Test</button>
<em>This can take up to a minute to complete</em>
</div>
<div style="display: none;" class="spinner">
<div class="rect1"></div>
<div class="rect2"></div>
<div class="rect3"></div>
<div class="rect4"></div>
</div>
<p style="display: none;" class="status">React: Adding 1000 todos</p>
<div style="display: none;" id="testResults">
<h2>Results <em>(smaller is faster)</em></h2>
<p>As more todos are rendered the DOM diff React perform becomes larger and slower while DoneJS' minimal DOM updates remain fast.</p>
<div id="container" style="min-width: 310px; height: 400px; margin: 0 auto"></div>
</div>
<h2>React</h2>
<!--React-->
<div class="todo-wrapper">
<section class="todoapp"></section>
<script src="https://gist.githack.com/christopherjbaker/58dde856b6e7a9f549db959a6360da7f/raw/f2a8796cd37e920a09089a520e050252b6b9614d/react-todomvc.js"></script>
</div>
<h2>DoneJS</h2>
<!--CanJS-->
<div class="todo-wrapper">
<section id="todoapp" class="todoapp"></section>
<script src="https://gist.githack.com/christopherjbaker/58dde856b6e7a9f549db959a6360da7f/raw/da208188041349ca1dcd8b4bc45c7ea58958de44/donejs-todomvc.js"></script>
<script type="text/stache" id="app-template">
<todo-app>
<header id="header">
<h1>todos</h1>
<input class="new-todo" placeholder="What needs to be done?" can-enter="createTodo">
</header>
<section class="main {{^if todos.length}}hidden{{/if}}">
<input class="toggle-all" type="checkbox" {{#if todos.allComplete}}checked="checked" {{/if}} can-click="toggleAll">
<label for="toggle-all">Mark all as complete</label>
<ul class="todo-list">
{{#each displayList}}
<li class="todo{{#if complete}} completed{{/if}}{{#if editing}} editing{{/if}}">
<div class="view">
<input class="toggle" type="checkbox" can-value="complete">
<label can-dblclick="edit">{{text}}</label>
<button class="destroy" can-click="destroy"></button>
</div>
<input class="edit" type="text" value="{{text}}" can-blur="updateTodo" can-keyup="cancelEditing" can-enter="updateTodo">
</li>
{{/each}}
</ul>
</section>
<footer class="footer {{^if todos.length}}hidden{{/if}}">
<span class="todo-count">
<strong>{{todos.remaining}}</strong> {{plural "item" todos.remaining}} left
</span>
<ul class="filters">
<li>{{{link "All" undefined}}}</li>
<li>{{{link "Active" "active"}}}</li>
<li>{{{link "Completed" "completed"}}}</li>
</ul>
<button class="clear-completed {{^if todos.completed.length}}hidden{{/if}}" can-click="clearCompleted">
Clear completed
</button>
</footer>
</todo-app>
</script>
</div>
</div>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
</body>
</html>
body {
font-family: "Lato", sans-serif;
background: #FCFEFD;
padding: 5px;
}
.intro {
font-size: 16px;
line-height: 24px;
font-weight: 400;
color: #424a4e;
}
#testResults p {
font-family: "Lato", sans-serif;
font-size: 16px;
color: #424a4e;
font-weight: 400;
}
.status {
color: #424a4e;
font-weight: bold;
text-align: center;
}
.runTest button {
cursor: pointer;
width: 100%;
color: #fff;
height: 35px;
padding: 5px;
display: block;
margin: 0 auto;
line-height: 1.5em;
background: #3B83A3;
font-weight: 700;
font-size: 14px;
}
.todo-wrapper {
border:1px solid #eee;
}
/* http://tobiasahlin.com/spinkit/ */
.spinner {
margin: 10px auto;
width: 50px;
height: 40px;
text-align: center;
font-size: 10px;
}
.spinner > div {
background-color: #424a4e;
height: 100%;
width: 6px;
display: inline-block;
animation: sk-stretchdelay 1.2s infinite ease-in-out;
animation: sk-stretchdelay 1.2s infinite ease-in-out;
}
.spinner .rect2 {
animation-delay: -1.1s;
animation-delay: -1.1s;
}
.spinner .rect3 {
animation-delay: -1.0s;
animation-delay: -1.0s;
}
.spinner .rect4 {
animation-delay: -0.9s;
animation-delay: -0.9s;
}
.spinner .rect5 {
animation-delay: -0.8s;
animation-delay: -0.8s;
}
@-webkit-keyframes sk-stretchdelay {
0%, 40%, 100% { transform: scaleY(0.4) }
20% { transform: scaleY(1.0) }
}
@keyframes sk-stretchdelay {
0%, 40%, 100% {
transform: scaleY(0.4);
transform: scaleY(0.4);
} 20% {
transform: scaleY(1.0);
transform: scaleY(1.0);
}
}
var todos = [10,20,30,40,50,60,70,80,90,100,120,140,160,180,200,250,300,350,400,450,500,750,1000];
var button = $('.runTest');
var testResults = $('#testResults');
var statusText = $('.status');
var testRunning = $('.spinner');
window.testSetup = function(cb){
button.hide();
testResults.hide();
testRunning.show();
statusText.text('').show();
setTimeout(cb, 10);
};
window.testTeardown = function(results){
window.resetCanTodos();
window.resetReactTodos();
button.show();
testResults.show();
testRunning.hide();
statusText.hide();
$('#container').highcharts({
title: {
text: 'Rendering Todo Items'
},
xAxis: {
categories: todos,
title: {
text: 'Todos Added'
}
},
yAxis: {
title: {
text: 'Render Time (ms)'
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
},
legend: {
layout: 'vertical',
align: 'right',
verticalAlign: 'middle',
borderWidth: 0
},
series: [{
name: 'CanJS 3.5.1',
data: results.can
}, {
name: 'React v15.4.2',
data: results.react
}]
});
};
window.runTests = function(){
var results = {
can: [],
react: []
};
window.testSetup(function(){
var limit = 2*todos.length;
var resolved = 0;
['can', 'react'].forEach(function(library){
var ucase = library.charAt(0).toUpperCase() + library.slice(1);
var i = 0;
var n;
function go(){
n = todos[i];
window['reset' + ucase + 'Todos']();
var test = window['add' + ucase + 'Todos'](n);
test.then(function(r){
results[library].push(r);
resolved++;
i++;
if(i < todos.length) {
go();
}
if(resolved === limit){
window.testTeardown(results);
}
});
}
go();
});
});
};
var oldCanAdd = window.addCanTodos;
window.addCanTodos = function(n){
statusText.text('DoneJS: Adding ' + n + ' todos');
return new Promise(function(resolve) {
setTimeout(function(){
resolve(oldCanAdd(n));
}, 100);
});
};
var oldReactAdd = window.addReactTodos;
window.addReactTodos = function(n){
statusText.text('React: Adding ' + n + ' todos');
return new Promise(function(resolve) {
setTimeout(function(){
resolve(oldReactAdd(n));
}, 100);
});
};
Output
300px
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. |