Añadir tooltips en WordPress sin plugins

Tiempo de lectura Se lee en: 10 min, 23 s
Número de palabras Palabras: 1923
Número de visitas Visitas: 37
Seleccionar idioma
Adding tooltips in WordPress without plugins
Un ejemplo de tooltip funcionando

El término tooltip no tiene una traducción única al español, puede describirse como "información sobre herramientas", "globo de ayuda", "descripción emergente" o incluso "texto de ayuda" dependiendo del contexto donde se use.

Un tooltip es un mensaje emergente que aparece al interactuar con un elemento, bien sea al pinchar sobre él o al pasar el ratón. Estos cartelitos sirven para poder ofrecer información adicional sin ocupar espacio permanente. Ideal para explicar iconos, términos técnicos o acciones en interfaces.

¿Los tooltips perjudican o benefician al SEO?

Pues como para casi todo, la respuesta es siempre depende. Si bien los más puristas del SEO advierten de que a Google no le gusta el contenido "escondido" o enmascarado y que penaliza con dureza su uso, esto sería un extremo para casos de abuso que podría "canibalizar" tu contenido. Sin embargo, los tooltips, usados con cierto equilibrio y una mínima lógica pueden ser beneficiosos.

Ventajas de usar tooltips

A grandes rasgos, estos mensajes, si son amigables y no molestan pueden contribuir a mejorar distintos aspectos de la experiencia del usuario. Entre otras cosas, reducen la tasa de rebote y mejoran la accesibilidad. También puede ofrecer oportunidades para usar palabras clave adicionales que no encajan naturalmente en el contenido principal, lo que ayuda con el SEO semántico.

Además, pueden mejorar las tasas de clics. Si ofrece información sobre herramientas en la navegación o en los botones de llamada a la acción, pueden guiar a los usuarios de manera más efectiva, lo que genera tasas de clics más altas. Pero para que todo esto sea posible se deben seguir unas mínimas pautas.

Recomendaciones de aspecto, contenido y funcionamiento

  • Uso de elementos HTML y atributos ARIA para garantizar que los lectores de pantalla y los motores de búsqueda puedan acceder a la información que contienen.
  • Un tooltip debe ser breve. Obviamente, nadie va a leer un tocho en un cartel emergente y además estarás tapando buena parte del contenido. Debe contener la información mínima, precisa y necesaria, y esta no debe ser crítica para el usuario a la hora de realizar o finalizar alguna acción.
  • El contenido no debe ser redundante o repetido, asegúrate de que estás ofreciendo información adicional útil y necesaria y que no se repite en tu contenido para evitar "contenido duplicado".
  • Intenta evitar en la medida de lo posible los tooltips que desaparecen al mover el cursor o úsalos solo en aquellos mensajes que no contengan mucha información y/o contenido. Lo suyo es que se muestren por clic y se cierren al volver a pinchar en cualquier lugar de la pantalla.
  • Procura que sean legibles y de un tamaño correcto.
  • No añadas veinte diseños diferentes de iconos de tooltips o enlaces con tooltip, eso solo crea confusión. Un diseño fijo y coherente contribuirá a que tus visitantes los identifiquen de un primer vistazo.
  • No abuses, planifica bien tu estrategia para añadirlos solo allí donde creas que facilitan el acceso a información complementaria relevante y/o necesaria.

Características y funcionalidades del código

Para este caso se ha usado esta imagen .png con fondo transparente.

La URL de la imagen se añade entre las comillas simples en esta línea:

$default_image = 'URL-de-tu-imagen';

Si optas por modificar el código para usar un icono SVG HTML, como este, podrás jugar con el color, fondo, grosor, etc más fácilmente.

Ajuste automático inteligente de posición para evitar bordes de pantalla. Es decir, cuando se acerca al borde superior se muestra el tooltip abajo y vivecersa.

