Centrar texto verticalmente con CSS

Parece mentira, pero todavía tengo muchos problemas para centrar el texto verticalmente dentro de un div. Hoy he visto una solución que me ha gustado en Stack Overflow.

<div>
  <span>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</span>
</div>
div {
  width: 250px;
  height: 100px;
  line-height: 100px;
  text-align: center;
}

span {
  display: inline-block;
  vertical-align: middle;
  line-height: normal;
}

De este modo conseguimos lo que vemos en esta imagen, que los botones siempre tengan la misma altura, y queden bien con una o dos líneas.

double-line-button

WordPress search: Customizar búsqueda para encontrar diferentes tipos de entradas

Hoy he tenido la necesidad de customizar la búsqueda por defecto de Worpdress, que usamos en la web corporativa de Paradigma, para poder mostrar en una misma página de resultados diferentes tipos de entradas/posts/artículos.

Aunque he visto que hay un par de plugins que hacen esta funcionalidad, he optado por adaptar la búsqueda por medio de funciones en el fichero functions.php de mi tema.

El primer problema es que para poder realizar estas búsquedas no podemos usar los campos por defecto de wordpress (por ejemplo el título o el contenido), así que necesitamos ejecutar una acción que añada un campo oculto por cada uno de los campos que necesitaremos para la búsqueda:

add_action( "save_post", "add_title_custom_field");

function add_title_custom_field($postid){
    // since we removed the "s" from the search query, we want to create
    // some custom fields for every post_title & post_content
    update_post_meta($postid, "_post_title", $_POST["post_title"]);
    update_post_meta($postid, "_post_content", $_POST["post_content"]);
}

Está acción se ejecutará cada vez que salvemos un post, y tendremos que tenerla siempre activa, para que todas las entradas que actualicemos tengan esta información. Pero ¿qué podemos hacer con todos las entradas que ya tenemos creadas en nuestro wordpress? ¿Es necesario salvar cada una por separado? Pues por suerte no. Con esta función que os pongo a continuación, y que basta con que sea ejecutada UNA ÚNICA VEZ, actualizaremos todo el contenido de nuestra base de datos, sin necesidad de tener que ir salvando uno a uno cada artículo.

// create new fields on every post of your wordpress
function addNewFieldsToPosts() {
    global $post;
    $args = array(
        'numberposts' => -1,
         //  set here each post type you want to search
        'post_type' => array( 'post', 'portfolio', 'openings', 'events' )
        );
    $the_query = get_posts( $args );
    if ($the_query) {
        foreach ($the_query as $post) {
            update_post_meta($post->ID, "_post_title", $post->post_title);
            update_post_meta($post->ID, "_post_content", $post->post_content);
        }
    }
}
wp_reset_query();
add_action('wp_head', 'addNewFieldsToPosts');

Si no ejecutamos estas dos funciones no encontraremos resultados en nuestras búsquedas.

Vale, si , muy bonito, pero… ¿Y la función necesaria para adaptar la búsqueda de WordPress? Pues aquí la teneis:

// CUSTOM SEARCH RESULTS
function custom_search_query( $query ) {
    $custom_fields = array(
        // put all the meta fields you want to search for here
        "_post_title", // we must add this fields on update_post_meta function
        "_post_content",
        "long_description"
    );
    $searchterm = $query->query_vars['s'];
    // we have to remove the "s" parameter from the query,
    // because it will prevent the posts from being found
    $query->query_vars['s'] = "";
    if ($searchterm != "") {
        //  set here each post type you want to search
        $query->set('post_type', array('post', 'pages', 'events', 'portfolio' ));
        $meta_query = array('relation' => 'OR');
        foreach($custom_fields as $cf) {
            array_push($meta_query, array(
                'key' => $cf,
                'value' => $searchterm,
                'compare' => 'LIKE'
            ));
        }
        $query->set("meta_query", $meta_query);
    };
}
add_filter( "pre_get_posts", "custom_search_query");

Diferenciar resultados por tipo de entrada

En nuestra página de resultados de búsqueda (search.php) podremos diferenciar cada resultado por su tipo de este modo:

$post_type = get_post_type( $post_id );

Espero que os sean de utilidad estas funciones. A mi me han salvado el día.

Mi combinación ganadora para Internet Explorer 8

Cuando empezamos un nuevo proyecto siempre queremos aprovechar todo el potencial de HTML5, pero por desgracia no podemos olvidarnos de nuestro fiel amigo Internet Explorer 8.

Por suerte, ya hace tiempo que desterramos de nuestras maquetas a sus predecesores: Internet Explorer 6 y 7, pero IE8 se quedará con nosotros a tomar otro par de copitas más antes de irse.

Gracias a frameworks como bootstrap, foundation o paraGridma, podemos tener un esqueleto de aplicación moderno, pero perfectamente funcionar en IE8. En el desarrollo de paraGridma, el mayor problema que me encontre para adaptarlo a IE8 fue el de que aceptara algunas de las novedades de HTML5 y sobretodo el uso de media-queries.

