WordPress Hosting

batalla seo a button read more

Sustituir enlaces <a> de leer más por <button>

El otro día, cuando añadí la opción de usar botones en vez de enlaces al plugin de compartir y resumir contenidos en las IAs por temas de SEO, para optimizar el crawl budget y no tener tantos enlaces en los archivos, páginas de blog, etc., se me ocurrió que eso estaría bien poder aplicarlo en otras situaciones.

Y lo primero que me vino a la mente son ese montón de enlaces, con o sin aspecto de botones según el tema, pero enlaces a fin de cuentas, que hay en todas las páginas de archivo, blog, incluso portada de web de los «Leer más», «Sigue leyendo», «» o como lo ponga tu tema WordPress.

Me parecieron un candidato ideal para aplicar esta misma estrategia de SEO.

Pero antes…

¿Por qué debería siquiera plantearme usar <button> en vez de enlaces <a>?

Eso mismo me preguntaba yo, y es que los enlaces tradicionales «Leer más» crean varios problemas de SEO, aquí tienes algunos argumentos de peso:

  • Enlaces duplicados: Cada entrada tiene dos enlaces idénticos (título + leer más) que apuntan a la misma URL.
  • Presupuesto de rastreo (crawl budget) desperdiciado: Los motores de búsqueda pierden tiempo rastreando enlaces redundantes.
  • Mala distribución del zumo de enlaces (me gusta mucho más que eso tan feo del link juice): La equidad de enlaces, también conocida como link equity o link to more link juice, se diluye en todas esas URL duplicadas.
  • Texto de anclaje repetitivo: «Leer más» aparece cientos de veces en cada página de archivo, blog e incluso portada de tu web.

¿Te he convencido o ya lo estabas?

Ea, vamos pues…

Código UNIVERSAL para convertir enlaces <a> de los Read More en <button>

Aquí tienes un fragmento de código que, añadido al archivo functions.php del tema (clásico), a tu plugin de personalizaciones, o usando un plugin de snippets sustituye los enlaces <a> habituales de «Leer más» o «Sigue leyendo» por elementos <button>, que no perjudican el zumo de enlaces (precioso palabro) de tus páginas de archivo, blog, etc.

