jueves, 30 de mayo de 2013

Simular "text-overflow:ellipsis" para textos largos ( multilínea )

En CSS, si utilizamos las siguientes propiedades:
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
Podemos conseguir que un texto se corte a la anchura que queramos y se inserten automáticamente unos puntos suspensivos al final.



El problema es que sólo funciona para una única línea. El texto se corta cuando la línea sobrepasa la anchura del contenedor. Necesitamos incluir white-space: nowrap para evitar que el texto que no cabe se desplace a la línea siguiente.
Sería muy util poder hacer también algo como esto:



Es decir, tenemos un texto largo, de varias líneas ( sin white-space: nowrap ), y queremos que se corte mostrando "..." cuando supera la altura del contenedor.

Ellipsis multilínea con JavaScript

Sólo son necesarias unas líneas de JavaScript para conseguirlo. Supongamos que tenemos un texto largo en un div contenedor con una altura limitada:
<div id="container">
  <p>
  Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
  </p>
</div>

Tenemos el siguiente CSS para limitar la altura y anchura del contenedor y ocultar el texto que no cabe:
#container {
  width: 300px;
  height: 95px;
  overflow: hidden;
}
El texto quedaría cortado pero no tenemos ninguna indicación de ello:



Podemos solucionar el problema con unas lineas de JavaScript (y JQuery):
var containerHeight = $("#container").height();
var $text = $("#container p");

while ( $text.outerHeight() > containerHeight ) {
        $text.text(function (index, text) {
            return text.replace(/\W*\s(\S)*$/, '...');
       });
}
El resultado es:



Lo que hace este pequeño script es ir recortando palabras del final del texto hasta que la altura del texto es igual a la altura del contenedor.Toda la magia se realiza con la expresión regular:
text.replace(/\W*\s(\S)*$/, '...');
Básicamente esta instrucción sustituye la última palabra, junto con cualquier espacio en blanco que tenga delante, por "...". Esto se va repitiendo hasta que el texto encaja dentro del contenedor.

Creando una función genérica

Podemos hacerlo más flexible creando una función que incluya los "..." en cualquier texto largo que sobrepase el contenedor. Pasaremos como parámetro el id del div que contiene el texto.
La siguiente función es válida siempre que el marcado HTML sea como en el ejemplo anterior,el texto debe estar englobado en un <p></p> que está dentro del elemento que se pasa como parámetro. No es dificil cambiar la función para otra estructura.
function createEllipsis ( containerId ) {
    $container = $("#" + containerId);
    var containerHeight = $container.height();
    var $text = $container.find("p");

    while ( $text.outerHeight() > containerHeight ) {
        $text.text(function (index, text) {
            return text.replace(/\W*\s(\S)*$/, '...');
        });
    }
  }

Se utilizaría:
createEllipsis("container");

Hay tambien una solución para hacer algo parecido con CSS descrita en el post Multiline ellipsis in pure css pero tiene varios problemas. Necesita algún marcado extra en el HTML y, la más grave, si la línea en la que cortamos es corta (un punto y aparte), los puntos suspensivos quedan siempre pegados a la derecha y queda un hueco en blanco.

sábado, 25 de mayo de 2013

JavaScript: Cómo comprobar si un elemento existe en el DOM

Es muy común tener que comprobar si un elemento HTML existe en el documento que estamos manejando. Vamos a ver a continuación dos formas de hacerlo, una con JavaScript puro y la otra utilizando jQuery.

Vamos a suponer que el elemento en cuestión tiene una id que para nuestro ejemplo será elementID.

JavaScript puro

En JavaScript podemos utilizar document.getElementById:


if ( document.getElementById( "elementID" )) {
      //...
}

En realidad estamos intentando seleccionar el elemento con id="elementID". Si el elemento existe, document.getElementById() lo devuelve ( es un elemento del DOM y equivale a true ). Si no existe devuelve null que equivale a false.

Usando jQuery

En jQuery no nos basta con intentar simplemente seleccionar el elemento:


// esto no funciona!!
if ( $("#elementID") ) {
      //...
}

No funciona porque jQuery siempre devuelve un array de elementos, aunque sólo haya uno o incluso aunque no haya ninguno. Si el elemento no existe devolverá un array vacío, que equivale a true. Por lo tanto tenemos que comprobar el número de elementos devueltos:


if ( $("#elementID").length ) {
     //...
}

Cuando el array está vacio $("#elementID").length es 0 y por lo tanto false, para cualquier otro número será true.

miércoles, 22 de mayo de 2013

No abuses de las librerias externas

Las librerias externas como jQuery o underscore son una gran ayuda para desarrollar aplicaciones grandes. Son librerias de utilidades genericas y normalmente su uso es apropiado. El problema viene cuando algunos desarrolladores buscan una librería para cada elemento que tienen que desarrollar. He trabajado en algun proyecto en el que me he encontrado más de veinte plugins de jQuery como parte de una applicación.


Programación Frankenstein: crear una aplicación monstruosa a base de 'coser' librerías que hacen alguna cosita

Esto es la programación Frankenstein, crear una aplicación monstruosa a base de 'coser' librerías que hacen alguna cosita. Si mi aplicacion necesita un mosaico de fotos, busco un plugin que me lo haga. Si necesito un scroll personalizado busco un plugin, si necesito visualizar las fotos como un carrusel, busco un plugin, etc, etc.

No quiero decir que no utilicemos una librería o un plugin cuando se adapte perfectamente a nuestras necesidades o cuando proporcione una funcionalidad que tardaríamos meses en desarrollar, pero el abuso de códigos externos nos puede llevar a crear un monstruo inmantenible.

La mayoría de las veces lo que necesitamos es una versión muy reducida de lo que proporciona la librería, que tiene que ser flexible para soportar las necesidades de usuarios diferentes. Habrá cientos de líneas que no utilicemos y algunos errores sin solucionar que nos obligarán a modificar un código que no conocemos.

Los problemas principales son:

  • Tenemos en la aplicación miles de líneas de código que no conocemos
  • Tenemos, probablemente, miles de líneas de código que no usamos
  • Aparecerán errores que nos obligarán a rastrear y modificar estas librerias externas
  • El cliente, tarde o temprano, solicitará alguna funcionalidad/cambio que nos obligará a cambiar el código de las librerias
  • Una vez que cambiemos un código externo, la actualizacion de las librerias se convierte en una pesadilla. Tendremos que pasar todos los cambios a las nuevas versiones o bien estar atentos a los cambios que solucionan errores e incorporarlos en nuestra versión

Cedric Dugas publica en su blog una excelente entrada sobre este tema titulada Muerte a las librerías monolíticas en el que expone algunos problemas que ha encontrado con el abuso de librerias y propone hacerse las siguientes preguntas antes de incorporar código de terceros:

  • ¿Puedo programarlo yo fácilmente?
  • ¿Tengo tiempo para hacerlo?
  • ¿Que navegadores soporta esta librería?
  • ¿Cuantos errores sin resolver tiene ( open issues )?
  • ¿Que tamaño tiene?
  • ¿El autor es de confianza?

Me parece una buena check list para reflexionar un poco antes de 'crear un monstruo'.

Fuentes:

Death to monolithic libraries