Finalmente, opte por la combinación de dos librerías: html5shiv y respond.js.

Podemos incluir estas librerías por el método condicional tradicional:

<!--[if lt IE 9]>
  <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
  <script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
<![endif]-->

O también podemos incluirlo de esta otra forma.

<?php
  // because new Internet Explorer develop tools don't proccess conditional comments
  $conditionalComments = '';
  if (strpos($_SERVER['HTTP_USER_AGENT'],'MSIE 8.0') !== false) {
    $conditionalComments .= '<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>';
    $conditionalComments .= '<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>';
  }

  echo $conditionalComments;
?>

Es recomendable usar esta segundo método si estamos testando la web con las develop tools de explorer 11, ya que incomprensiblemente este navegador no entiende los comentarios condicionales cuando emulamos una versión anterior del navegador.

Y hasta aquí mi consejo de hoy. Espero que os haya sido de utilidad.

Adaptar el alto y ancho al embeber un iframe de Youtube, Vimeo, …

Uno de los problemas que surgen al hacer una web responsible es el de querer mostrar un iframe de Youtube, Vimeo, etc… Los iframes que nos facilitan estos proveedores suelen tener asociado una anchura y altura fijas, por lo que difícilmente se van a adaptar a nuestro flamante diseño fluido. Lo bueno, es que ya hace tiempo alguien muy sesudo se encontró con este problema, y le dio una solución de lo más sencilla, para que al altura del iframe siempre sea relativa a su anchura.

video_responsive

<div class="responsiveContent">
    <iframe src="//www.youtube.com/embed/_veOIvMWazU" 
            allowfullscreen="" frameborder="0"></iframe>
</div>

 

.responsiveContent {
  position: relative;
  height: 0;
  overflow: hidden;
  padding-bottom: 56.2%;
  margin-bottom: 20px;
}
.responsiveContent iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

Como vemos en el código la solución pasa por:

  • Quitar toda referencia a width o height en la etiqueta del propio iframe
  • Crear un contenedor para el iframe con altura 0
  • La altura vendrá definida por el padding que le demos al contenedor. Al darle el padding en porcentage, la altura siempre sera el x% de la anchura del padre.

Podéis ver este ejemplo funcionando en mi querido framework paragridma.

Lo único que tendréis que tener en cuenta es el aspect ratio del vídeo que queráis mostrar. En caso de no coincidir se deberá de modificar el padding del contenedor.

Espero que este truqui os sea de utilidad.

Mi experiencia de wordpress Multiidioma con el plugin Polylang

En varias ocasiones he tenido que crear sitios wordpress en varios idiomas. Hasta ahora venía usando el plugin WPML, pero el hecho de que se haya convertido en un plugin de pago y que me surjieran algunos bugs, me hizo buscar alguna alternativa.

Al final he dado con el plugin Polylang, y de momento mi experiencia es muy buena. En la parte negativa puedo destacar la falta de documentación, que todavía se encuentra en una fase temprana de desarrollo (aunque su funcionamiento de momento es correcto), y que la integración con PODs no es todavía completa.

Algunos de mis problemas de momento:

Lo estoy usando junto con PODs, y al mostrar los registros de PODs muestra todos los que encuentran, sin importar el idioma:

En su día cree esta entrada en el foro de PODs


/*= GET CASES STUDIES */
$params = array(
'where' => 'active.meta_value = 1',
'orderby' => 'position ASC',
'limit' => 4
);

// Get the Pods object
$mypod = pods( 'case_studies', $params );