// Código UNIVERSAL - Convertir enlaces "Leer más" en botones - AyudaWP Universal
function ayudawp_read_more_buttons_universal() {
// Solo en páginas de archivo/home, no en single
if (is_single() || is_page()) return;
?>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Selectores para eliminar read more existentes
const removeSelectors = [
// WordPress estándar
'.more-link',
'.read-more-link',
'a[href*="#more-"]',
'.wp-block-post-excerpt__more-link', // Gutenberg

// Temas concretos
'.ast-read-more-container', // Astra
'.entry-read-more', // Muchos temas
'.post-read-more',
'.kadence-read-more', // Kadence
'.ocean-read-more', // OceanWP genérico
'.blog-entry-readmore', // OceanWP específico
'.blog-entry-readmore.clr', // OceanWP más específico
'.read-more-button-wrap', // Blocksy
'.et_pb_more_button', // Divi
'.elementor-post__read-more', // Elementor

// Selectores genéricos por clase
'[class*="read-more"]',
'[class*="readmore"]',
'[class*="more-link"]',
'[class*="continue-reading"]'
];

// Capturamos los estilos antes de eliminarlos
let capturedStyles = {};
removeSelectors.forEach(function(selector) {
try {
const elements = document.querySelectorAll(selector);
elements.forEach(function(el) {
if (!capturedStyles.captured && el.tagName === 'A') {
const computedStyle = window.getComputedStyle(el);
capturedStyles = {
captured: true,
backgroundColor: computedStyle.backgroundColor,
color: computedStyle.color,
padding: computedStyle.padding,
borderRadius: computedStyle.borderRadius,
fontSize: computedStyle.fontSize,
fontWeight: computedStyle.fontWeight,
textDecoration: computedStyle.textDecoration,
border: computedStyle.border,
fontFamily: computedStyle.fontFamily
};
}

// Comprobamos que realmente es un "leer más" por el texto
const text = el.textContent.toLowerCase();
if (text.includes('leer') || text.includes('read') || text.includes('más') || text.includes('more') || text.includes('continue') || text.includes('continuar')) {
el.remove();
}
});
} catch(e) {
// Ignoramos errores de selectores no válidos
}
});

// Selectores para encontrar entradas
const postSelectors = [
'article[id*="post-"]',
'article.post',
'.blog-entry', // para el puñetero OceanWP
'.hentry',
'.entry',
'.post-item',
'.wp-block-post',
'.et_pb_post',
'.elementor-post',
'[class*="post-"]'
];

let processedPosts = new Set();

postSelectors.forEach(function(postSelector) {
try {
document.querySelectorAll(postSelector).forEach(function(post) {
const postId = post.id || post.className;
if (processedPosts.has(postId)) return;
processedPosts.add(postId);

const contentSelectors = [
'.blog-entry-content',
'.blog-entry-summary',
'.post-content',
'.entry-content', 
'.entry-summary',
'.excerpt',
'.wp-block-post-excerpt',
'.elementor-post__excerpt',
'.et_pb_post_content',
'.post-excerpt',
'[class*="content"]',
'[class*="excerpt"]'
];

let contentArea = null;
for (let selector of contentSelectors) {
contentArea = post.querySelector(selector);
if (contentArea) break;
}

const titleSelectors = [
'.blog-entry-title a',
'.entry-title a',
'.post-title a',
'.wp-block-post-title a',
'h1 a',
'h2 a', 
'h3 a',
'.elementor-post__title a',
'.et_pb_post_title a',
'[class*="title"] a'
];

let titleLink = null;
for (let selector of titleSelectors) {
titleLink = post.querySelector(selector);
if (titleLink) break;
}

if (contentArea && titleLink && !post.querySelector('.ayudawp-read-more-btn')) {
const button = document.createElement('button');
button.className = 'ayudawp-read-more-btn';
button.textContent = 'Leer más';
button.setAttribute('aria-label', 'Leer más sobre: ' + titleLink.textContent.trim());

// Aplicamos estilos que capturamos antes
if (capturedStyles.captured) {
button.style.backgroundColor = capturedStyles.backgroundColor;
button.style.color = capturedStyles.color;
button.style.padding = capturedStyles.padding;
button.style.borderRadius = capturedStyles.borderRadius;
button.style.fontSize = capturedStyles.fontSize;
button.style.fontWeight = capturedStyles.fontWeight;
button.style.border = capturedStyles.border;
button.style.fontFamily = capturedStyles.fontFamily;
button.style.cursor = 'pointer';
button.style.textDecoration = 'none';
}

button.onclick = function() { 
window.location.href = titleLink.href; 
};

const wrapper = document.createElement('div');
wrapper.className = 'ayudawp-read-more-wrapper';
wrapper.appendChild(button);

contentArea.appendChild(wrapper);
}
});
} catch(e) {
// Ignoramos posibles errores de selectores
}
});
});
</script>

<style>
.ayudawp-read-more-wrapper {
margin-top: 15px;
}

/* Estilos mínimos solo por si acaso */
.ayudawp-read-more-btn {
display: inline-block;
cursor: pointer;
border: none;
transition: opacity 0.3s ease;
}

.ayudawp-read-more-btn:hover {
opacity: 0.8;
}
</style>
<?php
}
add_action('wp_footer', 'ayudawp_read_more_buttons_universal');

Debería funcionar en cualquier tema WordPress que use los elementos habituales para mostrar los Read More, y también en temas de bloques o edición completa del sitio. Yo lo he probado en montones de temas del top 50 de temas más utilizados, incluido Divi y Hello Elementor y funciona en todos.

Además, me tocó añadir una parte especial para OceanWP, que es un poco coñazo porque usa cosas raras, pero como es de los más utilizados no quería dejarlo fuera.

¡Es que yo lo quiero solo para el tema Astra!

Claro, pues sí, es el que uso yo mismo, aquí lo tienes:

