FrameWarp:complemento de jQuery para mostrar páginas en una superposición ordenada

Mientras trabajaba en una nueva y emocionante aplicación web, descubrí que necesitaba una forma de mostrar ciertas páginas en una ventana superpuesta. Esto es útil si desea reutilizar algo como compartir o una página de configuración en diferentes pantallas de su aplicación. En lugar de armar algo que apenas hizo el trabajo, decidí tomarme el tiempo, hacerlo correctamente y compartirlo con ustedes.

Por supuesto, existe la opción de usar uno de los numerosos complementos de lightbox para hacer esto, pero el complemento que crearemos en este tutorial tiene muchas ventajas sobre un script de lightbox genérico:

  • Ligero:se creó específicamente para mostrar páginas, no imágenes;
  • Sin interfaz de usuario, por lo que la página parece una ventana de diálogo;
  • La página puede cerrarse sola y también puede enviar mensajes a la ventana principal;
  • Opcionalmente, puede usar un caché para cargas de página subsiguientes más rápidas;
  • Utiliza una animación CSS ordenada con un respaldo de JavaScript.

¡Excelente! Ahora comencemos.

La idea

Cuando se hace clic en un enlace o botón, nuestro complemento, denominado FrameWarp, detectará las coordenadas de ese elemento y activará una animación CSS de un polígono en expansión que se mueve hacia el centro de la ventana. Luego, el complemento cargará un Iframe que apunta a la URL que queremos mostrar. Si la página es del mismo origen que el sitio actual, FrameWarp también agregará dos métodos útiles al iframe:uno para ocultarlo y otro para enviar un mensaje al padre.

Usaremos la colección de herramientas jQuery++ para jQuery, que convierte el método animate() de la biblioteca para usar transiciones CSS3 en navegadores que las admitan. Esto hace que la construcción de animaciones CSS complejas sea bastante sencilla.

La Animación

Como dicen, un violín vale más que 1000 palabras. Así que aquí está la animación en acción (presione el Resultado pestaña):

El truco aquí es que estamos animando las propiedades del borde del elemento y el ancho, mientras que la altura sigue siendo 0. Los bordes izquierdo y derecho se configuran como transparentes en el CSS del complemento. Alternativamente, podría hacerlo con transformaciones 3D CSS, pero no funcionaría en navegadores más antiguos.

El complemento

Ahora a escribir el plugin. Vamos a envolver nuestro código en una función anónima para que quede aislado del resto de la página. En efecto, todas las variables y funciones auxiliares que puede ver a continuación son privadas y solo accesibles para nuestro complemento.

activos/framewarp/framewarp.js

(function($){

    // Private varialble deffinitions

    var body = $('body'),
        win = $(window),
        popup, popupBG;

    var frameCache = {};
    var frameCacheDiv = $('<div class="frameCacheDiv">').appendTo('body');
    var currentIframe;

    $.fn.frameWarp = function(settings){

        // The main code of the plugin will go here

    };

    // Helper Functions

    function hide(){

        // Here we will remove the popup and dark background from the page

    }

    function setUpAPI(iframe, settings){

        // In this function, we will make two API methods available to the frame,
        // if it the page is from the same domain.
    }

    function sameOrigin(url){

        // Here we will determine whether the page is from the same domain
    }

    function getOrigin(url){

        // A helper function for generating an origin string
        // of the type: https://www.google.com
        // This includes the protocol and host.
    }

})(jQuery);

El complemento crea un div con un nombre de clase frameCacheDiv. Contendrá los iframes que estamos agregando a la página. El complemento agrega dos divs más a la página:.popup y .popupBG, que discutiremos en un momento. Ahora inspeccionemos las funciones auxiliares.

function hide(){

    if(currentIframe){
        currentIframe.hide();
        currentIframe = null;
    }

    popupBG.remove();
    popup.remove();
}

function setUpAPI(iframe, settings){

    if(sameOrigin(settings.url)){

        // Exposing a minimal API to the iframe
        iframe[0].contentWindow.frameWarp = {
            hide: hide,
            sendMessage:function(param){
                return settings.onMessage(param);
            }
        };
    }
}

function sameOrigin(url){

    // Compare whether the url belongs to the
    // local site or is remote

    return (getOrigin(url) == getOrigin(location.href));
}

function getOrigin(url){

    // Using an anchor element to
    // parse the URL

    var a = document.createElement('a');
    a.href = url;

    return a.protocol+'//'+a.hostname;
}

Los navegadores implementan una característica de seguridad llamada "política del mismo origen" que limita el acceso de un sitio web al DOM de otro. Por esta razón, tenemos una función auxiliar que compara la URL del iframe con la dirección de la página actual. Solo cuando el dominio y el protocolo coincidan, el complemento intentará acceder al DOM del iframe y agregará los métodos API para enviar mensajes y ocultarlos.

