Già a gennaio di quest'anno, JQuery ne annunciò una nuova registro dei plugin , quindi ora sembrava un buon momento per scrivere un tutorial che combina la costruzione di un plugin jQuery con la mia passione: le tecnologie web in tempo reale.

Le tecnologie web in tempo reale semplificano l'aggiunta di contenuti live a pagine Web precedentemente statiche. I contenuti live possono portare una pagina in vita, trattenere gli utenti e rimuovere la necessità per loro di aggiornare periodicamente la pagina. Gli aggiornamenti in tempo reale vengono generalmente ottenuti collegandosi a una fonte di dati, sottoscrivendo i dati che si desidera aggiungere alla pagina e quindi aggiornando la pagina man mano che i dati arrivano. Ma perché non è possibile ottenerlo semplicemente marcando una pagina per identificare quali dati dovrebbero essere mostrati e dove? Beh, forse può!

La tagline di jQuery è scrivere meno, fare di più . La tagline per il plugin jQuery Realtime che stiamo per creare in questo tutorial sarà scritta meno, in tempo reale.

In questo tutorial creeremo un plugin jQuery che semplifica molto l'aggiunta di contenuti in tempo reale a una pagina semplicemente aggiungendo alcuni markup. Innanzitutto, tratteremo come utilizzare un servizio chiamato Pusher per iscriversi ai dati in tempo reale. Quindi definiremo un modo per marcare un documento HTML5 con attributi "data- *" in un modo che può essere interrogato dal nostro plugin jQuery in tempo reale e convertito in sottoscrizioni di dati in tempo reale. Infine, creeremo il plugin jQuery che utilizzerà gli attributi per iscriversi ai dati e visualizzare istantaneamente gli aggiornamenti all'interno della pagina.

Se vuoi semplicemente tuffarti dritto, puoi guarda una demo in azione o puoi scarica il codice e inizia a fare hacking.

Elementi fondamentali del pusher

Pusher è un servizio in hosting che semplifica l'aggiunta di contenuti in tempo reale ed esperienze interattive alle app web e mobile. Qui ci limiteremo a connetterti, iscriverti ad alcuni dati e poi aggiornare una pagina quando i dati arriveranno.

Per dimostrarlo, crea un file chiamato "example.html" e includi la libreria JavaScript Pusher dal CDN Pusher. Sappiamo che utilizzeremo jQuery 2.0.0, quindi dovremmo includerlo anche ora:

Creating a realtime jQuery plugin | Webdesigner Depot

Collegare

Una volta inclusa la libreria JavaScript Pusher, possiamo collegarci a Pusher creando una nuova istanza "Pusher" e passando una chiave dell'applicazione. Crea un ulteriore '

Note: For the tutorial we’ll use an application key that I’ve provided but for your own applications you’ll need to sign up to Pusher to get your own.

You can check that you’re connected in three different ways. You can do it manually by checking the Pusher Debug Console, if you load the page with the Pusher Debug Console open you’ll see the connection logged. The Pusher JavaScript library provides a log property that you can assign a function to and then you can manually check to make sure a connection has been established by inspecting the browser’s JavaScript console. Or you can check the connection programmatically by monitoring the connection state of the Pusher instance.

pusher_001

The Pusher Debug console

Whatever you choose to do, you’ll now be connected.

Subscribe

Pusher uses the Publish & Subscribe pattern, so to receive data from Pusher you first need to subscribe to it. Pusher uses the term channels when it comes to subscriptions, so let’s subscribe to a channel called ‘test-channel’.

As with connection state, you can check the status of a subscription in a few ways; using the Pusher Debug Console, by checking the output from ‘Pusher.log’ or by binding to the ‘pusher:subscription_succeeded’ event.

pusher_002

Using Pusher.log to log pusher-js library information

Bind to events