Se pueden añadir enlaces completamente funcionales dentro del tooltip con estilos personalizables. Si bien no es aconsejable añadir enlaces, es posible que puedas necesitarlo en alguna ocasión. En el caso de que un tooltip contenga un enlace hay un retraso de 200ms en la ocultación automática para facilitar que pueda hacerse clic.

Soporte HTML seguro. Admite:

<strong>, <em>, <a>, <br>, <span>

Atributos de posición, fijos y adaptable.

Atributos de posición disponibles:  position="auto" (default), top, bottom, left, right

Atributos ARIA (aria-expanded, role="tooltip").

Actualización. Se añade aria-hidden="true" y role="presentation" en la imagen y se oculta el icono decorativo de los lectores de pantalla.

Se evita redundancia con el aria-label que permite personalizar el texto accesible desde el shortocode.

[tooltip text="Contenido" aria_label="Ver detalles sobre este elemento"]

Esta solución cumple con WCAG 2.1 AA y resuelve el aviso en PageSpeed Insights de que el botón no tenía nombre accesible.

Diseño responsive.

Adaptable en móviles (toggle con clic).

Reajuste al redimensionar, girar o desplazar

Clases CSS principales:

.tooltip-container /* Contenedor principal */
.tooltip-trigger  /* Botón/icono que activa el tooltip */
.tooltip          /* Caja del mensaje emergente */
.tooltip-arrow    /* Flecha indicadora */

Funciones clave del JS:

calculateBestPosition() // Determina si colocar el tooltip arriba, abajo, etc.
showTooltip()          // Muestra el tooltip
hideTooltip()          // Oculta el tooltip con retraso

Ejemplos de shortcodes:

[tooltip text="Texto a mostrar"] 

[tooltip text="Texto a mostrar <strong>en negrita</strong> donde sea" position="auto"] 

[tooltip text="Texto <strong>importante</strong> y <em>énfasis</em>"]

[tooltip text="<a href='https://ejemplo.com' target='_blank'>Visite nuestro sitio</a>"]

En cuanto a rendimiento:

  1. Preload de assets: Imágenes y CSS cargados anticipadamente.
  2. Lazy loading: Atributos loading="lazy" y decoding="async" para la imagen.
  3. Carga condicional: JS solo se carga cuando se usa el shortcode.
  4. Debounce: Optimización de eventos resize y scroll.
  5. CSS optimizado: Uso de will-change y unidades modernas (min()).
  6. Versiones cacheadas: Usa filemtime() para evitar caché obsoleto.

Este código es el que he usado para mis necesidades concretas. Entiéndelo como un punto de partida. Es muy mejorable. Incluso plantearlo prescindiendo del uso de Javascript y adaptarlo para que se pueda aplicar también a palabras del contenido sin mostrar el icono.

Para usarlo, como siempre, basta añadirlo al functions.php y en la carpeta assets/css/ y asstes/js/ de tu plantilla o de tu tema hijo si quieren conservar los cambios cuando se actualice tu plantilla.

Código

Esta es la estructura de los archivos:

tu-plantilla/
├── functions.php (añadir el código PHP al final)
├── assets/
│ ├── css/
│ │ └── tooltip.css (Estilos CSS)
│ └── js/
│ └── tooltip.js (Javascript)

Código a añadir a functions.php de la plantilla

/**
 * Shortcode para tooltip accesible y optimizado
 * Uso: [tooltip text="Texto" position="auto" color="#6a0000" aria_label="Descripción"]
 */