¡Ahora estamos listos para escribir el complemento frameWarp real!

$.fn.frameWarp = function(settings){

    // Supplying default settings

    settings = $.extend({
        cache: true,
        url: '',
        width:600,
        height:500,
        closeOnBackgroundClick: true,
        onMessage:function(){},
        onShow:function(){}
    }, settings);

    this.on('click',function(e){

        e.preventDefault();

        var elem = $(this),
            offset = elem.offset();

        // The center of the button
        var buttonCenter = {
            x: offset.left - win.scrollLeft() + elem.outerWidth()/2,
            y: offset.top - win.scrollTop() + elem.outerHeight()/2
        };

        // The center of the window
        var windowCenter = {
            x: win.width()/2,
            y: win.height()/2
        };

        // If no URL is specified, use the href attribute.
        // This is useful for progressively enhancing links.

        if(!settings.url && elem.attr('href')){
            settings.url = elem.attr('href');
        }

        // The dark background

        popupBG = $('<div>',{'class':'popupBG'}).appendTo(body);

        popupBG.click(function(){

            if(settings.closeOnBackgroundClick){
                hide();
            }

        }).animate({    // jQuery++ CSS3 animation
            'opacity':1
        },400);

        // The popup

        popup = $('<div>').addClass('popup').css({
            width   : 0,
            height  : 0,
            top     : buttonCenter.y,
            left    : buttonCenter.x - 35
        });

        // Append it to the page, and trigger a CSS3 animation
        popup.appendTo(body).animate({
            'width'                 : settings.width,
            'top'                   : windowCenter.y - settings.height/2,
            'left'                  : windowCenter.x - settings.width/2,
            'border-top-width'      : settings.height,
            'border-right-width'    : 0,
            'border-left-width'     : 0
        },200,function(){

            popup.addClass('loading').css({
                'width': settings.width,
                'height': settings.height
            });

            var iframe;

            // If this iframe already exists in the cache
            if(settings.cache && settings.url in frameCache){
                iframe = frameCache[settings.url].show();
            }
            else{

                iframe = $('<iframe>',{
                    'src' : settings.url,
                    'css' : {
                        'width' : settings.width,
                        'height' : settings.height,
                    }
                });

                // If the cache is enabled, add the frame to it
                if(settings.cache){
                    frameCache[settings.url] = iframe;
                    iframe.data('cached',true);
                    settings.onShow();
                }
                else{

                    // remove non-cached iframes
                    frameCacheDiv.find('iframe').each(function(){
                        var f = $(this);
                        if(!f.data('cached')){
                            f.remove();
                        }
                    });
                }

                iframe.ready(function(){
                    frameCacheDiv.append(iframe);
                    setUpAPI(iframe, settings);
                    settings.onShow();
                });
            }

            currentIframe = iframe;

        });

    });

    return this;
};

Como mencioné en la sección inicial, estamos usando jQuery++ para mejorar la función animate() de jQuery para admitir animaciones CSS3. De esta manera, no tenemos que escribir toneladas de CSS y también logramos una compatibilidad total con versiones anteriores, ya que el nuevo método animate() volverá al anterior si el navegador no es compatible con las animaciones CSS.

Una vez que se completa la primera animación, agregamos la clase de carga al div .popup. La nueva clase agrega un gif de precargador animado a la ventana emergente y una sombra de cuadro suave, como puede ver al inspeccionar assets/framewarp/framewarp.css .

Uso del complemento

Para usar el complemento, incluya assets/framewarp/framewarp.css al encabezado de su página y assets/framewarp/framewarp.js después de su copia de la biblioteca jQuery.

Después de esto, todo lo que queda es inicializar el complemento. Como ejemplo, aquí está el código que impulsa nuestra página de demostración:

activos/js/script.s

$(function(){

    // If no url property is passed, the
    // href attribute will be used

    $('#b1').frameWarp();

    $('#b2').frameWarp({
        onMessage: function(msg){
            $('#messages').append('Message Received: '+ msg+'
');
        }
    });

    // Cache is enabled by default
    $('#b3').frameWarp({
        url : 'http://www.cnn.com/'
    });

    // Disable caching
    $('#b4').frameWarp({
        url : 'http://www.cnn.com/',
        cache:false
    });
});

¡Listo!

¡Con esto el complemento está completo! Puede usarlo para mejorar su aplicación web y reutilizar ciertas partes sin escribir código adicional. Me encantaría escuchar sus sugerencias o pensamientos en la sección de comentarios a continuación.