Those of you who use jQuery will probably be familiar with the idea of binding to events. jQuery does provide shortcuts for some events (e.g. ‘.onclick( )’) but you can also bind to events using ‘.bind(, )’. Pusher follows this convention and you can bind to events to be informed when something updates; when the connection state changes, when a subscription succeeds or when new application data is received. For this example, and with the realtime plugin, we’re interested primarily in the latter.

Let’s bind to a ‘test-event’ on the channel:

When binding to an event you simply identify the event by name and pass in a reference to a function that will be called when that event occurs (is triggered) on the channel.

If you have a Pusher account you can test that the ‘handleEvent’ function is called by using the Pusher Event Creator; enter ‘test-channel’ as the channel name, ‘test-event’ as the event name and some data (‘{ “some” : “data” }’) into the event data text area and click the submit button. You’ll then see the debug information, along with the data you entered, logged to the JavaScript console.

pusher_003 

Triggering an event from the Pusher Event Creator and logging it in the JavaScript console

Since the realtime jQuery plugin that we’re building doesn’t publish (trigger) data (it just consumes it) we won’t cover that here. But if you’re interested in finding out more checkout the Pusher server docs.

Displaying realtime updates

The next thing to consider is displaying the realtime data updates to the user.

For this we’ll need an idea for a simple application; having worked in finance for a few years I’m generally keen to avoid any type of financial example, but Bitcoin has made it interesting and relevant. So, let’s create a very simple display for showing Bitcoin prices.

Note: We’re going to use some fake data. Let’s make sure this doesn’t result in more Bitcoin panic selling!

First, let’s create some HTML where we’ll display the realtime prices. We can pre-populate the display with prices known at the time the page was loaded:

Bitcoin Fake Prices

LastLowHighVolume
BTC/USD61.157 USD51 USD95.713 USD66271 BTC / 4734629 USD

Let’s update the JavaScript to subscribe to a more appropriately named channel called ‘btc-usd’ and bind to a ‘new-price’ event:

The ‘data’ sent to the ‘handleEvent’ function should also be in a more appropriate format – here’s the JSON:

{"last": "last value","low": "low value","high": "high value","volume": "volume value"}

Now that we know this we can change the ‘handleEvent’ function to update the appropriate cell in the table:

function handleEvent( data ) {var cells = $( '#bitcoin_prices tbody tr td' );cells.eq( 1 ).text( data.last );cells.eq( 2 ).text( data.low );cells.eq( 3 ).text( data.high );cells.eq( 4 ).text( data.volume );}

If you now trigger a ‘new-price’ event on the ‘btc-usd’ channel, using the JSON we defined, the page will update to show the new values.

There are ways of both making this code nicer and, as the page grows to show more data, optimise things. But, we’re going to make it so that realtime data will be added to the page simply by applying markup.

Before we progress, let’s first add a bit of styling to the example. In the ‘’ add the following CSS:

As you can undoubtedly tell, I’m no designer. So please feel free to improve on this.

pusher_004

The “styled” Bitcoin Fake Prices application

Finally, restructure things so we’re set up for building the plugin.

  1. Create an ‘examples’ directory and within it a ‘bitcoin’ directory.
  2. Move the ‘example.html’ file to ‘examples/bitcoin’, rename it ‘index.html’.
  3. Create a ‘src’ directory at the top-level of the project.

The directory structure should now look as follows:

/
examples/
bitcoin/
index.html
src/

We’re now ready to define our realtime markup and build the realtime jQuery plugin.

Realtime markup

The first thing to highlight is that this isn’t a new idea — I worked for a company called Caplin Systems and in 2001 they had a technology known as RTML that let you markup a page so that realtime updates could be applied. The purpose here is to use jQuery to parse the page and then interpret the markup, resulting in subscriptions, event binding and ultimately live content being added to the page.

For our plugin we’ll use HTML5’s data-* attributes. These attributes don’t directly affect the layout or presentation of the page so they’re a great choice for our realtime markup.

