Hacer mejores elementos seleccionados con jQuery y CSS3

Al crear sus diseños web, siempre se esfuerza por lograr una apariencia uniforme en los diferentes navegadores. Desafortunadamente, uno de los elementos más fundamentales de su sitio web, los controles del navegador, también resultan ser los más difíciles de diseñar. Algunos de ellos, como el select elemento, son imposibles de cambiar más allá de cierta medida.

Es por eso que hoy estamos creando un script que tomará una selección normal. y reemplácelo con una versión más atractiva, potenciada por marcado, mientras mantiene intacta toda la funcionalidad.

El HTML

Como de costumbre, comenzamos con la parte HTML del tutorial. Estoy usando el marcado HTML5 ya que nos brinda algunas características útiles, como los atributos de datos, con el que podemos agregar datos arbitrarios al marcado de la página.

seleccionar-jquery.html

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Making Better Select Elements with jQuery and CSS3 | Tutorialzine Demo</title>

<link rel="stylesheet" type="text/css" href="css/styles.css" />

</head>
<body>

<div id="page">
    <h1>Your Product</h1>

    <form method="post" action="">

        <!-- We are going to use jQuery to hide the select element and replace it -->

        <select name="fancySelect" class="makeMeFancy">

            <!-- Notice the HTML5 data attributes -->

            <option value="0" selected="selected" data-skip="1">Choose Your Product</option>
            <option value="1" data-icon="img/products/iphone.png" data-html-text="iPhone 4&lt;i&gt;in stock&lt;/i&gt;">iPhone 4</option>
            <option value="2" data-icon="img/products/ipod.png" data-html-text="iPod &lt;i&gt;in stock&lt;/i&gt;">iPod</option>
            <option value="3" data-icon="img/products/air.png" data-html-text="MacBook Air&lt;i&gt;out of stock&lt;/i&gt;">MacBook Air</option>
            <option value="4" data-icon="img/products/imac.png" data-html-text="iMac Station&lt;i&gt;in stock&lt;/i&gt;">iMac Station</option>
        </select>
    </form>

</div>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js"></script>
<script src="js/script.js"></script>

</body>
</html>

Puede ver que estamos usando los datos atributos para incrustar información en la opción elementos del seleccionar . Estamos incluyendo un ícono de producto y una descripción de texto enriquecido, los cuales se muestran más tarde en la versión mejorada del elemento seleccionado.

Establecí un salto de datos arbitrario atributo en el primer elemento, para que nuestro script sepa que no debe incluirlo en la lista generada. Alternativamente, puede verificar la existencia del icono de datos y datos-html-texto atributos y omita el elemento si es necesario.

En la parte inferior del documento se incluye la versión 1.4.3 de jQuery (la última versión de la biblioteca a la fecha de este escrito) y nuestro script.js, que puede ver en el siguiente paso.

JQuery

En el evento document.ready, jQuery inspecciona el elemento seleccionado y usando los atributos de datos, construye el marcado que puede ver a continuación, que se agrega justo después de seleccionar :

<div style="width: 144px;" class="tzSelect">
    <div class="selectBox">iMac Station</div>
    <ul class="dropDown">
        <li><img src="img/products/iphone.png"><span>iPhone 4<i>in stock</i></span></li>
        <li><img src="img/products/ipod.png"><span>iPod <i>in stock</i></span></li>
        <li><img src="img/products/air.png"><span>MacBook Air<i>out of stock</i></span></li>
        <li><img src="img/products/imac.png"><span>iMac Station<i>in stock</i></span></li>
    </ul>
</div>

Como puede ver, básicamente estamos construyendo una lista desordenada, con un elemento li que representa cada opción de select . El cuadro de selección en sí está representado por un div con un .selectBox clase.

Ahora echemos un vistazo más de cerca a cómo se genera este código.

js/script.js

$(document).ready(function(){

    // The select element to be replaced:
    var select = $('select.makeMeFancy');

    var selectBoxContainer = $('<div>',{
        width       : select.outerWidth(),
        className   : 'tzSelect',
        html        : '<div class="selectBox"></div>'
    });

    var dropDown = $('<ul>',{className:'dropDown'});
    var selectBox = selectBoxContainer.find('.selectBox');

    // Looping though the options of the original select element

    select.find('option').each(function(i){
        var option = $(this);

        if(i==select.attr('selectedIndex')){
            selectBox.html(option.text());
        }

        // As of jQuery 1.4.3 we can access HTML5
        // data attributes with the data() method.

        if(option.data('skip')){
            return true;
        }

        // Creating a dropdown item according to the
        // data-icon and data-html-text HTML5 attributes:

        var li = $('<li>',{
            html:   '<img src="'+option.data('icon')+'" /><span>'+
                    option.data('html-text')+'</span>'
        });

        li.click(function(){

            selectBox.html(option.text());
            dropDown.trigger('hide');

            // When a click occurs, we are also reflecting
            // the change on the original select element:
            select.val(option.val());

            return false;
        });

        dropDown.append(li);
    });

    selectBoxContainer.append(dropDown.hide());
    select.hide().after(selectBoxContainer);

    // Binding custom show and hide events on the dropDown:

    dropDown.bind('show',function(){

        if(dropDown.is(':animated')){
            return false;
        }

        selectBox.addClass('expanded');
        dropDown.slideDown();

    }).bind('hide',function(){

        if(dropDown.is(':animated')){
            return false;
        }

        selectBox.removeClass('expanded');
        dropDown.slideUp();

    }).bind('toggle',function(){
        if(selectBox.hasClass('expanded')){
            dropDown.trigger('hide');
        }
        else dropDown.trigger('show');
    });

    selectBox.click(function(){
        dropDown.trigger('toggle');
        return false;
    });

    // If we click anywhere on the page, while the
    // dropdown is shown, it is going to be hidden:

    $(document).click(function(){
        dropDown.trigger('hide');
    });
});