/**
* Convertir enlaces de leer más de Astra a buttons - AyudaWP.com
* Añade este código al final del archivo functions.php del tema hijo de Astra
*/
function ayudawp_astra_read_more_buttons() {
// Solo en archivos, no en single
if (is_single() || is_page()) return;
?>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Encontramos y capturamos estilos de los read more de Astra antes de quitarlos
let capturedStyles = {};
const astraReadMore = document.querySelector('.ast-read-more-container a');
if (astraReadMore) {
const computedStyle = window.getComputedStyle(astraReadMore);
capturedStyles = {
backgroundColor: computedStyle.backgroundColor,
color: computedStyle.color,
padding: computedStyle.padding,
borderRadius: computedStyle.borderRadius,
fontSize: computedStyle.fontSize,
fontWeight: computedStyle.fontWeight,
border: computedStyle.border,
fontFamily: computedStyle.fontFamily
};
}

// Quitamos elementos read more de Astra
document.querySelectorAll('.ast-read-more-container').forEach(function(el) {
el.remove();
});

// Añadimos button a cada entrada
document.querySelectorAll('article[id*="post-"]').forEach(function(post) {
const content = post.querySelector('.post-content, .entry-content');
const titleLink = post.querySelector('.entry-title a, h2 a, h3 a');

if (content && titleLink && !post.querySelector('.astra-read-more-btn')) {
const button = document.createElement('button');
button.className = 'astra-read-more-btn';
button.textContent = 'Leer más';
button.onclick = function() { 
window.location.href = titleLink.href; 
};

// Aplicamos los estilos de Astra
if (astraReadMore) {
Object.assign(button.style, capturedStyles);
button.style.cursor = 'pointer';
button.style.textDecoration = 'none';
button.style.display = 'inline-block';
}

const wrapper = document.createElement('div');
wrapper.style.marginTop = '15px';
wrapper.appendChild(button);
content.appendChild(wrapper);
}
});
});
</script>
<?php
}
add_action('wp_footer', 'ayudawp_astra_read_more_buttons');

// Quitamos el read more de WordPress
add_filter('excerpt_more', '__return_empty_string');
add_filter('the_content_more_link', '__return_empty_string');

¡Es que yo lo quiero solo para temas FSE o de bloques!

Anda queeeee…

Toma:

/**
* Convertir enlaces read more de temas FSE/Bloques a button - AyudaWP.com
* Añadir al final del archivo functions.php del tema hijo
*/
function ayudawp_fse_read_more_buttons() {
// Solo en archivos, no en single
if (is_single() || is_page()) return;
?>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Encontramos y capturamos estilos de los read more de FSE/Gutenberg antes de quitarlos
let capturedStyles = {};
const fseReadMore = document.querySelector('.wp-block-post-excerpt__more-link');
if (fseReadMore) {
const computedStyle = window.getComputedStyle(fseReadMore);
capturedStyles = {
backgroundColor: computedStyle.backgroundColor,
color: computedStyle.color,
padding: computedStyle.padding,
borderRadius: computedStyle.borderRadius,
fontSize: computedStyle.fontSize,
fontWeight: computedStyle.fontWeight,
border: computedStyle.border,
fontFamily: computedStyle.fontFamily
};
}

// Quitamos los elementos read more de FSE/Gutenberg
const fseSelectors = [
'.wp-block-post-excerpt__more-link',
'.more-link',
'a[href*="#more-"]'
];

fseSelectors.forEach(function(selector) {
document.querySelectorAll(selector).forEach(function(el) {
const text = el.textContent.toLowerCase();
if (text.includes('leer') || text.includes('read') || text.includes('más') || text.includes('more')) {
el.remove();
}
});
});

// Añadimos button a cada entrada (estructura FSE)
const postSelectors = [
'.wp-block-post', // FSE posts
'article[id*="post-"]', // Standard posts
'article.post'
];

postSelectors.forEach(function(postSelector) {
document.querySelectorAll(postSelector).forEach(function(post) {
const content = post.querySelector('.wp-block-post-excerpt, .entry-content, .post-content');
const titleLink = post.querySelector('.wp-block-post-title a, .entry-title a, h1 a, h2 a, h3 a');

if (content && titleLink && !post.querySelector('.fse-read-more-btn')) {
const button = document.createElement('button');
button.className = 'fse-read-more-btn';
button.textContent = 'Leer más';
button.onclick = function() { 
window.location.href = titleLink.href; 
};

// Aplicamos los estilos de FSE capturados por si acaso
if (fseReadMore) {
Object.assign(button.style, capturedStyles);
button.style.cursor = 'pointer';
button.style.textDecoration = 'none';
button.style.display = 'inline-block';
} else {
// Estilos FSE
button.style.padding = '8px 16px';
button.style.backgroundColor = 'var(--wp--preset--color--primary, #0073aa)';
button.style.color = '#fff';
button.style.border = 'none';
button.style.borderRadius = '4px';
button.style.cursor = 'pointer';
button.style.fontSize = 'var(--wp--preset--font-size--small, 14px)';
button.style.fontFamily = 'inherit';
}

const wrapper = document.createElement('div');
wrapper.style.marginTop = '15px';
wrapper.appendChild(button);
content.appendChild(wrapper);
}
});
});
});
</script>
<?php
}
add_action('wp_footer', 'ayudawp_fse_read_more_buttons');