The questions we now need to answer about the markup are:

  • Where do we put the Pusher application key?
  • How do we identify what channels should be subscribed to?
  • How do we identify the events that should be bound to on a channel?
  • How do we know what data to display in the page, and where?

The first one is relatively easy. Since we need to include our plugin JavaScript file we can add a ‘data-rt-key’ attribute to the ‘

So, from the script tag you can see we’re going to connect to Pusher using the key identified by ‘data-rt-key’. We’re going to subscribe to the ‘btc-usd’ channel and bind to the ‘new-price’ event. When an event is received we’re going to update the appropriate table cell based on the value indicated by ‘data-rt-value’; if the value of the attribute is ‘last’ then the value of the ‘last’ property is taken from the received ‘data’ object and displayed in the cell.

Hopefully what we are trying to achieve is now pretty clear. Let’s start looking at how to create a jQuery plugin.

jQuery plugin basics

The jQuery plugin creation docs are pretty good so I won’t go into the details here. We’ll simply concentrate on building the functionality that we need in our plugin.

Before we write any code we should consider how we want to use the plugin. The normal way a plugin functions is that you use jQuery to query the page, and then you execute the plugin functionality against the matched elements.

$( 'a' ).toggle();

The above code would find all ‘’ elements and then execute the ‘toggle()’ functionality on them — probably hiding all anchors, so not the most useful example you’ll ever see.

So, let’s say we would want to use the plugin as follows:

Diamo un'occhiata alla creazione della funzionalità prevista.

Un plugin in tempo reale

Innanzitutto, crea un file 'realtime.jquery.js' all'interno della directory 'src'. Questo file conterrà la funzionalità del plugin. Quindi aggiungi il seguente al file come punto di partenza del nostro plugin:

( function( $) {$.fn.realtime = function() {console.log( 'realtime!' );console.log( $( this ).html() );}  ;} (jQuery)); 

Possiamo persino testarlo ora. In 'examples / bitcoin / index.html' rimuovi il plugin di esempio '

Se aggiorni la pagina ora vedrai 'realtime!' registrato nella console JavaScript insieme con l'HTML dal '

'elemento. Questo è ottimo in quanto significa che il plugin funziona; stiamo eseguendo con successo la nostra funzionalità plugin sulla tabella identificata dal selettore passato a jQuery.

pusher_005

plugin jQuery e librerie di terze parti

Il nostro plugin in tempo reale si basa su una libreria di terze parti: la libreria JavaScript Pusher. Per il momento lo abbiamo incluso staticamente nel nostro HTML, ma non vogliamo rendere obbligatorio l'uso del plugin. Quindi, cariciamolo dinamicamente. jQuery fornisce un modo per farlo facilmente nella forma di '.getScript ()' funzione.

Quindi, cariciamo la versione 2.0 della libreria JavaScript Pusher. Caricheremo la versione ospitata HTTPS in modo che i browser siano soddisfatti se il nostro plug-in viene utilizzato su una pagina pubblicata su HTTPS (Chrome già blocca i tentativi di caricare script ospitati HTTP nelle pagine HTTPS e Firefox lo farà in Firefox 23 ). Ho intenzione di avvolgere il caricamento della libreria in una funzione come segue:

var libraryLoaded = false;function loadPusher() {$.getScript( "https://d3dy5gmtp8yhk7.cloudfront.net/2.0/pusher.min.js" ).done( pusherLoaded ).fail( function( jqxhr, settings, exception ) {console.log( 'oh oh! ' + exception );}  );} function pusherLoaded (script, textStatus) {libraryLoaded = true; console.log ('pusher.min.js caricato:' + textStatus);} loadPusher (); 

Se ricarichi la pagina, il messaggio "pusher.min.js caricato: successo" verrà registrato nella console.

