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

Source.

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

Source.

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?

Example.

<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...

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.

AKA. Edit that Rich DOM!

Demo & Library

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,962