function accessible_tooltip_shortcode($atts) {
    $default_image = 'https://jrmora.com/wp-content/uploads/2025/07/tooltip-negro.png';
    
    $atts = shortcode_atts(
        array(
            'text' => __('Información adicional', 'text-domain'),
            'position' => 'auto',
            'color' => '#6a0000',
            'shadow' => '#9c9c9c',
            'width' => '18px',
            'id' => 'tooltip-' . uniqid(),
            'aria_label' => __('Mostrar información adicional', 'text-domain')
        ),
        $atts,
        'tooltip'
    );

    // HTML permitido (seguro)
    $allowed_html = array(
        'strong' => array(),
        'b' => array(),
        'em' => array(),
        'i' => array(),
        'br' => array(),
        'span' => array(
            'style' => array(),
            'class' => array()
        ),
        'a' => array(
            'href' => array(),
            'title' => array(),
            'target' => array(),
            'style' => array(),
            'rel' => array()
        )
    );
    
    $text = wp_kses($atts['text'], $allowed_html);
    $position = in_array($atts['position'], ['top', 'bottom', 'left', 'right', 'auto']) ? $atts['position'] : 'auto';
    $color = sanitize_hex_color($atts['color']);
    $shadow = sanitize_hex_color($atts['shadow']);
    $width = esc_attr($atts['width']);
    $id = sanitize_html_class($atts['id']);
    $aria_label = esc_attr($atts['aria_label']);

    // Preload para la imagen (mejora rendimiento)
    add_action('wp_head', function() use ($default_image) {
        echo '<link rel="preload" href="' . esc_url($default_image) . '" as="image">';
    });

    return '
    <span class="tooltip-container" data-position="' . esc_attr($position) . '">
        <button class="tooltip-trigger" 
                aria-describedby="' . $id . '" 
                aria-expanded="false"
                aria-label="' . $aria_label . '">
            <img src="' . esc_url($default_image) . '" 
                 width="' . $width . '" 
                 alt=""
                 aria-hidden="true"
                 loading="lazy"
                 decoding="async"
                 role="presentation"/>
        </button>
        <span id="' . $id . '" role="tooltip" class="tooltip" style="background-color: ' . $color . '; box-shadow: 0 5px 10px ' . $shadow . ';">
            ' . $text . '
            <span class="tooltip-arrow"></span>
        </span>
    </span>';
}
add_shortcode('tooltip', 'accessible_tooltip_shortcode');

/**
 * Carga optimizada de assets
 */
function load_tooltip_assets() {
    global $post;
    
    // CSS externo
    wp_enqueue_style(
        'tooltip-css',
        get_theme_file_uri('/assets/css/tooltip.css'),
        array(),
        filemtime(get_theme_file_path('/assets/css/tooltip.css'))
    );
    
    // JS solo si se usa el shortcode
    if (is_a($post, 'WP_Post') && has_shortcode($post->post_content, 'tooltip')) {
        wp_enqueue_script(
            'tooltip-js',
            get_theme_file_uri('/assets/js/tooltip.js'),
            array(),
            filemtime(get_theme_file_path('/assets/js/tooltip.js')),
            true
        );
    }
}
add_action('wp_enqueue_scripts', 'load_tooltip_assets');

CSS / tooltip.css (se debe guardar en esta ruta /assets/css/tooltip.css)

/* Contenedor principal */
.tooltip-container {
    display: inline-block;
    position: relative;
    vertical-align: middle;
}

/* Botón activador */
.tooltip-trigger {
    background: none;
    border: none;
    padding: 0;
    margin: 0 2px;
    cursor: pointer;
    line-height: 1;
}

/* Caja del tooltip */
.tooltip {
    position: absolute;
    color: #fff;
    padding: 10px 12px;
    border-radius: 4px;
    font-size: 14px;
    line-height: 1.5;
    width: 220px;
    max-width: min(90vw, 220px);
    text-align: left;
    visibility: hidden;
    opacity: 0;
    transition: opacity 0.25s ease;
    z-index: 100000;
    pointer-events: none;
    will-change: transform, opacity;
}

/* Flecha indicadora */
.tooltip-arrow {
    position: absolute;
    width: 0;
    height: 0;
    border: 7px solid transparent;
}

/* Posiciones */
.tooltip[data-position="top"] {
    bottom: 100%;
    left: 50%;
    transform: translateX(-50%);
    margin-bottom: 10px;
}
.tooltip[data-position="top"] .tooltip-arrow {
    top: 100%;
    left: 50%;
    transform: translateX(-50%);
    border-top-color: inherit;
}