// Loop through the items returned
while ( $mypod->fetch() ) {
?>

<? echo $mypod->display( 'title' ); ?>
<? echo $mypod->display( 'short_description' ); ?>

Para solucionarlo he tenido que hacer esta modificación:


while ( $mypod->fetch() ) {
// it show all matched pods, from all language,
  // so we check if current pod is in current language
$post_id = $mypod->data->row['ID'];
$translated_post_id = pll_get_post($post_id, pll_current_language());

// if both id are equal, then it is in the right laguage
if ($post_id == $translated_post_id) {
?>

<? echo $mypod->display( 'title' ); ?>
<? echo $mypod->display( 'short_description' ); ?>

}
}

Link a la home page con el idioma correcto


<?= pll_home_url(); ?>

No se me muestran algunas páginas

Este es el error que más me preocupa, y estoy francamente preocupado por que se me pueda reproducir en producción

De repente (no se que desencadena el fallo) los links a las páginas de worpdress muestran el contenido del index.php en lugar de mostrar el page.php

Hasta ahora lo he podido solucionar desactivando y volviendo a activar los plugins de PODs, y de WordPress SEO

¿Dónde carajo han metido el emulador de Google Chrome?

Alguien en el brillante equipo de Google Chrome ha decidido (seguro que con buen criterio) el cambiar la localización de la pestaña del emulador de Google Chrome.

Esta funcionalidad es muy útil si deseas emular que estas viendo una web con alguna determinada versión de móvil o tablet.

¡Guala!, después de buscar un buen rato he encontrado la nueva localización.

chrome_emulator

Estos son los pasos que debes de seguir para abrirlo:

  1. Abrir el panel de Settigns, situado en la parte superior derecha del panel
  2. Comprobar que tienes marcada la opción de “Show ‘Emulation’ view in console drawer” y cerrar la ventana de configuración
  3. Abrir la pestaña de “Sources”
  4. Pulsa “Esc” y aparecerá un nuevo panel en la parte inferior
  5. Selecciona la pestaña de “Emulation” y listo!

La solución la he encontrado en esta página

Aplicar estilo CSS a Inputs de tipo File

A algunos tipos de campo de formulario no es fácil aplicarles cambios en el estilo, como por ejemplo, los radio button, los checkbox o los campos de subida de ficheros.

Con el plugin que hoy os propongo aquí, podremos falsear los campos de tipo file fácilmente.

La clave está en ocultar el campo real (el que no podemos editar) y crear un nuevo elemento que lo imite, pero en el que sí podamos aplicar todos los cambios deseados. Posteriormente, por medio de Javascript haremos que al hacer clic en el campo falso, se abra la ventana de diálogo, para la elección del fichero en cuestión.

customInputFile

Estructura HTML:

<div class="customFileInput inputWithButton inputWithButton_150">
  <div class="inputWrap fileName"></div>
  <div class="btWrap">
    <a class="bt" href="#">Select File...</a>
  </div>
  <input class="text" type="file" placeholder="Placeholder" />
</div>

Plugin jQuery:

$.fn.customFileInput = function () {

    return this.each(function() {
        var t = $(this),
            input = t.find('input'),
            fakeTrigger = t.find('.bt'),
            fakeInput = t.find('.fileName');

        input.change(function () {
            // get only file name, with out path
            var fileName = input.val().split('\\').pop();
            fakeInput.html(fileName);
        });

        input.click(function (e) {
            e.stopPropagation();
        })

        t.click(function (e) {
            e.preventDefault();
            input.click();
        });

    });
};

$(function () {
    $('.customFileInput').customFileInput();
});

Las propiedades CSS para darle estilo no las pongo aquí, ya que simplemente estoy usando la nueva versión del framework paraGridma que estamos termiando de desarrollar en Paradigma

Carga diferida del Javascript (defer loading)

Llamarme rarito, pero soy de los que le gusta tener una nota lo más alta posible en los test de “Page Speed” y me gusta que mis páginas carguen lo más rápido posible. Para intentar conseguir esto una de las recomendaciones es cargar tus ficheros Javascript de manera diferida, después de que el contenido de la página haya sido cargado y mostrado, pero esto no es tan fácil como podría parecer.

Hay varios métodos, y no todos ellos valen para todos los navegadores. Según he leído en el artículo Defer loading of javascript de Patrick Sexton este es el método que recomienda Google:

<script type="text/javascript">
function downloadJSAtOnload() {
  var element = document.createElement("script");
  element.src = "defer.js";
  document.body.appendChild(element);
}
if (window.addEventListener)
  window.addEventListener("load", downloadJSAtOnload, false);
else if (window.attachEvent)
  window.attachEvent("onload", downloadJSAtOnload);
else
  window.onload = downloadJSAtOnload;
</script>

Esta porción de código la debemos de insertar justo antes de la etiqueta </body>, al final de nuestro documento html.

Asegúrate que la ruta al fichero es correcta. En este ejemplo el fichero defer.js debe de estar en el mismo directorio que el fichero HTML.

Parece una práctica fácil de implementar y con buenos resultados, así que ¿por qué no usarla?

LESS CSS examples

I’m using LESS in the new version that i’m developing of the framework ParaGRIDma.

To render the .less file in a .css i’ve choose the software WinLess. I also evaluate Simpless that is a more beautiful software to do the same task, but it give me some error editing the file. WinLess work like a charm.

Making loops in less

@iterator: 12;
.loop (@index) when (@index > 0) {
    .g-@{index}  { width: (100% *  @index / @iterator) }
    .loop(@index - 1); // next iteration
}
.loop (0) {} // exit loop
.loop (@iterator); // draw loop

Convering dimesions in less

Sometimes you want to change dimensions in your CSS. To do this you only need to add 0[unit]

@context = 12px;
@context/10 + 0em; // returns 1.2em;

Make color variations in less

You can convert given color by adding other color. For example:

@linkColor: #2a85e8;
@linkColorHover: (@linkColor + #111);

Importing files in less

If you want to import some other css file you can do it like:

@import "normalize.css";

If you want to process this CSS file like a LESS files do this:

@import (less) "normalize.css";

Nested rules in less

#header {
  h1 {
    font-size: 26px;
    font-weight: bold;
  }
  p { font-size: 12px;
    a { text-decoration: none;
      &:hover { border-width: 1px }
    }
  }
}

Obtener los parámetros de una URL

Esta simple función te devuelve un array con cada uno de los parametros de la URL que le pases:

function getUrlParams(url) {
    var vars = {};
    var parts = url.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) {
        vars[key] = value;
    });
    return vars;
}