Como crear un Sitemap en Blogger
Cómo Crear un Mapa del Sitio Automático para Blogger
Un mapa del sitio es como el plano de un centro comercial: permite a tus visitantes encontrar rápidamente el contenido que les interesa, mejorando su experiencia y haciendo que se queden más tiempo en tu blog. Es especialmente útil en blogs con mucho contenido, ya que organiza visualmente todas tus publicaciones por categorías (etiquetas) y muestra las más recientes.
En este tutorial, te mostraremos cómo añadir un mapa del sitio interactivo, moderno y totalmente automático para Blogger. Una vez instalado, se actualizará solo cada vez que publiques algo nuevo.
¿Cómo funciona este Mapa del Sitio?
Antes de empezar, estas son las fantásticas características del widget que vamos a implementar:
- Diseño Moderno y Responsivo: Se verá bien tanto en computadoras como en móviles.
- Actualización Automática: Se conecta al feed de tu blog y añade tus nuevas entradas automáticamente.
- Búsqueda en Tiempo Real: Incluye un campo de búsqueda para filtrar publicaciones al instante.
- Organización por Etiquetas: Agrupa todas tus entradas bajo sus respectivas categorías (etiquetas), haciendo que el contenido sea fácil de navegar.
- Destaca Contenido Nuevo: Marca automáticamente las publicaciones más recientes (de los últimos 14 días) con un indicador "(Nuevo)".
- Fácil de Instalar: Solo necesitas copiar y pegar un código.
A continuación puede ver su demostración en el siguiente blog
Paso a Paso: Crea tu Página de Mapa del Sitio
Sigue estos sencillos pasos:
1 En tu Panel de Blogger, ve a la sección "Páginas" en el menú lateral.
2 Haz clic en el botón "Nueva página".
3 Titula la página. Por ejemplo: "Mapa del Sitio", "Índice de Contenidos" o "Todos los Artículos".
4 ¡MUY IMPORTANTE! En la barra de herramientas del editor, haz clic en el icono "HTML" para cambiar al modo HTML. Debes ver el código en blanco.
5 Copia el siguiente código y pégalo exactamente como está en el editor HTML. No realices ningún cambio.
<style type="text/css">
/* Contenedor principal del sitemap */
#toc-modern {
width: 100%;
max-width: 800px; /* Ancho máximo para pantallas grandes */
margin: 20px auto;
background: #ffffff;
border: 1px solid #e0e0e0;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
overflow: hidden;
}
/* Encabezado del sitemap */
#toc-modern h3 {
background: linear-gradient(to right, #055A85, #2D96DF);
color: #ffffff;
padding: 15px 20px;
margin: 0;
font-size: 1.5em;
border-top-left-radius: 7px;
border-top-right-radius: 7px;
}
/* Campo de búsqueda */
#toc-search-box {
padding: 15px 20px;
border-bottom: 1px solid #eee;
background-color: #f9f9f9;
}
#toc-search-box input[type="text"] {
width: calc(100% - 20px);
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
font-size: 1em;
outline: none;
transition: border-color 0.3s ease;
}
#toc-search-box input[type="text"]:focus {
border-color: #2D96DF;
box-shadow: 0 0 5px rgba(45, 150, 223, 0.3);
}
/* Contenido del sitemap (últimas publicaciones y etiquetas) */
#toc-content {
padding: 20px;
}
/* Títulos de sección (últimas publicaciones, categorías) */
.section-title {
font-size: 1.3em;
color: #055A85;
margin-bottom: 15px;
border-bottom: 2px solid #2D96DF;
padding-bottom: 5px;
}
/* Lista de últimas publicaciones */
#recent-posts ul {
list-style: none;
padding: 0;
margin-bottom: 30px;
}
#recent-posts li {
padding: 10px 0;
border-bottom: 1px dotted #eee;
}
#recent-posts li:last-child {
border-bottom: none;
}
#recent-posts li a {
color: #333;
text-decoration: none;
transition: color 0.3s ease;
}
#recent-posts li a:hover {
color: #2D96DF;
text-decoration: underline;
}
/* Estilos para las etiquetas (categorías) */
.label-category {
margin-bottom: 25px;
}
.label-name {
display: block;
background: linear-gradient(to right, #C2EAFE 0%, #055A85 100%);
color: #ffffff;
font-weight: bold;
padding: 10px 15px;
margin: 0;
border: 1px solid #2D96DF;
border-radius: 5px;
cursor: pointer;
position: relative;
transition: background 0.3s ease, box-shadow 0.3s ease;
user-select: none; /* Evita selección de texto al hacer clic */
}
.label-name:hover {
background: linear-gradient(to right, #c2eafed8 0%, #055a85d3 100%);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.label-name:first-letter {
text-transform: uppercase;
}
/* Icono de flecha para desplegar */
.label-name::after {
content: '\25BC'; /* Carácter Unicode para flecha hacia abajo (▼) */
position: absolute;
right: 15px;
top: 50%;
transform: translateY(-50%) rotate(0deg);
transition: transform 0.3s ease;
font-weight: normal; /* Asegura que no se vea en negrita si el padre lo es */
}
.label-name.expanded::after {
content: '\25B2'; /* Carácter Unicode para flecha hacia arriba (▲) */
transform: translateY(-50%) rotate(0deg); /* No rota, solo cambia el caracter */
}
.label-posts {
list-style: none;
padding: 0;
margin: 0;
border: 1px solid #eee;
border-top: none;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
background-color: #fcfcfc;
max-height: 0;
overflow: hidden;
transition: max-height 0.5s ease-out, padding 0.5s ease-out;
}
.label-posts.active {
max-height: 500px; /* Ajusta este valor si tienes muchísimas entradas por categoría */
padding: 10px 0;
}
.label-posts li {
padding: 8px 15px;
border-bottom: 1px dotted #f0f0f0;
}
.label-posts li:last-child {
border-bottom: none;
}
.label-posts li a {
color: #555;
text-decoration: none;
display: block;
transition: color 0.3s ease;
}
.label-posts li a:hover {
color: #FF5F00;
text-decoration: underline;
}
/* Estilos para "New" (opcional, si quieres destacarlo) */
.post-new-indicator {
color: #FF5F00;
font-weight: bold;
font-style: italic;
margin-left: 8px;
font-size: 0.85em;
}
/* Loader o mensaje de carga */
#toc-loading {
text-align: center;
padding: 30px;
font-size: 1.1em;
color: #666;
}
/* Mensaje de no resultados */
#no-results-message {
text-align: center;
padding: 20px;
color: #888;
font-style: italic;
display: none; /* Oculto por defecto */
}
/* Media Queries para responsividad */
@media (max-width: 600px) {
#toc-modern {
margin: 10px auto;
border-radius: 0;
box-shadow: none;
}
#toc-modern h3 {
font-size: 1.3em;
padding: 12px 15px;
border-radius: 0;
}
#toc-search-box {
padding: 10px 15px;
}
#toc-content {
padding: 15px;
}
.label-name {
padding: 8px 12px;
}
.label-name::after {
right: 12px;
}
}
</style>
<div id="toc-modern">
<h3>Mapa del Sitio</h3>
<div id="toc-search-box">
<input type="text" id="search-input" placeholder="Buscar publicaciones..." />
</div>
<div id="toc-loading">Cargando mapa del sitio...</div>
<div id="toc-content" style="display: none;">
<div id="recent-posts">
<h4 class="section-title">Últimas Publicaciones</h4>
<ul id="recent-posts-list">
<!-- Las últimas publicaciones se cargarán aquí -->
</ul>
</div>
<div id="categories-section">
<h4 class="section-title">Categorías (Etiquetas)</h4>
<div id="label-container">
<!-- Las categorías y sus publicaciones se cargarán aquí -->
</div>
</div>
<div id="no-results-message">No se encontraron publicaciones que coincidan con tu búsqueda.</div>
</div>
</div>
<script type="text/javascript">
// <![CDATA[
function createSitemap() {
var toc = document.getElementById('toc-modern');
var tocContent = document.getElementById('toc-content');
var tocLoading = document.getElementById('toc-loading');
var recentPostsList = document.getElementById('recent-posts-list');
var labelContainer = document.getElementById('label-container');
var searchInput = document.getElementById('search-input');
var noResultsMessage = document.getElementById('no-results-message');
var blogURL = window.location.protocol + '//' + window.location.hostname;
var maxRecentPosts = 10; // Número de últimas publicaciones a mostrar
// --- Carga de datos del blog ---
function loadBlogData() {
var script = document.createElement('script');
script.src = blogURL + '/feeds/posts/default?max-results=9999&alt=json-in-script&callback=handleFeed';
document.body.appendChild(script);
}
// --- Manejo de la respuesta del feed (callback) ---
window.handleFeed = function(json) {
tocLoading.style.display = 'none';
tocContent.style.display = 'block';
var posts = [];
if (json.feed && json.feed.entry) {
posts = json.feed.entry;
}
displayRecentPosts(posts);
displayCategories(posts);
// Listener para la búsqueda
searchInput.addEventListener('keyup', function() {
filterPosts(posts);
});
};
// --- Mostrar últimas publicaciones ---
function displayRecentPosts(posts) {
recentPostsList.innerHTML = '';
var count = 0;
for (var i = posts.length - 1; i >= 0 && count < maxRecentPosts; i--) {
var post = posts[i];
var postTitle = post.title.$t;
var postUrl = getPostUrl(post);
if (postUrl) {
var listItem = document.createElement('li');
var link = document.createElement('a');
link.href = postUrl;
link.textContent = postTitle;
listItem.appendChild(link);
recentPostsList.appendChild(listItem);
count++;
}
}
}
// --- Mostrar categorías y sus publicaciones ---
function displayCategories(posts) {
labelContainer.innerHTML = '';
var labels = {}; // Objeto para agrupar publicaciones por etiqueta
posts.forEach(function(post) {
var postTitle = post.title.$t;
var postUrl = getPostUrl(post);
var postPublished = new Date(post.published.$t);
var twoWeeksAgo = new Date();
twoWeeksAgo.setDate(twoWeeksAgo.getDate() - 14); // Considerar "Nuevo" si tiene menos de 2 semanas
if (post.category && postUrl) {
post.category.forEach(function(category) {
var labelName = category.term;
if (!labels[labelName]) {
labels[labelName] = [];
}
labels[labelName].push({
title: postTitle,
url: postUrl,
isNew: postPublished > twoWeeksAgo
});
});
}
});
// Ordenar las etiquetas alfabéticamente
var sortedLabels = Object.keys(labels).sort();
sortedLabels.forEach(function(labelName) {
var categoryDiv = document.createElement('div');
categoryDiv.className = 'label-category';
var labelHeader = document.createElement('div');
labelHeader.className = 'label-name';
labelHeader.textContent = labelName;
categoryDiv.appendChild(labelHeader);
var postsList = document.createElement('ul');
postsList.className = 'label-posts';
// Ordenar publicaciones dentro de cada etiqueta por título
labels[labelName].sort(function(a, b) {
return a.title.localeCompare(b.title);
});
labels[labelName].forEach(function(post) {
var listItem = document.createElement('li');
var link = document.createElement('a');
link.href = post.url;
link.textContent = post.title;
listItem.appendChild(link);
if (post.isNew) {
var newIndicator = document.createElement('span');
newIndicator.className = 'post-new-indicator';
newIndicator.textContent = ' (Nuevo)';
link.appendChild(newIndicator);
}
postsList.appendChild(listItem);
});
categoryDiv.appendChild(postsList);
labelContainer.appendChild(categoryDiv);
// Funcionalidad de desplegar/contraer
labelHeader.addEventListener('click', function() {
postsList.classList.toggle('active');
labelHeader.classList.toggle('expanded');
});
});
}
// --- Función para obtener la URL de la publicación ---
function getPostUrl(post) {
for (var i = 0; i < post.link.length; i++) {
if (post.link[i].rel == 'alternate') {
return post.link[i].href;
}
}
return null;
}
// --- Función de búsqueda y filtrado ---
function filterPosts(allPosts) {
var searchTerm = searchInput.value.toLowerCase();
var foundPostsCount = 0;
// Ocultar todas las categorías y posts primero
Array.from(labelContainer.children).forEach(function(categoryDiv) {
categoryDiv.style.display = 'none';
var postsList = categoryDiv.querySelector('.label-posts');
if (postsList) {
Array.from(postsList.children).forEach(function(postItem) {
postItem.style.display = 'none';
});
}
});
Array.from(recentPostsList.children).forEach(function(postItem) {
postItem.style.display = 'none';
});
if (searchTerm.length === 0) {
// Si la búsqueda está vacía, mostrar todo
document.getElementById('recent-posts').style.display = 'block';
document.getElementById('categories-section').style.display = 'block';
noResultsMessage.style.display = 'none';
Array.from(recentPostsList.children).forEach(function(postItem) {
postItem.style.display = 'block';
});
Array.from(labelContainer.children).forEach(function(categoryDiv) {
categoryDiv.style.display = 'block';
// No expandir automáticamente las categorías
// categoryDiv.querySelector('.label-posts').classList.remove('active');
// categoryDiv.querySelector('.label-name').classList.remove('expanded');
});
return;
}
// Mostrar solo el apartado de categorías para los resultados de búsqueda
document.getElementById('recent-posts').style.display = 'none';
document.getElementById('categories-section').style.display = 'block';
allPosts.forEach(function(post) {
var postTitle = post.title.$t.toLowerCase();
var postUrl = getPostUrl(post);
if (postTitle.includes(searchTerm)) {
foundPostsCount++;
if (post.category && postUrl) {
post.category.forEach(function(category) {
var labelName = category.term;
var categoryDivs = labelContainer.querySelectorAll('.label-category');
Array.from(categoryDivs).forEach(function(catDiv) {
if (catDiv.querySelector('.label-name').textContent === labelName) {
catDiv.style.display = 'block'; // Mostrar la categoría
var postsList = catDiv.querySelector('.label-posts');
if (postsList) {
postsList.classList.add('active'); // Expandir la categoría
catDiv.querySelector('.label-name').classList.add('expanded'); // Cambiar icono
Array.from(postsList.children).forEach(function(postItem) {
var linkText = postItem.querySelector('a').textContent.toLowerCase();
if (linkText.includes(searchTerm)) {
postItem.style.display = 'block'; // Mostrar el post
}
});
}
}
});
});
}
}
});
if (foundPostsCount === 0) {
noResultsMessage.style.display = 'block';
} else {
noResultsMessage.style.display = 'none';
}
}
// Iniciar la carga de datos al cargar el script
loadBlogData();
}
// Llamar a la función principal cuando el DOM esté listo
document.addEventListener('DOMContentLoaded', createSitemap);
// ]]>
</script>
6 Haz clic en el botón "Publicar".
¡Y eso es todo! Tu página de Mapa del Sitio ha sido creada. Ahora visita la página desde el menú de navegación de tu blog y comprueba cómo se carga y organiza todo tu contenido de forma automática.
Características Adicionales y Personalización
El mapa del sitio incluye varias características avanzadas:
- Diseño responsivo: Se adapta perfectamente a dispositivos móviles y tablets.
- Interfaz interactiva: Las categorías se expanden y contraen con suaves animaciones.
- Indicador de contenido nuevo: Las publicaciones de menos de dos semanas se marcan con un distintivo "(Nuevo)".
- Búsqueda en tiempo real: Encuentra contenido instantáneamente mientras escribes.
¿Necesitas ayuda?
Si tienes alguna duda o algo no funciona como esperabas, no dudes en dejarme un comentario. Con gusto te ayudaremos a resolverlo.
¡Disfruta de tu nuevo y poderoso Mapa del Sitio!
Genial, me encanta. He creado la página y funciona perfectamente. Sólo tengo ya que adecuar los colores a los de mi blog.
ResponderEliminarSólo una pregunta: En "publicaciones recientes", muestra justamente lo contrario, las publicaciones más antiguas: desde la primera publicada hacia adelante. ¿Podría solucionarse?
Muchas gracias. Saludos cordiales