JavaScript can do WHAT?!
What are we covering?
A brief history of JavaScript (90s, 00s, Prototype, jQuery, Backbone, TodoMVC, Virtual DOM, Node, Servers, Desktop)
What are we covering?
The latest ECMAScript standards (ES6+). Discover native classes, promises for easy async, where promises fall short. How to compile ES6+ code today (Babel).
What are we covering?
Short history of Node.js, it’s usages over time (servers, command line apps, robotics, desktop apps), things to know
What are we covering?
Build tools and generators: Introduction to Grunt, Gulp, NPM Scripts, and Static Site Generators like DocPad
What are we covering?
Browser innovations like WebRTC for webcam, microphone, and peer 2 peer communication - think video chat apps and netflix
What are we covering?
Browser hinderances like the DOM and the introduction of the Virtual DOM (React.js) as well as creating native iOS applications using React’s Virtual DOM using React Native
Path to JavaScript
Hi JavaScript (Netscape)!
Hi JScript (MS)!
Hi VBScript (MS)!
<script>
window.app = {}
app.validate = function () {
return (
document.getElementById('name').value
|| alert('Name is required!')
) && confirm('Are you sure?')
}
</script>
<form action="" method="post" onsubmit="return window.app.validate()">
<label for="name">Name:</label>
<input id="name" type="text" name="name">
<input type="submit" value="Delete the Internet">
</form>
Client-Side Form Interactions
Problems?
Would this cause Duplicate or Inconsistent Validation?
Solutions?
What if JavaScript could talk directly to the server?
// Request
var request = new XMLHttpRequest() // @QUESTION: What if this doesn't exist?
request.onreadystatechange = function () {
if ( this.readyState === (this.DONE || 4) ) {
try {
var result = JSON.parse(request.responseText)
}
catch (error) { // response was invalid json
throw new Error('Invalid response: ' + error.message)
}
if ( !result.success ) { // response had error
throw new Error(result.message || 'Unknown error.')
}
// success, handle result
}
}
request.open('POST', url, true)
request.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
request.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
request.send('data='+JSON.stringify(data))
Problems?
Will this work in other browsers?
XHR to the Rescue!
Solutions?
Maybe we can create a compatibility script?
Shims to the Rescue!
// Shim for IE Support
if (typeof XMLHttpRequest === "undefined") {
XMLHttpRequest = function () {
try { return new ActiveXObject("Msxml2.XMLHTTP.6.0") }
catch (e) {}
try { return new ActiveXObject("Msxml2.XMLHTTP.3.0") }
catch (e) {}
try { return new ActiveXObject("Microsoft.XMLHTTP") }
catch (e) {}
throw new Error("This browser does not support XMLHttpRequest.")
}
}
Problems?
Is this the only thing that needs a shim?
Solutions?
We have google, copy & paste, we'll be fine...
<script>
// ...
app.submit = function (form) {
if ( form.validated ) {
return true // continue with form submission
}
var name = document.getElementById('name').value
var data = {name: name}
var url = form.action || document.location.href
window.app.request(url, data, function (error, result) {
if ( error ) {
document.getElementById('error').innerHTML = error.message || error.toString()
form.validated = false // not validated, have the user try again
}
else {
form.validated = true
form.submit() // submit again, but this time it will continue through
}
})
return false // cancel form submission
}
</script>
<form action="" method="post" onsubmit="return window.app.submit(this)">
<div id="error"></div>
<label for="name">Name:</label>
<input id="name" type="text" name="name">
<input type="submit" value="Delete the Internet">
</form>
Problems?
Hrmm, this is starting to get complicated!
Server-Side Validation
Solutions?
Maybe time for libraries.
Path to Libraries
Prototype.js
// vanilla
var el = document.getElementById('myel')
if (el.classList) el.classList.add('pending')
else el.className += ' pending'
el.style.display = 'none'
// long-hand
var el = document.getElementById('myel')
Element.extend(myel)
el.addClassName('pending').hide()
// short-hand
$('myel').addClassName('pending').hide()
// however, prototype monkey patches, so this works
var el = document.getElementById('myel')
el.addClassName('pending').hide()
Problems?
Conflicts with native instances and classes?
Solutions?
Maybe we should provide new APIs?
AKA. Let's shim+awesomify the bundled APIs.
Also brought early classes.
MooTools
// vanilla
var el = document.getElementById('myel')
if (el.classList) el.classList.add('pending')
else el.className += ' pending'
el.style.display = 'none'
// shorthand
$('myel').addClass('pending').hide()
// note, one monkey patch still exists
document.id('myel').addClass('pending').hide()
Problems?
Sometimes a lot of plumbing is needed...
Solutions?
What if there was a simpler way to extend things?
AKA. Let's shim+awesomify the new APIs.
Also brought early classes.
jQuery
// vanilla
var el = document.getElementById('myel')
if (el.classList) el.classList.add('pending')
else el.className += ' pending'
el.style.display = 'none'
// long-hand
var el = document.getElementById('myel')
$(el).addClass('pending').hide()
// short-hand
$('#myel').addClass('pending').hide()
// easy chainable extensions
jQuery.prototype.yell = $.fn.yell = function () {
alert(this.innerHTML)
}
$('#myel').yell()
Problems?
Great for views, but what about managing state?
Solutions?
What state management options are there?
AKA. Let's shim+awesomify new APIs.
Path to
State Management
Hacking Anchors to Hashes
Problems?
How many loads occur? What about SEO? Compat?
Solutions?
Maybe we need browsers to step up here!
HTML5 History API
Path to
Web Applications
JavaScript Templating
<div class="smarty">
{$myvar|default:"hi"|capitalize} <br/>
{assign var="myvar" value="hello"}
{$myvar|default:"hi"|capitalize}
</div>
<script>
$('.smarty').populate()
</script>
Problems?
Great for views, but what about data and actions?
Solutions?
Maybe we need to introduce Models and Controllers?
AKA. The server can do it, why can't we?
<div class="smarty">
Hi <br/>
Hello
</div>
=>
// Model
var Message = Backbone.Model.extend({})
var Messages = Backbone.Collection.extend({model: Message})
// View
var MessageList = Backbone.View.extend({
el: jQuery('.message-list').remove().first().prop('outerHTML'),
initialize: function () {
// listen to the collection to update the view
var messages = this.collection
messages.on('reset', this.render, this)
messages.on('add', this.addMessage, this)
messages.on('remove', this.removeMessage, this)
messsages.each(this.addMessage, this)
},
render: function () {
// render template, attach listeners, etc
},
addMessage: function () {
// render one message to the list
},
removeMessage: function () {
// remove one message from the list
}
})
// Controller
var messages = new Messages([new Message({text: 'hello')])
var messageList = new MessageList({collection: messages})
jQuery('.app').append(messageList.el)
Problems?
Is this the best way to do views? Seems complex.
Solutions?
What if there was a simpler way to extend things?
AKA. MVC for the Web.
Problems?
That's complicated... Can Browsers save us again?
Solutions?
Maybe templating and components can be native?
AKA. Let's try everything until we figure something out.
Web Components
<dom-module id="contact-card">
<link rel="import" type="css" href="contact-card.css">
<template>
<content></content>
<iron-icon icon="star" hidden$="{{!starred}}"></iron-icon>
</template>
<script>
Polymer({
is: 'contact-card',
properties: {
starred: Boolean
}
});
</script>
</dom-module>
<contact-card starred>
<img src="profile.jpg" alt="Eric's photo">
<span>Eric Bidelman</span>
</contact-card>
Problems?
Is cross-browser support simple? Packaging? Easier?
Solutions?
Is it possible to remove the complexity in dev land?
AKA. Lets get browsers to make the DOM awesome.
Virtual DOM
var TodoList = React.createClass({
render: function () {
var createItem = function (item) {
return <li key={item.id}>{item.text}</li>
}
return <ul>{this.props.items.map(createItem)}</ul>
}
})
var TodoApp = React.createClass({
getInitialState: function () {
return {items: [], text: ''}
},
onChange: function (e) {
this.setState({text: e.target.value})
},
handleSubmit: function (e) {
e.preventDefault()
var nextItems = this.state.items.concat([{
text: this.state.text, id: Date.now()
}])
var nextText = ''
this.setState({items: nextItems, text: nextText})
},
render: function () {
return (
<div>
<h3>TODO</h3>
<TodoList items={this.state.items} />
<form onSubmit={this.handleSubmit}>
<input onChange={this.onChange} value={this.state.text} />
<button>{'Add #' + (this.state.items.length + 1)}</button>
</form>
</div>
)
}
})
ReactDOM.render(<TodoApp />, mountNode)
Problems?
Well that's easy, what else can it do?
Solutions?
Well... everything...
AKA. Let's do it our way.
VDOM Targets
// iOS
var React = require('react-native')
var { TabBarIOS, NavigatorIOS } = React
var App = React.createClass({
render: function() {
return (
<TabBarIOS>
<TabBarIOS.Item title="React Native" selected={true}>
<NavigatorIOS initialRoute={{ title: 'React Native' }} />
</TabBarIOS.Item>
</TabBarIOS>
)
},
})
// Android
var React = require('react-native')
var { DrawerLayoutAndroid, ProgressBarAndroid } = React
var App = React.createClass({
render: function() {
return (
<DrawerLayoutAndroid
renderNavigationView={() => <Text>React Native</Text>}>
<ProgressBarAndroid />
</DrawerLayoutAndroid>
)
},
})
Problems?
So it can do everything! Do I need to know everything?
Solutions?
Well... maybe...
AKA. Native Everything!
Babel + JSX + ESNext + Redux + Flux + Router + Dispatcher + Actions + Stores + Providers + Reducers + Fetcher + Server + History + Middlewares + Generators + Thunks + Promises + Webpack + Rollup + Inline CSS + Themeing + React Dev Tools + React Native + Isomorphic + Immutable + Functional Programming
Path to
Desktop Applications
var http = require('http'),
hostname = '127.0.0.1',
port = 1337,
file = 'file.txt'
http.createServer(function (req, res) { // AMAZING
res.writeHead(200, {'Content-Type': 'text/plain'})
res.write('Sending contents of: ' + file + '\n')
fs.createReadStream(file).pipe(res) // AMAZING
}).listen(port, hostname, function () {
console.log('Server running at http://' + hostname + ':' + port + '/');
})
Problems?
New platform, new foundations? How to share?
Solutions?
Well... downloading zip files won't cut it.
AKA. JavaScript for all the things.
# Publish
mkdir multiply
npm init
echo "module.exports = function (a, b) { return a*b }" > index.js
npm publish
# Consume
mkdir multiplier
npm init
npm install --save multiply
echo "console.log(require('multiply')(5, 10))" > index.js
node index.js
Problems?
That's awesome! What about libraries & apps?
Solutions?
JavaScript developers unite!
AKA. JavaScript packages for all the things.
JavaScript Everything!
Even This!
Path to
Modern Applications
Content Editable
<section id="editable" contenteditable="true">
<h2>Go ahead, edit away!</h2>
<p>Here's a typical paragraph element</p>
<ol>
<li>and now a list</li>
<li>with only</li>
<li>three items</li>
</ol>
</section>
Problems?
Standards? Compatibility? Selections? GUIs?
Solutions?
Quite messy right now. Many attempts at solving it.
Web Sockets
// Server
// Create the HTTP server and serve our index.html
var server = require('http').createServer(function incoming(req, res) {
res.setHeader('Content-Type', 'text/html')
require('fs').createReadStream(__dirname + '/index.html').pipe(res)
})
// Attach Primus to the HTTP server.
var Primus = new (require('primus'))(server)
// Listen for connections and echo the events send.
primus.on('connection', function connection(spark) {
spark.on('data', function received(data) {
console.log(spark.id, 'received message:', data)
spark.write(data)
})
})
// Open server
server.listen(8080, function () {
console.log('Open http://localhost:8080 in your browser')
})
Problems?
Compatibility? Perhaps fallback to XHR?
Solutions?
All sorted by great libraries.
AKA. Proper Client <-> Server Communication
// Client
var outputEl = document.getElementById('output'),
echoEl = document.getElementById('echo')
// Tell primus to create a new default connection
var primus = new Primus()
// Listen for incoming data and log it in our textarea
primus.on('data', function received(data) {
outputEl.value += data + '\n'
})
// Listen for submits of the form so we can send messages
document.getElementById('write').onsubmit = function (e) {
if (e && e.preventDefault) e.preventDefault()
// Write the typed message
primus.write(echoEl.value)
echoEl.value = ''
}
Web RTC
<script>
navigator.mediaDevices.getUserMedia({audio:false, video:true}).then(function(stream) {
var videoTracks = stream.getVideoTracks()
stream.onended = function() {
console.log('Stream ended')
}
document.getElementById('webcam').srcObject = stream
})
</script>
<video id="webcam" autoplay></video>
Problems?
What about cross-browser?
Solutions?
Commitments from everyone except silent Apple.
AKA. P2P Media Streams.
ECMAScript Next
// vanilla
function A (name) {
this.name = name || 'Unknown'
}
A.prototype.greet = function (greeting) {
alert(
(greeting || 'Hello') +
' ' + this.name
)
}
function B (name) {
A.call(this, name)
}
B.prototype = Object.create(A.prototype)
B.prototype.hi = function () {
this.greet('Hi')
}
var b = new B()
b.hi()
Problems?
Would still need to be compiled right? Compat?
Solutions?
Use babel for that.
AKA. Let's make JavaScript AWESOME!
// esnext
class A {
constructor (name) {
this.name = name || 'Unknown'
}
greet (greeting) {
alert(`${greeting || 'Hello'} ${this.name}`)
}
}
class B extends A {
hi () {
this.greet('Hi')
}
}
const b = new B()
b.hi()
Follow Up Resources
JavaScript can do WHAT?!
By Benjamin Lupton
JavaScript can do WHAT?!
- 4,939