Mentre stiamo sviluppando è sempre bene avere un modo di logging delle informazioni, quindi a questo punto creiamo una semplice funzione 'log' che possiamo usare che registra solo nella console. Lo useremo ora e lo useremo anche per registrare eventi Pusher. La fonte completa del plugin è ora:

( function( $ ) {function log( msg ) {console.log( msg );}var libraryLoaded = false;function loadPusher() {$.getScript( "https://d3dy5gmtp8yhk7.cloudfront.net/2.0/pusher.min.js" ).done( pusherLoaded ).fail( function( jqxhr, settings, exception ) {log( 'oh oh! ' + exception );}  );} function pusherLoaded (script, textStatus) {libraryLoaded = true; Pusher.log = log; log ('pusher.min.js caricato:' + textStatus);} $. fn.realtime = function () {log (' realtime! '); log ($ (this) .html ());}; loadPusher ();} (jQuery)); 

Noterai inoltre che abbiamo assegnato la funzione "registro" alla proprietà "Pusher.log". Ciò significa che possiamo vedere la registrazione interna della libreria Pusher così come la nostra.

Quando dovremmo connetterci?

A causa della natura asincrona del caricamento della libreria, non possiamo garantire che verrà caricato quando il nostro plug-in è chiamato in azione. Sfortunatamente questo rende le cose un po 'più complesse dell'ideale, ma cercheremo di risolverlo nel modo più semplice possibile.

Dobbiamo verificare se la libreria ha caricato - da qui il flag "libraryLoaded" - e agire in modo appropriato; se la libreria ha caricato possiamo collegarci, se non ce l'ha dobbiamo fare la coda dell'esecuzione finché non lo fa. Per questo motivo ha più senso creare l'istanza Pusher solo quando ne abbiamo veramente bisogno, ovvero quando vogliamo effettivamente iscriversi ai dati.

Diamo un'occhiata a come possiamo farlo:

var pending = [];function pusherLoaded( script, textStatus ) {libraryLoaded = true;while( pending.length !== 0 ) {var els = pending.shift();subscribe( els );}}function subscribe( els ) {}$.fn.realtime = function() {var els = this;if( libraryLoaded ) {subscribe( els );}else {pending.push( els );}};

Quando viene chiamato il plugin, controlliamo il flag "libraryLoaded" per vedere se la libreria JavaScript Pusher è stata caricata. Se è così, possiamo andare e possiamo iscriverci. Se è ancora in sospeso, è necessario accodare le sottoscrizioni. Lo facciamo spingendo la raccolta jQuery ('els') su una matrice 'in sospeso'.

Ora, collega

Ora che sappiamo che la libreria JavaScript Pusher è stata caricata e che la pagina vuole iscriversi ai dati possiamo creare la nostra istanza 'Pusher'. Perché vogliamo solo un'istanza "Pusher" per pagina che seguiremo Modello Singleton e avere un 'getPusher ()':

var pusher;function getPusher() {if( pusher === undefined ) {var pluginScriptTag = $("script[src$='jquery.realtime.js']");var appKey = pluginScriptTag.attr("data-rt-key");pusher = new Pusher( appKey );}return pusher;}

Questa funzione acquisisce il tag script del plugin cercando un tag con un attributo 'src' che termina con 'jquery.realtime.js', e quindi ottiene il valore dell'attributo 'data-rt-key'. Quindi crea una nuova istanza 'Pusher', passando la chiave. Come discusso in precedenza, la creazione di una nuova istanza "Pusher" si traduce in una connessione alla fonte dei nostri dati che vengono stabiliti.

sottoscrivi

Ora possiamo usare la funzione 'getPusher ()' ogni volta che vogliamo accedere all'istanza 'Pusher'. Nel nostro caso, vogliamo usarlo quando analizziamo gli elementi per determinare le sottoscrizioni.

Aggiorna la funzione 'subscribe' del segnaposto e aggiungi le funzioni aggiuntive mostrate di seguito:

function subscribe( els ) {var channelEls = els.find( "*[data-rt-channel]" );log( 'found ' + channelEls.size() + ' channels' );channelEls.each( subscribeChannel );}function subscribeChannel( index, el ) {el = $( el );var pusher = getPusher();var channelName = el.attr( 'data-rt-channel' );var channel = pusher.subscribe( channelName );}function find( els, selector ) {var topLevelEls = els.filter( selector );var childEls = els.find( selector );return topLevelEls.add( childEls );}

La funzione 'trova' è una funzione di utilità che consente di ottenere qualsiasi elemento da una raccolta esistente che corrisponde a un determinato selettore '.filtro()', insieme a qualsiasi discendente degli elementi che usano '.trova()'. Usiamo questa funzione per trovare tutti gli elementi contrassegnati per rappresentare gli abbonamenti dei canali (attributo 'data-rt-channel') e per ognuno chiamiamo 'subscribeChannel'. Questa funzione estrae il nome del canale da sottoscrivere e utilizza il valore chiamando "pusher.subscribe (channelName)" per iscriversi effettivamente al canale.

legare

Dobbiamo quindi trovare tutti gli elementi contrassegnati per rappresentare gli eventi (attributo 'data-rt-event') da associare a:

function subscribeChannel( index, el ) {el = $( el );var pusher = getPusher();var channelName = el.attr( 'data-rt-channel' );var channel = pusher.subscribe( channelName );var eventEls = find( el, '*[data-rt-event]' );log( 'found ' + eventEls.size() + ' events' );eventEls.each( function( i, el) {bind( el, channel );} );}function bind( el, channel ) {el = $( el );var eventName = el.attr( 'data-rt-event' );channel.bind( eventName, function( data ) {displayUpdate( el, data );} );}function displayUpdate( el, data ) {}

Per ogni elemento evento troviamo chiamare la nostra funzione 'bind' che si lega all'evento sul canale usando 'channel.bind (eventName, eventHandler)'. La funzione di gestione degli eventi è una piccola chiusura che ci consente di passare l'aggiornamento dei dati, quando ricevuti, e l'elemento evento a una funzione 'displayUpdate'.

Se eseguiamo questa operazione ora possiamo vedere dal logging che è stata stabilita una connessione, stiamo trovando un canale e ci stiamo iscrivendo e trovando un evento a cui collegarci.

pusher_006

jQuery markup in tempo reale che rileva l'iscrizione al canale e l'associazione degli eventi

Visualizza l'aggiornamento

Quando viene chiamato il gestore di eventi, dobbiamo trovare il nome di ogni proprietà sull'oggetto "data" (ad es. Last, low, high e volume) inviato con l'aggiornamento e trovare tutti gli elementi contrassegnati con quel nome.

function bind( el, channel ) {el = $( el );var eventName = el.attr( 'data-rt-event' );channel.bind( eventName, function( data ) {displayUpdate( el, data );} );}function displayUpdate( el, data ) {for( var propName in data ) {var value = data[ propName ];var updateEls = find( el, '*[data-rt-value="' + propName + '"]' );log( 'found ' + updateEls.size() + ' "' + propName + '" elements to update' );updateEls.text( value );}}

Effettuiamo un loop sull'oggetto 'data' e otteniamo il nome di ogni proprietà. Una volta che conosciamo il nome della proprietà ('propName') possiamo trovare gli elementi associati e aggiornare il loro valore di testo con il nuovo valore di dati. Per ora non stiamo andando a supportare oggetti con alcun tipo di gerarchia - vogliamo solo un livello di coppie chiave e valore.

Se ora aggiorni la pagina e attivi un evento dal Creatore di eventi Pusher, i nuovi dati verranno immediatamente visualizzati all'interno della pagina.

Hai mai lavorato con un servizio di dati in tempo reale? Quali lezioni hai imparato? Fateci sapere nei commenti.

Immagine in primo piano / miniatura, immagine dati live via Shutterstock.