En la carga de la página, el script explora las opciones del elemento seleccionado y genera el marcado de acuerdo con los atributos de datos HTML5 que contienen estos elementos. A partir de jQuery 1.4.3, es posible acceder a los valores de estos atributos directamente con jQuery data() método. Esta es una característica realmente útil, que hace que sea realmente fácil leer los datos incrustados.

El elemento de selección original no se destruye, solo se oculta con hide() método. Esto es importante porque, como puede ver en el código anterior, estamos reflejando cualquier cambio de la selección en ese elemento de selección original. De esta forma, cuando utilice la selección como parte de un formulario, los valores se registrarán correctamente y se pasarán a su secuencia de comandos de backend.

Ahora que tenemos nuestro código en su lugar, echemos un vistazo más de cerca a la magia de CSS3 que lo hace todo posible.

El CSS

Como puede ver en el marcado en la parte superior del paso anterior, solo estamos usando una cantidad mínima de marcado para mostrar el cuadro de selección y el menú desplegable. Si nos limitáramos a usar técnicas anteriores a CSS3, tendríamos que agregar muchos más divs y spans.

css/estilos.css

#page{
    width:230px;
    margin:100px auto;
}

#page h1{
    font-weight:normal;
    text-indent:-99999px;
    overflow:hidden;
    background:url('../img/your_product.png') no-repeat;
    width:230px;
    height:36px;
}

#page form{
    margin:20px auto;
    width:200px;
}

.tzSelect{

    /* This is the container of the new select element */

    height:34px;
    display:inline-block;
    min-width:200px;
    position:relative;

    /* Preloading the background image for the dropdown */
    background:url("../img/dropdown_slice.png") no-repeat -99999px;
}

.tzSelect .selectBox{
    position:absolute;

    height:100%;
    width:100%;

    /* Font settings */

    font:13px/34px "Lucida Sans Unicode", "Lucida Grande", sans-serif;
    text-align:center;
    text-shadow:1px 1px 0 #EEEEEE;
    color:#666666;

    /* Using CSS3 multiple backgrounds and a fallback */

    background:url('../img/select_slice.png') repeat-x #ddd;
    background-image:url('../img/select_slice.png'),url('../img/select_slice.png'),url('../img/select_slice.png'),url('../img/select_slice.png');
    background-position:0 -136px, right -204px, 50% -68px, 0 0;
    background-repeat: no-repeat, no-repeat, no-repeat, repeat-x;

    cursor:pointer;

    -moz-border-radius:3px;
    -webkit-border-radius:3px;
    border-radius:3px;
}

.tzSelect .selectBox:hover,
.tzSelect .selectBox.expanded{
    background-position:0 -170px, right -238px, 50% -102px, 0 -34px;
    color:#2c5667;
    text-shadow:1px 1px 0 #9bc2d0;
}

CSS3 nos permite asignar múltiples imágenes de fondo a los elementos simplemente agregando múltiples url() declaraciones divididas por una coma. Se agregan al elemento de arriba a abajo, y cada fondo consecutivo se muestra debajo del anterior.

Actualmente, Firefox, Safari, Chrome y Opera admiten varios fondos. Para Internet Explorer y versiones anteriores de los primeros navegadores, se define un respaldo, que es básicamente una versión normal del fondo. Al analizar el documento CSS, los navegadores que no comprenden varios antecedentes simplemente ignorarán la regla y usarán la simple.

.tzSelect .dropDown{
    position:absolute;
    top:40px;
    left:0;
    width:100%;
    border:1px solid #32333b;
    border-width:0 1px 1px;
    list-style:none;

    -moz-box-sizing:border-box;
    -webkit-box-sizing:border-box;
    box-sizing:border-box;

    -moz-box-shadow:0 0 4px #111;
    -webkit-box-shadow:0 0 4px #111;
    box-shadow:0 0 4px #111;
}

.tzSelect li{
    height:85px;
    cursor:pointer;
    position:relative;

    /* Again, using CSS3 multiple backgrounds */

    background:url('../img/dropdown_slice.png') repeat-x #222;
    background-image:url('../img/dropdown_slice.png'),url('../img/dropdown_slice.png'),url('../img/dropdown_slice.png');
    background-position: 50% -171px, 0 -85px, 0 0;
    background-repeat: no-repeat, no-repeat, repeat-x;
}

.tzSelect li:hover{
    background-position: 50% -256px, 0 -85px, 0 0;
}

.tzSelect li span{
    left:88px;
    position:absolute;
    top:27px;
}

.tzSelect li i{
    color:#999999;
    display:block;
    font-size:12px;
}

.tzSelect li img{
    left:9px;
    position:absolute;
    top:13px;
}

El tamaño de la caja propiedad que he usado para .dropDown clase, especifica cómo los bordes se suman al tamaño total del elemento. Normalmente, los bordes aquí aumentarían el ancho total en 2px y arruinarían la alineación. Con tamaño de caja establecer en cuadro de borde , sin embargo, el ancho total no excederá el especificado en la definición y los bordes ocuparán espacio en el interior.

¡Con esto, nuestro cuadro de selección impulsado por jQuery y CSS3 está completo!

Palabras de despedida

En este tutorial demostramos algunas de las características útiles que se introdujeron con jQuery 1.4.3 y un poco más de las capacidades de CSS3. Lo bueno de este script es que mantiene el cuadro de selección original oculto en la página y cambia su valor de acuerdo con el reemplazo elegante. De esta manera, cuando envía el formulario, también se transmite el valor correcto.