// Quitamos los read more de WordPress
add_filter('excerpt_more', '__return_empty_string');
add_filter('the_content_more_link', '__return_empty_string');

¿Alguna petición más del respetable? 🙂

¿Cómo añado el código?

Si te apañas bien simplemente elige el que te sirva para tu tema en concreto o el primero que sirve para casi todos, y añádelo al final del archivo functions.php del tema hijo, si tienes tema hijo.

En caso contrario tienes 2 opciones, a saber:

  1. Crear un tema hijo y añadir el código (vuelve a mirar el párrafo anterior)
  2. Usar un plugin de fragmentos de código (lo mejor si vas a usar el primer código)

¿Y no podría ser un plugin?

Pues sí, estoy en ello, cuando lo tenga aviso por aquí 🙂

Compartir en redes
Resumir con IA

¿De cuánta utilidad te ha parecido este contenido?

¡Haz clic en las estrellas para valorarlo!

Promedio de puntuación 5 / 5. Total de votos: 5

¡Todavía no hay votos! Sé el primero en valorar este contenido.

Ya que has encontrado útil este contenido...

¡Sígueme en las redes sociales!

¿Te gustó este artículo? ¡Ni te imaginas lo que te estás perdiendo en YouTube!



Sobre el autor

3 comentarios en “Sustituir enlaces <a> de leer más por <button>”

  1. Muchas gracias por tu trabajo, me parece una opción muy interesante. Por desgracia lo he probado y no me funciona. Tampoco me funciona el plugin que has hecho para esto, me pasa lo mismo. Creo que el problema es que utilizo DIVI pero la página en la que tengo el blog es una página «normal» a la que se le ha añadido un módulo de post.
    El caso es que si cambio «if (is_single() || is_page()) return;» por un simple «if (is_single()) return;», entonces si que me funciona, pero claro, lo hace en todas las páginas (no sé si eso es así correcto).
    El código original tampoco me muestra los botones en la página principal, ya que ocurre lo mismo; es un página «normal» en la que se ha añadido un modulo «Blog», para que muestre las ultimas entradas.

    ¿Cuál sería la mejor forma de adaptarlo? ¿Se podría poner en el plugin?.

    Muchas gracias de nuevo

    1. Hola Tomás, gracias por le montón de información valiosa, lo miro en detalle en cuando saque un rato y si hay arreglo lo incorporo en el plugin 🙂

      1. La verdad es que con ese cambio ya me funciona bien y, aunque el filtro se aplique a todas las paginas, solo tengo el modulo deDivi del Blog en la pagina de inicio (las últimas 3 noticias) y la pagina del blog «/blog». De momento lo dejo así, sino le pondría un filtro del tipo «if (!is_page(123)) return;» para que solo lo haga en la de inicio y el blog.

        Mil gracias.

Los comentarios están cerrados.

Scroll al inicio