Ya en enero de este año jQuery anunció un nuevo registro de plugins , así que ahora me pareció un gran momento para escribir un tutorial que combinara la construcción de un plugin jQuery con mi pasión: las tecnologías web en tiempo real.

Las tecnologías web en tiempo real hacen que sea muy fácil agregar contenido en vivo a páginas web previamente estáticas. El contenido en vivo puede dar vida a una página, retener a los usuarios y eliminar la necesidad de actualizar la página periódicamente. Las actualizaciones en tiempo real generalmente se logran conectándose a una fuente de datos, suscribiéndose a los datos que desea agregar a la página y luego actualizando la página a medida que llegan los datos. Pero ¿por qué no se puede lograr esto simplemente marcando una página para identificar qué datos se deben mostrar y dónde? Bueno, ¡quizás sí!

El eslogan de jQuery es escribir menos, hacer más . El lema del plugin jQuery Realtime que vamos a compilar en este tutorial será escribir menos, hacer en tiempo real.

En este tutorial crearemos un plugin de jQuery que hace que sea realmente fácil agregar contenido en tiempo real a una página simplemente agregando algo de marcado. Primero, cubriremos cómo usar un servicio llamado Arribista suscribirse a datos en tiempo real. A continuación, definiremos una forma de marcar un documento HTML5 con los atributos 'data- *' de modo que nuestro plugin jQuery en tiempo real pueda consultarlo y convertirlo a suscripciones de datos en tiempo real. Finalmente, crearemos el plugin jQuery que usará los atributos para suscribirse a datos y mostrar al instante las actualizaciones dentro de la página.

Si solo quieres bucear directamente puedes ver una demostración en acción o puedes descarga el código y comienza a piratear

Conceptos básicos de Pusher

Pusher es un servicio alojado que facilita agregar contenido en tiempo real y experiencias interactivas a aplicaciones web y móviles. Aquí simplemente conectaremos, suscribiremos algunos datos y luego actualizaremos una página cuando ingresen los datos.

Para demostrar esto, crea un archivo llamado 'ejemplo.html' e incluye la biblioteca de JavaScript Pusher desde el CDN de Pusher. Sabemos que vamos a usar jQuery 2.0.0, así que también deberíamos incluir eso ahora:

Creating a realtime jQuery plugin | Webdesigner Depot

Conectar

Una vez que se ha incluido la biblioteca de JavaScript de Pusher, podemos conectarnos a Pusher creando una nueva instancia de 'Pusher' y pasando una clave de aplicación. Crear un adicional '

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:

Veamos cómo crear la funcionalidad esperada.

Un plugin en tiempo real

Primero, crea un archivo 'realtime.jquery.js' en el directorio 'src'. Este archivo contendrá la funcionalidad del complemento. A continuación, agregue lo siguiente al archivo como punto de partida de nuestro complemento:

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

Incluso podemos probar esto ahora. En 'examples / bitcoin / index.html', elimine el plugin de ejemplo '

Si actualizas la página ahora, ¡verás 'en tiempo real'! conectado a la consola de JavaScript junto con el HTML de '

'elemento. Esto es genial ya que significa que el complemento está funcionando; estamos ejecutando con éxito nuestra funcionalidad de complemento en la tabla identificada por el selector que pasamos a jQuery.

pusher_005

Complementos jQuery y bibliotecas de terceros

Nuestro plugin en tiempo real se basa en una biblioteca de terceros: la biblioteca JavaScript de Pusher. Por el momento lo tenemos incluido estáticamente dentro de nuestro HTML, pero no queremos que sea un requisito para usar el complemento. Entonces, carguemos dinámicamente. jQuery proporciona una forma de hacerlo fácilmente en forma de '.getScript ()' función.

Entonces, carguemos la versión 2.0 de la biblioteca JavaScript de Pusher. Cargaremos la versión alojada de HTTPS para que los navegadores estén contentos si nuestro complemento se usa en una página servida a través de HTTPS (Chrome ya bloquea los intentos de cargar scripts HTTP alojados en páginas HTTPS y Firefox lo hará en Firefox 23 ) Voy a ajustar la carga de la biblioteca en una función de la siguiente manera:

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 );}  );} función pusherLoaded (script, textStatus) {libraryLoaded = true; console.log ('pusher.min.js loaded:' + textStatus);} loadPusher (); 

Si vuelve a cargar la página, se registrará el mensaje 'pusher.min.js loaded: success' en la consola.

A medida que desarrollamos, siempre es bueno tener una forma de registrar la información, así que en este punto creemos una función de 'registro' simple que podemos usar y que simplemente se conecta a la consola. Usaremos esto ahora y también lo usaremos para registrar eventos de Pusher. La fuente completa del complemento ahora es:

( 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 );}  );} función pusherLoaded (script, textStatus) {libraryLoaded = true; Pusher.log = log; log ('pusher.min.js loaded:' + textStatus);} $. fn.realtime = function () {log (' en tiempo real! '); log ($ (this) .html ());}; loadPusher ();} (jQuery)); 

También notará que hemos asignado la función 'log' a la propiedad 'Pusher.log'. Esto significa que podemos ver el registro interno de la biblioteca Pusher, así como el nuestro.

¿Cuándo deberíamos conectarnos?

Debido a la naturaleza asíncrona de cargar la biblioteca, no podemos garantizar que se habrá cargado cuando nuestro complemento se llame a la acción. Desafortunadamente, esto hace que las cosas sean un poco más complejas de lo que es ideal, pero trataremos de resolverlas de la manera más simple posible.

Necesitamos verificar si la biblioteca se ha cargado, de ahí el indicador 'libraryLoaded', y actuar apropiadamente; si la biblioteca se ha cargado, podemos conectarnos, si no es así, tenemos que poner en cola la ejecución hasta que lo haga. Debido a esto, tiene más sentido crear solo la instancia de Pusher cuando realmente la necesitamos, que es cuando realmente queremos suscribirnos a los datos.

Veamos cómo podemos hacer eso:

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 );}};

Cuando se llama al complemento, verificamos el indicador 'libraryLoaded' para ver si se ha cargado la biblioteca de JavaScript de Pusher. Si es así, estamos listos y podemos suscribirnos. Si todavía está pendiente, debemos poner en cola las suscripciones. Hacemos esto presionando la colección jQuery ('els') en una matriz 'pendiente'.

Ahora, conecta

Ahora que sabemos que la biblioteca JavaScript de Pusher se ha cargado y que la página quiere suscribirse a los datos, podemos crear nuestra instancia 'Pusher'. Como solo queremos una instancia de 'Empujador' por página, vamos a seguir el Patrón Singleton y tener 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;}

Esta función toma la etiqueta del script del complemento buscando una etiqueta con un atributo 'src' que finaliza con 'jquery.realtime.js', y luego obtiene el valor del atributo 'data-rt-key'. Luego crea una nueva instancia 'Pusher', pasando la clave. Como se discutió anteriormente, al crear una nueva instancia 'Pusher' se establece una conexión con la fuente de nuestros datos.

Suscribir

Ahora podemos usar la función 'getPusher ()' cada vez que queremos acceder a la instancia 'Pusher'. En nuestro caso, queremos usarlo cuando analizamos los elementos para determinar las suscripciones.

Actualice la función 'suscribir' del marcador de posición y agregue las funciones adicionales que se muestran a continuación:

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 función 'buscar' es una función de utilidad para obtener cualquier elemento de una colección existente que coincida con un selector dado usando '.filtrar()', junto con cualquier descendiente de los elementos usando '.encontrar()'. Utilizamos esta función para encontrar los elementos marcados para representar las suscripciones de canales (atributo 'data-rt-channel') y para cada uno de ellos llamamos 'subscribeChannel'. Esta función extrae el nombre del canal al que se va a suscribir y usa el valor para llamar a 'pusher.subscribe (channelName)' para suscribirse al canal.

Enlazar

Necesitamos encontrar los elementos marcados para representar eventos (atributo 'data-rt-event') a los cuales vincularse:

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 ) {}

Para cada elemento de evento, encontramos llamar a nuestra propia función 'bind' que se une al evento en el canal usando 'channel.bind (eventName, eventHandler)'. La función del controlador de eventos es un pequeño cierre que nos permite pasar la actualización de datos, cuando se recibe, y el elemento de evento a una función 'displayUpdate'.

Si ejecutamos esto ahora podemos ver en el registro que se está estableciendo una conexión, estamos encontrando un canal y suscribiéndonos a él, y encontrando un evento al cual vincularnos.

pusher_006

Marcado en tiempo real de jQuery que encuentra la suscripción al canal y la vinculación del evento

Mostrar la actualización

Cuando se llama al controlador de eventos, necesitamos encontrar el nombre de cada propiedad en el objeto 'datos' (por ejemplo, último, bajo, alto y volumen) enviado con la actualización y encontrar cualquier elemento que esté marcado con ese nombre.

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 );}}

Recorrimos el objeto 'datos' y obtenemos el nombre de cada propiedad. Una vez que conocemos el nombre de la propiedad ('propName') podemos encontrar los elementos asociados y actualizar su valor de texto con el nuevo valor de datos. Por ahora, no vamos a admitir objetos con ningún tipo de jerarquía; solo queremos un nivel de clave y pares de valores.

Si ahora actualiza la página y activa un evento desde el Creador de eventos de Pusher, los nuevos datos se mostrarán instantáneamente dentro de la página.

¿Has trabajado con un servicio de datos en vivo? ¿Qué lecciones aprendiste? Háganos saber en los comentarios.

Imagen / miniatura destacada, imagen de datos en vivo a través de Shutterstock.