JS LEGOS
REUSABLE UI PATTERNS IN JAVASCRIPT
If you're here for the examples, jump to the
end.
TYLER BENZIGER
github.com/tybenz • @tybenz
tybenz@adobe.com • tybenz.com
WHAT WE'LL COVER
UI Patterns
Decoupling HTML, CSS, and JS
The JavaScript
DECOUPLE
HTML => structure
CSS => style
JS => behavior/interaction
WHAT IT MEANS
Style only happens in CSS
Animation only happens in CSS
JS shouldn't care about markup
<ul class="task-list">
<li class="task">
<input type="checkbox" name="groceries" />
Groceries
</li>
<li class="task">
<input type="checkbox" name="homework" />
Homework
</li>
<li class="task">
<input type="checkbox" name="write_code" />
Write Code
</li>
</ul>
<ul class="task-list">
<li class="task">
<input type="checkbox" name="groceries" />
Groceries
</li>
<li class="task">
<input type="checkbox" name="homework" />
Homework
</li>
<li class="task">
<input type="checkbox" name="write_code" />
Write Code
</li>
</ul>
<ul class="task-list">
<li class="task">
<input type="checkbox" name="groceries" />
Groceries
</li>
<li class="task">
<input type="checkbox" name="homework" />
Homework
</li>
<li class="task">
<input type="checkbox" name="write_code" />
Write Code
</li>
</ul>
<ul class="task-list">
<li class="task">
<input type="checkbox" name="groceries" />
Groceries
</li>
<li class="task">
<input type="checkbox" name="homework" />
Homework
</li>
<li class="task">
<input type="checkbox" name="write_code" />
Write Code
</li>
</ul>
<ul class="task-list">
<li class="task">
<input type="checkbox" name="groceries" />
Groceries
</li>
<li class="task">
<input type="checkbox" name="homework" />
Homework
</li>
<li class="task">
<input type="checkbox" name="write_code" />
Write Code
</li>
</ul>
<ul class="task-list">
<li class="task">
<input type="checkbox" name="groceries" />
Groceries
</li>
<li class="task">
<input type="checkbox" name="homework" />
Homework
</li>
<li class="task">
<input type="checkbox" name="write_code" />
Write Code
</li>
</ul>
$( '.task input[type=checkbox]' ).on( 'change', function () {
if ( this.checked ) {
$( this ).closest( '.task' ).addClass( 'completed' );
}
});
$(
'.task input[type=checkbox]' ).on( 'change', function () {
if ( this.checked ) {
$( this ).closest( '.task' ).addClass( 'completed' );
}
});
$( '.task input[type=checkbox]' ).
on(
'change', function () {
if ( this.checked ) {
$( this ).closest( '.task' ).addClass( 'completed' );
}
});
$( '.task input[type=checkbox]' ).on( 'change', function () {
if ( this.checked ) {
$( this ).closest( '.task' ).addClass( 'completed' );
}
});
$( '.task input[type=checkbox]' ).on( 'change', function () {
if ( this.checked ) {
$( this ).closest( '.task' ).addClass( 'completed' );
}
});
$( '.task input[type=checkbox]' ).on( 'change', function () {
if ( this.checked ) {
$( this ).closest( '.task' ).
addClass( 'completed' );
}
});
$( '.task input[type=checkbox]' ).on( 'change', function () {
if ( this.checked ) {
$( this ).closest( '.task' ).addClass( 'completed' );
}
});
.task {
width: 400px;
padding: 10px;
margin-bottom: 10px;
overflow: hidden;
}
.task.complete {
display: none;
}
.task {
width: 400px;
padding: 10px;
margin-bottom: 10px;
overflow: hidden;
}
.task.complete {
display: none;
}
.task {
width: 400px;
padding: 10px;
margin-bottom: 10px;
overflow: hidden;
}
.task.complete {
display: none;
}
.task {
width: 400px;
padding: 10px;
margin-bottom: 10px;
overflow: hidden;
}
.task.complete {
display: none;
}
.task {
width: 400px;
padding: 10px;
margin-bottom: 10px;
overflow: hidden;
}
.task.complete {
animation: fadeout 1s ease-out;
opacity: 0;
height: 0;
margin-bottom: 0;
padding: 0;
}
@keyframes fadeout {
0% { opacity: 1; height: auto; margin-bottom: 10px; padding:10px; }
20% { opacity: 0; height: auto; margin-bottom: 10px; padding:10px; }
100% { opacity: 0; height: 0; margin-bottom: 0px; }
}
.task {
width: 400px;
padding: 10px;
margin-bottom: 10px;
overflow: hidden;
}
.task.complete {
animation: fadeout 1s ease-out;
opacity: 0;
height: 0;
margin-bottom: 0;
padding: 0;
}
@keyframes fadeout {
0% { opacity: 1; height: auto; margin-bottom: 10px; padding:10px; }
20% { opacity: 0; height: auto; margin-bottom: 10px; padding:10px; }
100% { opacity: 0; height: 0; margin-bottom: 0px; }
}
.task {
width: 400px;
padding: 10px;
margin-bottom: 10px;
overflow: hidden;
}
.task.complete {
animation: fadeout 1s ease-out;
opacity: 0;
height: 0;
margin-bottom: 0;
padding: 0;
}
@keyframes fadeout {
0% { opacity: 1; height: auto; margin-bottom: 10px; padding:10px; }
20% { opacity: 0; height: auto; margin-bottom: 10px; padding:10px; }
100% { opacity: 0; height: 0; margin-bottom: 0px; }
}
.task {
width: 400px;
padding: 10px;
margin-bottom: 10px;
overflow: hidden;
}
.task.complete {
animation: fadeout 1s ease-out;
opacity: 0;
height: 0;
margin-bottom: 0;
padding: 0;
}
@keyframes fadeout {
0% { opacity: 1; height: auto; margin-bottom: 10px; padding:10px; }
20% { opacity: 0; height: auto; margin-bottom: 10px; padding:10px; }
100% { opacity: 0; height: 0; margin-bottom: 0px; }
}
.task {
width: 400px;
padding: 10px;
margin-bottom: 10px;
overflow: hidden;
}
.task.complete {
animation: fadeout 1s ease-out;
opacity: 0;
height: 0;
margin-bottom: 0;
padding: 0;
}
@keyframes fadeout {
0% { opacity: 1; height: auto; margin-bottom: 10px; padding:10px; }
20% { opacity: 0; height: auto; margin-bottom: 10px; padding:10px; }
100% { opacity: 0; height: 0; margin-bottom: 0px; }
}
$.fn.secret = function () {
this.on( 'mousedown', function () {
$( this ).addClass( 'show' );
});
this.on( 'mouseup', function () {
$( this ).removeClass( 'show' );
});
};
$( '.secret' ).secret();
$.fn.secret
= function () {
this.on( 'mousedown', function () {
$( this ).addClass( 'show' );
});
this.on( 'mouseup', function () {
$( this ).removeClass( 'show' );
});
};
$( '.secret' ).secret();
$.fn.secret = function () {
this.on(
'mousedown', function () {
$( this ).addClass( 'show' );
});
this.on( 'mouseup', function () {
$( this ).removeClass( 'show' );
});
};
$( '.secret' ).secret();
$.fn.secret = function () {
this.on( 'mousedown', function () {
$( this ).addClass( 'show' );
});
this.on( 'mouseup', function () {
$( this ).removeClass( 'show' );
});
};
$( '.secret' ).secret();
$.fn.secret = function () {
this.on( 'mousedown', function () {
$( this ).addClass( 'show' );
});
this.on(
'mouseup', function () {
$( this ).removeClass( 'show' );
});
};
$( '.secret' ).secret();
$.fn.secret = function () {
this.on( 'mousedown', function () {
$( this ).addClass( 'show' );
});
this.on( 'mouseup', function () {
$( this ).removeClass( 'show' );
});
};
$( '.secret' ).secret();
$.fn.secret = function () {
this.on( 'mousedown', function () {
$( this ).addClass( 'show' );
});
this.on( 'mouseup', function () {
$( this ).removeClass( 'show' );
});
};
$( '.secret' ).secret();
$.fn.secret = function () {
this.on( 'mousedown', function () {
$( this ).addClass( 'show' );
});
this.on( 'mouseup', function () {
$( this ).removeClass( 'show' );
});
};
$( '.secret' ).secret();
var Secret = Backbone.View.extend({
el: '.secret',
events: {
'mousedown': 'apply',
'mouseup': 'remove'
},
apply: function () {
this.$el.addClass( 'show' );
this.trigger( 'apply' );
},
remove: function () {
this.$el.removeClass( 'show' );
this.trigger( 'remove' );
}
});
var secret = new Secret();
var Secret =
Backbone.View.extend({
el: '.secret',
events: {
'mousedown': 'apply',
'mouseup': 'remove'
},
apply: function () {
this.$el.addClass( 'show' );
this.trigger( 'apply' );
},
remove: function () {
this.$el.removeClass( 'show' );
this.trigger( 'remove' );
}
});
var secret = new Secret();
var Secret = Backbone.View.extend({
el: '.secret',
events: {
'mousedown': 'apply',
'mouseup': 'remove'
},
apply: function () {
this.$el.addClass( 'show' );
this.trigger( 'apply' );
},
remove: function () {
this.$el.removeClass( 'show' );
this.trigger( 'remove' );
}
});
var secret = new Secret();
var Secret = Backbone.View.extend({
el: '.secret',
events: {
'mousedown': 'apply',
'mouseup': 'remove'
},
apply: function () {
this.$el.addClass( 'show' );
this.trigger( 'apply' );
},
remove: function () {
this.$el.removeClass( 'show' );
this.trigger( 'remove' );
}
});
var secret = new Secret();
var Secret = Backbone.View.extend({
el: '.secret',
events: {
'mousedown': 'apply',
'mouseup': 'remove'
},
apply: function () {
this.$el.addClass( 'show' );
this.trigger( 'apply' );
},
remove: function () {
this.$el.removeClass( 'show' );
this.trigger( 'remove' );
}
});
var secret = new Secret();
var Secret = Backbone.View.extend({
el: '.secret',
events: {
'mousedown': 'apply',
'mouseup': 'remove'
},
apply: function () {
this.$el.addClass( 'show' );
this.trigger( 'apply' );
},
remove: function () {
this.$el.removeClass( 'show' );
this.trigger( 'remove' );
}
});
var secret = new Secret();
var Secret = Backbone.View.extend({
el: '.secret',
events: {
'mousedown': 'apply',
'mouseup': 'remove'
},
apply: function () {
this.$el.addClass( 'show' );
this.trigger( 'apply' );
},
remove: function () {
this.$el.removeClass( 'show' );
this.trigger( 'remove' );
}
});
var secret = new Secret();
var Secret = Backbone.View.extend({
el: '.secret',
events: {
'mousedown': 'apply',
'mouseup': 'remove'
},
apply: function () {
this.$el.addClass( 'show' );
this.trigger( 'apply' );
},
remove: function () {
this.$el.removeClass( 'show' );
this.trigger( 'remove' );
}
});
var secret = new Secret();
var Secret = Backbone.View.extend({
el: '.secret',
events: {
'mousedown': 'apply',
'mouseup': 'remove'
},
apply: function () {
this.$el.addClass( 'show' );
this.trigger( 'apply' );
},
remove: function () {
this.$el.removeClass( 'show' );
this.trigger( 'remove' );
}
});
var secret = new Secret();
var Secret = Backbone.View.extend({
el: '.secret',
events: {
'mousedown': 'apply',
'mouseup': 'remove'
},
apply: function () {
this.$el.addClass( 'show' );
this.trigger( 'apply' );
},
remove: function () {
this.$el.removeClass( 'show' );
this.trigger( 'remove' );
}
});
var secret = new Secret();
// Class stuff: http://ejohn.org/blog/simple-javascript-inheritance/
var Lego = Class.extend({
defaultOptions: {},
init: function ( el, options ) {
this.$el = $( el );
this.options = $.extend( {}, this.defaultOptions, options );
},
trigger: function ( evt, data ) {
$( this ).trigger( evt, data );
},
on: function ( evt, fn ) {
$( this ).on( evt, fn );
}
});
// Class stuff: http://ejohn.org/blog/simple-javascript-inheritance/
var Lego = Class.extend({
defaultOptions: {},
init: function ( el, options ) {
this.$el = $( el );
this.options = $.extend( {}, this.defaultOptions, options );
},
trigger: function ( evt, data ) {
$( this ).trigger( evt, data );
},
on: function ( evt, fn ) {
$( this ).on( evt, fn );
}
});
// Class stuff: http://ejohn.org/blog/simple-javascript-inheritance/
var Lego = Class.extend({
defaultOptions: {},
init: function ( el, options ) {
this.$el = $( el );
this.options = $.extend( {}, this.defaultOptions, options );
},
trigger: function ( evt, data ) {
$( this ).trigger( evt, data );
},
on: function ( evt, fn ) {
$( this ).on( evt, fn );
}
});
// Class stuff: http://ejohn.org/blog/simple-javascript-inheritance/
var Lego = Class.extend({
defaultOptions: {},
init: function ( el, options ) {
this.$el = $( el );
this.options = $.extend( {}, this.defaultOptions, options );
},
trigger: function ( evt, data ) {
$( this ).trigger( evt, data );
},
on: function ( evt, fn ) {
$( this ).on( evt, fn );
}
});
// Class stuff: http://ejohn.org/blog/simple-javascript-inheritance/
var Lego = Class.extend({
defaultOptions: {},
init: function ( el, options ) {
this.$el = $( el );
this.options = $.extend( {}, this.defaultOptions, options );
},
trigger: function ( evt, data ) {
$( this ).trigger( evt, data );
},
on: function ( evt, fn ) {
$( this ).on( evt, fn );
}
});
// Class stuff: http://ejohn.org/blog/simple-javascript-inheritance/
var Lego = Class.extend({
defaultOptions: {},
init: function ( el, options ) {
this.$el = $( el );
this.options = $.extend( {}, this.defaultOptions, options );
},
trigger: function ( evt, data ) {
$( this ).trigger( evt, data );
},
on: function ( evt, fn ) {
$( this ).on( evt, fn );
}
});
var Secret = Lego.extend({
defaultOptions: {
applyClass: 'show',
applyEvent: 'mousedown',
removeEvent: 'mouseup'
},
init: function ( el, options ) {
this._super( el, options );
this.$el.on( this.options.applyEvent, function () {
self.apply();
self.trigger( 'secret-apply' );
});
this.$el.on( this.options.removeEvent, function () {
self.remove();
self.trigger( 'secret-remove' );
});
},
apply: function () { this.$el.addClass( this.options.applyClass ); },
remove: function () { this.$el.removeClass( this.options.applyClass ); }
});
var Secret = Lego.extend({
defaultOptions: {
applyClass: 'show',
applyEvent: 'mousedown',
removeEvent: 'mouseup'
},
init: function ( el, options ) {
this._super( el, options );
this.$el.on( this.options.applyEvent, function () {
self.apply();
self.trigger( 'secret-apply' );
});
this.$el.on( this.options.removeEvent, function () {
self.remove();
self.trigger( 'secret-remove' );
});
},
apply: function () { this.$el.addClass( this.options.applyClass ); },
remove: function () { this.$el.removeClass( this.options.applyClass ); }
});
var Secret = Lego.extend({
defaultOptions: {
applyClass: 'show',
applyEvent: 'mousedown',
removeEvent: 'mouseup'
},
init: function ( el, options ) {
this._super( el, options );
this.$el.on(
this.options.applyEvent, function () {
self.apply();
self.trigger( 'secret-apply' );
});
this.$el.on( this.options.removeEvent, function () {
self.remove();
self.trigger( 'secret-remove' );
});
},
apply: function () { this.$el.addClass( this.options.applyClass ); },
remove: function () { this.$el.removeClass( this.options.applyClass ); }
});
var Secret = Lego.extend({
defaultOptions: {
applyClass: 'show',
applyEvent: 'mousedown',
removeEvent: 'mouseup'
},
init: function ( el, options ) {
this._super( el, options );
this.$el.on( this.options.applyEvent, function () {
self.apply();
self.trigger( 'secret-apply' );
});
this.$el.on( this.options.removeEvent, function () {
self.remove();
self.trigger( 'secret-remove' );
});
},
apply: function () { this.$el.addClass( this.options.applyClass ); },
remove: function () { this.$el.removeClass( this.options.applyClass ); }
});
var Secret = Lego.extend({
defaultOptions: {
applyClass: 'show',
applyEvent: 'mousedown',
removeEvent: 'mouseup'
},
init: function ( el, options ) {
this._super( el, options );
this.$el.on( this.options.applyEvent, function () {
self.apply();
self.trigger( 'secret-apply' );
});
this.$el.on( this.options.removeEvent, function () {
self.remove();
self.trigger( 'secret-remove' );
});
},
apply: function () { this.$el.addClass( this.options.applyClass ); },
remove: function () { this.$el.removeClass( this.options.applyClass ); }
});
var Secret = Lego.extend({
defaultOptions: {
applyClass: 'show',
applyEvent: 'mousedown',
removeEvent: 'mouseup'
},
init: function ( el, options ) {
this._super( el, options );
this.$el.on( this.options.applyEvent, function () {
self.apply();
self.trigger( 'secret-apply' );
});
this.$el.on( this.options.removeEvent, function () {
self.remove();
self.trigger( 'secret-remove' );
});
},
apply: function () { this.$el.addClass( this.options.applyClass ); },
remove: function () { this.$el.removeClass( this.options.applyClass ); }
});
var Secret = Lego.extend({
defaultOptions: {
applyClass: 'show',
applyEvent: 'mousedown',
removeEvent: 'mouseup'
},
init: function ( el, options ) {
this._super( el, options );
this.$el.on( this.options.applyEvent, function () {
self.apply();
self.trigger( 'secret-apply' );
});
this.$el.on( this.options.removeEvent, function () {
self.remove();
self.trigger( 'secret-remove' );
});
},
apply: function () { this.$el.addClass( this.options.applyClass ); },
remove: function () { this.$el.removeClass( this.options.applyClass ); }
});
var Secret = Lego.extend({
defaultOptions: {
applyClass: 'show',
applyEvent: 'mousedown',
removeEvent: 'mouseup'
},
init: function ( el, options ) {
this._super( el, options );
this.$el.on( this.options.applyEvent, function () {
self.apply();
self.trigger( 'secret-apply' );
});
this.$el.on( this.options.removeEvent, function () {
self.remove();
self.trigger( 'secret-remove' );
});
},
apply: function () { this.$el.addClass( this.options.applyClass ); },
remove: function () { this.$el.removeClass( this.options.applyClass ); }
});
var Secret = Lego.extend({
defaultOptions: {
applyClass: 'show',
applyEvent: 'mousedown',
removeEvent: 'mouseup'
},
init: function ( el, options ) {
this._super( el, options );
this.$el.on( this.options.applyEvent, function () {
self.apply();
self.trigger( 'secret-apply' );
});
this.$el.on( this.options.removeEvent, function () {
self.remove();
self.trigger( 'secret-remove' );
});
},
apply: function () { this.$el.addClass( this.options.applyClass ); },
remove: function () { this.$el.removeClass( this.options.applyClass ); }
});
var secret1 = new Secret( '#secret1' ),
secret2 = new Secret( '#secret2' );
secret1.on( 'secret-apply', function () {
secret2.apply();
});
secret1.on( 'secret-remove', function () {
secret2.remove();
});
var secret1 = new Secret( '#secret1' ),
secret2 = new Secret( '#secret2' );
secret1.on( 'secret-apply', function () {
secret2.apply();
});
secret1.on( 'secret-remove', function () {
secret2.remove();
});
var secret1 = new Secret( '#secret1' ),
secret2 = new Secret( '#secret2' );
secret1.on(
'secret-apply', function () {
secret2.apply();
});
secret1.on( 'secret-remove', function () {
secret2.remove();
});
var secret1 = new Secret( '#secret1' ),
secret2 = new Secret( '#secret2' );
secret1.on( 'secret-apply', function () {
secret2.apply();
});
secret1.on( 'secret-remove', function () {
secret2.remove();
});
var secret1 = new Secret( '#secret1' ),
secret2 = new Secret( '#secret2' );
secret1.on( 'secret-apply', function () {
secret2.apply();
});
secret1.on(
'secret-remove', function () {
secret2.remove();
});
var secret1 = new Secret( '#secret1' ),
secret2 = new Secret( '#secret2' );
secret1.on( 'secret-apply', function () {
secret2.apply();
});
secret1.on( 'secret-remove', function () {
secret2.remove();
});
var secret1 = new Secret( '#secret1' ),
secret2 = new Secret( '#secret2' );
secret1.on( 'secret-apply', function () {
secret2.apply();
});
secret1.on( 'secret-remove', function () {
secret2.remove();
});
$.each( $( '.slice' ), function ( index, slice ) {
var secret = new Secret( $( slice ), {
applyEvent: 'mouseover',
removeEvent: 'mouseout'
});
});
$.
each( $(
'.slice' ), function ( index, slice ) {
var secret = new Secret( $( slice ), {
applyEvent: 'mouseover',
removeEvent: 'mouseout'
});
});
$.each( $( '.slice' ), function ( index, slice ) {
var secret =
new Secret( $( slice ), {
applyEvent: 'mouseover',
removeEvent: 'mouseout'
});
});
$.each( $( '.slice' ), function ( index, slice ) {
var secret = new Secret( $( slice ), {
applyEvent: 'mouseover',
removeEvent: 'mouseout'
});
});
$.each( $( '.slice' ), function ( index, slice ) {
var secret = new Secret( $( slice ), {
applyEvent: 'mouseover',
removeEvent: 'mouseout'
});
});
lego.js
github.com/tybenz/lego.js
THANKS!
github.com/tybenz • @tybenz
tybenz@adobe.com • tybenz.com