.tooltip[data-position="bottom"] {
    top: 100%;
    left: 50%;
    transform: translateX(-50%);
    margin-top: 10px;
}
.tooltip[data-position="bottom"] .tooltip-arrow {
    bottom: 100%;
    left: 50%;
    transform: translateX(-50%);
    border-bottom-color: inherit;
}

/* Interacción */
.tooltip-trigger:hover + .tooltip,
.tooltip-trigger:focus + .tooltip,
.tooltip-trigger[aria-expanded="true"] + .tooltip {
    visibility: visible;
    opacity: 1;
}

/* Enlaces dentro del tooltip */
.tooltip a {
    color: #fff !important;
    text-decoration: underline;
    pointer-events: auto !important;
}

/* Focus para accesibilidad */
.tooltip-trigger:focus {
    outline: 2px solid #005fcc;
    outline-offset: 2px;
}

Javascript / Tooltip.js (Se debe guardar en la ruta /assets/js/tooltip.js)

document.addEventListener("DOMContentLoaded", () => {
    const tooltips = document.querySelectorAll('.tooltip-container[data-position="auto"]');
    if (!tooltips.length) return;

    // Debounce para optimización
    const debounce = (func, wait = 50) => {
        let timeout;
        return (...args) => {
            clearTimeout(timeout);
            timeout = setTimeout(() => func.apply(this, args), wait);
        };
    };

    // Configurar cada tooltip
    tooltips.forEach(container => {
        const tooltip = container.querySelector('.tooltip');
        const trigger = container.querySelector('.tooltip-trigger');
        let hoverTimeout;
        let isMouseIn = false;

        // Calcular mejor posición
        const calculatePosition = () => {
            const rect = trigger.getBoundingClientRect();
            const viewportHeight = window.innerHeight;
            return (rect.top < viewportHeight / 2) ? 'bottom' : 'top';
        };

        // Actualizar posición (optimizado)
        const updatePosition = debounce(() => {
            tooltip.dataset.position = calculatePosition();
        });

        // Mostrar tooltip
        const showTooltip = () => {
            clearTimeout(hoverTimeout);
            tooltip.style.visibility = 'visible';
            tooltip.style.opacity = '1';
            trigger.setAttribute('aria-expanded', 'true');
        };

        // Ocultar con retraso (para enlaces)
        const hideTooltip = () => {
            if (!isMouseIn) {
                hoverTimeout = setTimeout(() => {
                    tooltip.style.visibility = 'hidden';
                    tooltip.style.opacity = '0';
                    trigger.setAttribute('aria-expanded', 'false');
                }, 200);
            }
        };

        // Eventos
        trigger.addEventListener('mouseenter', showTooltip);
        trigger.addEventListener('mouseleave', hideTooltip);
        trigger.addEventListener('focus', showTooltip);
        trigger.addEventListener('blur', hideTooltip);

        tooltip.addEventListener('mouseenter', () => {
            isMouseIn = true;
            clearTimeout(hoverTimeout);
        });

        tooltip.addEventListener('mouseleave', () => {
            isMouseIn = false;
            hideTooltip();
        });

        // Inicialización
        updatePosition();
        window.addEventListener('resize', updatePosition);
        window.addEventListener('scroll', updatePosition, { passive: true });
    });

    // Cerrar al hacer clic fuera
    document.addEventListener('click', (e) => {
        if (!e.target.closest('.tooltip-container') && !e.target.closest('.tooltip a')) {
            document.querySelectorAll('.tooltip').forEach(t => {
                t.style.visibility = 'hidden';
                t.style.opacity = '0';
            });
        }
    });
});

Donar

Artículos relacionados



Repositorio de documentales sobre dibujantes de cómic y humor gráfico.

Tontolares. Los titulares más gilipollas de la prensa. Envía los tuyos

Este blog se aloja en LucusHost

LucusHost, el mejor hosting