¿Cómo crear un Sitemap con unos simples pasos?

Mejora la experiencia de usuario y la organización de tu blog con este tutorial paso a paso

¿Insertar un Sitemap en mi blog de Blogger de forma fácil? ¿Agregar un Sitemap en mi blog? ¿Sitemap automático o tabla de contenidos que muestre lo que yo he publicado? ¿Crear un Mapa del sitio? Una de las mejores formas de crear un Sitemap es copiar un simple código CSS, sumado a un código HTML. Para hacer las cosas más sencillas deberán seguir las instrucciones tal como lo muestro en este Tutorial de Sitemaps. Este truco de Blogger lo podrán insertar en una entrada o en una página de su blog de Blogger.

¿Por qué necesitas un Sitemap en tu blog?

Un mapa del sitio o Sitemap es una herramienta esencial para cualquier blog. No solo ayuda a los motores de búsqueda a indexar todo tu contenido, sino que también mejora la experiencia de usuario al permitir que tus visitantes encuentren fácilmente lo que buscan.

Lo que nos va a mostrar este nuevo Sitemap son categorías basadas en etiquetas desplegables, como también un cuadro de búsqueda, además las últimas publicaciones de su blog.

En este tutorial te mostraré cómo crear un Sitemap automático, elegante y profesional para tu blog de Blogger, sin necesidad de ser un experto en programación.

A continuación puede ver su demostración en el siguiente blog de demos

Nota: Este Sitemap se actualizará automáticamente cada vez que publiques nuevo contenido en tu blog.

Características de tu nuevo Sitemap

Filtro por Etiquetas

Menú desplegable para explorar artículos por categorías y temas específicos.

Buscador Interno

Encuentra cualquier publicación al instante con la función de búsqueda integrada.

Diseño de Tarjetas

Muestra tus posts con imagen, título, fecha, resumen y número de comentarios.

Totalmente Responsivo

Se adapta perfectamente a todos los dispositivos: móviles, tablets y desktop.

Carga Automática

Siempre mostrará tu contenido más reciente sin que tengas que hacer nada.

Diseño Moderno

Interfaz limpia y profesional que mejora la experiencia de usuario.

Pasos para crear tu Sitemap automático

Sigue estas instrucciones cuidadosamente. El proceso es sencillo y solo te tomará unos minutos.

Ir a la sección de Páginas

Inicia sesión en tu cuenta de Blogger. En el menú lateral izquierdo, haz clic en "Página".

Crear una nueva página

Haz clic en el botón "Nueva página".

Agregar un título

Pon un título a tu página, por ejemplo: "Mapa del Sitio", "Índice de Contenidos" o "Todos los Artículos".

Cambiar al modo HTML

Es muy importante que actives la casilla "HTML" en el editor de la página. Esto te permitirá pegar el código correctamente.

Importante: No olvides este paso o el código no funcionará correctamente.

Pegar el código

Copia TODO el siguiente código y pégalo dentro del editor, sin realizar ningún cambio.

Código para el Sitemap de Blogger
<style>
/* Estilos generales */
#table-outer {
    font-family: Arial, sans-serif;
    color: #333;
    max-width: 900px;
    margin: 20px auto;
    padding: 15px;
    background-color: #f9f9f9;
    border-radius: 8px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}

#table-outer table {
    width: 100%;
    margin: 0;
    padding: 0;
    border-collapse: collapse;
}

#table-outer td {
    padding: 10px 0;
    vertical-align: middle;
}

/* --- AJUSTE DE ESTILOS PARA LAS ETIQUETAS (LABELS) --- */
/* Selecciona solo las etiquetas que son hijas directas de  dentro de #table-outer */
#table-outer td > label {
    font-weight: 600;
    margin-right: 10px;
    white-space: nowrap;
    /* Asegúrate de que no haya estilos heredados no deseados */
    display: inline-block; /* O 'block' si prefieres que ocupen su propia línea */
    font-size: 14px; /* O el tamaño que desees para tus labels específicos aquí */
    color: #333; /* Color específico para estas labels */
    padding: 0; /* Asegúrate de que no haya padding no deseado */
    line-height: 1.2; /* Ajusta si es necesario */
    position: initial;
}

td form {
    padding: 0px 0px 0px 0px;
}
#table-outer input[type="text"],
#table-outer select {
    padding: 10px 12px;
    font-size: 14px;
    background-color: #fff;
    width: 100%;
    max-width: 250px; /* Limita el ancho para un mejor diseño */
    box-sizing: border-box;
    border: 1px solid #e0e0e0;
    border-radius: 6px;
    color: #555;
    transition: all 0.3s ease;
    -webkit-appearance: none; /* Eliminar estilos predeterminados en algunos navegadores */
    -moz-appearance: none;
    appearance: none;
    background-image: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23777%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13.7-6.9H18.8c-7.7%200-13.4%207.2-13.4%2014.2%200%205.8%203.2%2011.6%208.5%2014.7l126.9%20126.9c7.9%207.9%2020.6%207.9%2028.5%200l126.9-126.9c5.3-3%208.5-8.8%208.5-14.7Z%22%2F%3E%3C%2Fsvg%3E'); /* Flecha personalizada para select */
    background-repeat: no-repeat;
    background-position: right 12px top 50%;
    background-size: 12px auto;
    cursor: pointer;
}

#table-outer input[type="text"]:focus,
#table-outer select:focus {
    outline: none;
    border-color: #9ecaed;
    box-shadow: 0 0 8px rgba(158, 202, 237, 0.6);
}

#resultDesc {
    margin: 25px auto 15px auto;
    padding: 10px;
    background-color: #e3f2fd;
    border-left: 5px solid #2196f3;
    color: #1a237e;
    font-weight: 500;
    max-width: 900px;
    border-radius: 4px;
    display: none; /* Ocultar por defecto */
}

#feedContainer {
    overflow: hidden;
    margin: 20px auto 0 auto;
    padding: 0;
    list-style: none;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); /* Diseño de cuadrícula responsivo */
    gap: 20px;
    max-width: 900px;
}

#feedContainer li {
    padding: 0;
    margin: 0;
}

#feedContainer .inner {
    display: flex;
    align-items: center;
    padding: 15px;
    min-height: 90px; /* Altura mínima para las tarjetas */
    background-color: #fff;
    border-radius: 8px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
    transition: all 0.3s ease;
    text-decoration: none;
    color: inherit;
}

#feedContainer .inner:hover {
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
    transform: translateY(-3px);
}

#feedContainer img {
    flex-shrink: 0; /* No permitir que la imagen se encoja */
    margin-right: 15px;
    width: 70px; /* Ancho fijo para la miniatura */
    height: 70px; /* Altura fija para la miniatura */
    object-fit: cover; /* Asegura que la imagen cubra el espacio sin distorsionarse */
    border-radius: 4px;
    border: 1px solid #eee;
}

#feedContainer .content-wrapper {
    flex-grow: 1; /* Permite que el contenido tome el espacio restante */
}

#feedContainer .toc-title {
    font-size: 16px;
    font-weight: 600;
    color: #333;
    line-height: 1.4;
    max-height: 44px; /* Aproximadamente 2 líneas */
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 2; /* Limitar a 2 líneas */
    -webkit-box-orient: vertical;
    text-decoration: none;
    transition: color 0.2s ease;
}

#feedContainer .toc-title:hover {
    color: #007bff;
}

#feedContainer .post-meta {
    font-size: 12px;
    color: #777;
    margin-top: 5px;
    display: flex;
    align-items: center;
    flex-wrap: wrap; /* Permitir que los elementos se envuelvan */
}

#feedContainer .post-meta span {
    margin-right: 10px;
}

#feedContainer .post-meta .comments-count {
    font-weight: 500;
    color: #555;
}

#feedContainer .news-text {
    font-size: 13px;
    color: #666;
    margin-top: 8px;
    line-height: 1.5;
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
}

#feedNav {
    text-align: center;
    margin: 30px auto;
    max-width: 900px;
}

#feedNav a,
#feedNav span {
    display: inline-block;
    padding: 12px 25px;
    background-color: #007bff;
    color: #fff;
    text-decoration: none;
    border-radius: 25px;
    transition: background-color 0.3s ease, transform 0.2s ease;
    font-weight: 500;
    min-width: 150px;
}

#feedNav a:hover {
    background-color: #0056b3;
    transform: translateY(-2px);
}

#feedNav span {
    background-color: #6c757d;
    cursor: not-allowed;
}

/* Responsividad básica */
@media (max-width: 768px) {
    #table-outer {
        padding: 10px;
        margin: 15px auto;
    }
    #table-outer td {
        display: block;
        width: 100%;
        padding: 5px 0;
    }
    /* También ajustamos la etiqueta en móvil para evitar que se salga del flujo */
    #table-outer td > label {
        margin-bottom: 5px;
        display: block;
    }
    #table-outer input[type="text"],
    #table-outer select {
        max-width: 100%;
    }
    #feedContainer {
        grid-template-columns: 1fr; /* Una columna en pantallas pequeñas */
        gap: 15px;
        padding: 0 10px;
    }
    #feedContainer .inner {
        flex-direction: column;
        align-items: flex-start;
    }
    #feedContainer img {
        margin-right: 0;
        margin-bottom: 10px;
    }
    #feedContainer .content-wrapper {
        width: 100%;
    }
}
</style>

<div id="table-outer">
    <table>
        <tbody>
            <tr>
                <td><label for="orderFeedBy">Artículos: </label></td>
                <td>
                    <select id="orderFeedBy">
                        <option value="published" selected>Nuevos post</option>
                        <option value="updated">Post actualizadas</option>
                    </select>
                </td>
            </tr>
            <tr>
                <td><label for="labelSorterSelect">Publicaciones por etiquetas: </label></td>
                <td><span id="labelSorter">
                    <select id="labelSorterSelect" disabled>
                        <option selected>Cargando etiquetas...</option>
                    </select>
                </span></td>
            </tr>
            <tr>
                <td><label for="postSearchInput">Búsqueda de artículos: </label></td>
                <td>
                    <form id="postSearcher">
                        <input type="text" id="postSearchInput" placeholder="Escribe aquí para buscar..." />
                    </form>
                </td>
            </tr>
        </tbody>
    </table>
</div>

<div id="resultDesc"></div>

<ul id="feedContainer"></ul>

<div id="feedNav">
    Cargando...
</div>

<script>
// Configuración del sitemap
var tocConfig = {
    url: "", // Deja vacío para usar la URL actual del blog
    feedNum: 10, // Número de posts a cargar inicialmente y por cada "Cargar más"
    numChars: 140, // Caracteres para el resumen de la publicación
    thumbWidth: 70, // Ancho de la miniatura de la imagen
    navText: "Cargar más \u2193", // Texto del botón "Cargar más"
    frontText: "Volver arriba \u2191", // Texto del botón "Volver arriba"
    noImage: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAA3NCSVQICAjb4U/gAAAADElEQVQImWOor68HAAL+AX7vOF2TAAAAAElFTkSuQmCC", // Imagen por defecto si no hay miniatura
    loading: "<span>Cargando...</span>",
    searching: "<span>Buscando...</span>",
    MonthNames: ["Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic"],
    noResult: "No se encontraron resultados."
};

// Referencias a elementos del DOM
const getID = (id) => document.getElementById(id);
const head = document.getElementsByTagName("head")[0];
const tocContainer = getID("feedContainer");
const feedNav = getID("feedNav");
const orderByer = getID("orderFeedBy");
const labelSorter = getID("labelSorter");
const labelSorterSelect = getID("labelSorterSelect");
const postSearcherForm = getID("postSearcher");
const postSearchInput = getID("postSearchInput");
const resultDesc = getID("resultDesc");

let nextPage = "";
let currentFeedUrl = ""; // Almacena la URL de la feed actual
let currentPageIndex = 1; // Para paginación

// Función para cortar el HTML y obtener el resumen
function cropFeed(html, numChars) {
    const doc = new DOMParser().parseFromString(html, 'text/html');
    const text = doc.body.textContent || "";
    return text.substring(0, numChars);
}

// Función para mostrar las etiquetas en el desplegable
function showLabels(json) {
    const categories = json.feed.category;
    let options = "<option value='' selected>TODAS LAS CATEGORÍAS</option>";
    if (categories) {
        categories.sort((a, b) => a.term.localeCompare(b.term)); // Ordenar alfabéticamente
        for (const category of categories) {
            options += `<option value="${encodeURIComponent(category.term)}">${category.term.toUpperCase()}</option>`;
        }
    }
    labelSorterSelect.innerHTML = options;
    labelSorterSelect.removeAttribute("disabled");

    // Seleccionar la etiqueta si está en la URL (al cargar la página)
    if (window.location.hash && window.location.hash !== "#0" && window.location.hash !== "#search") {
        const hashLabel = decodeURIComponent(window.location.hash.substring(1));
        labelSorterSelect.value = encodeURIComponent(hashLabel);
    }
}

// Función para mostrar la lista de publicaciones
function showFeedList(json) {
    const entries = json.feed.entry;
    let postHtml = "";

    if (entries && entries.length > 0) {
        for (const entry of entries) {
            const title = entry.title.$t;
            let postUrl = "";
            for (const link of entry.link) {
                if (link.rel === "alternate") {
                    postUrl = link.href;
                    break;
                }
            }

            let commentCount = "0 comentarios";
            for (const link of entry.link) {
                if (link.rel === "replies" && link.type === "text/html") {
                    commentCount = link.title;
                    break;
                }
            }
            
            const content = entry.content ? entry.content.$t : (entry.summary ? entry.summary.$t : "");
            const description = cropFeed(content, tocConfig.numChars) + "…";

            let thumbnailUrl = tocConfig.noImage;
            if (entry.media$thumbnail && entry.media$thumbnail.url) {
                thumbnailUrl = entry.media$thumbnail.url.replace(/\/s[0-9]+\-c/, "/s" + tocConfig.thumbWidth + "-c");
            }

            const postDate = new Date(entry.published.$t);
            const day = postDate.getDate();
            const month = tocConfig.MonthNames[postDate.getMonth()];
            const year = postDate.getFullYear();
            const dateString = `${day} ${month} ${year}`;

            postHtml += `
                <li>
                    <a class="inner" href="${postUrl}" target="_blank">
                        <img src="${thumbnailUrl}" alt="${title}" />
                        <div class="content-wrapper">
                            <h3 class="toc-title">${title}</h3>
                            <div class="post-meta">
                                <span class="date">${dateString}</span>
                                <span class="comments-count">${commentCount}</span>
                            </div>
                            <p class="news-text">${description}</p>
                        </div>
                    </a>
                </li>
            `;
        }

        // Actualizar el enlace "Cargar más"
        nextPage = "";
        for (const link of json.feed.link) {
            if (link.rel === "next") {
                nextPage = link.href;
                break;
            }
        }

        if (currentPageIndex === 1) { // Si es la primera carga, vaciar y luego añadir
            tocContainer.innerHTML = postHtml;
        } else { // Si es "cargar más", simplemente añadir al final
            tocContainer.innerHTML += postHtml;
        }

        // Mostrar descripción de búsqueda
        if (postSearchInput.value !== "" && window.location.hash === "#search") {
            resultDesc.innerHTML = `<span>Resultados de búsqueda para: <strong>&quot;${postSearchInput.value}&quot;</strong></span>`;
            resultDesc.style.display = "block";
        } else {
            resultDesc.innerHTML = "";
            resultDesc.style.display = "none";
        }

        // Gestionar el botón de navegación
        if (nextPage) {
            feedNav.innerHTML = `<a href="javascript:loadMorePosts();" class="next">${tocConfig.navText}</a>`;
        } else {
            feedNav.innerHTML = `<a href="#table-outer" onclick="scrollToTop();" class="front">${tocConfig.frontText}</a>`;
        }

    } else {
        if (currentPageIndex === 1) { // Si no hay resultados en la primera carga
            tocContainer.innerHTML = `<li style="text-align: center; width: 100%;">${tocConfig.noResult}</li>`;
            resultDesc.innerHTML = "";
            resultDesc.style.display = "none";
            feedNav.innerHTML = `<a href="#table-outer" onclick="scrollToTop();" class="front">${tocConfig.frontText}</a>`;
        }
        // Si no hay más resultados al hacer clic en "Cargar más", el botón ya se gestionó correctamente.
    }

    // Habilitar los selectores
    labelSorterSelect.removeAttribute("disabled");
    orderByer.removeAttribute("disabled");
}

// Función para desplazar la página hacia arriba
function scrollToTop() {
    window.scroll({
        top: 0,
        behavior: 'smooth'
    });
    return false;
}

// Inicializar la carga de posts o cargar más
function loadPosts(isLoadMore = false) {
    if (!isLoadMore) {
        currentPageIndex = 1; // Reiniciar para nueva búsqueda/filtro
        tocContainer.innerHTML = ""; // Limpiar posts anteriores
        feedNav.innerHTML = tocConfig.loading; // Mostrar estado de carga
        labelSorterSelect.setAttribute("disabled", "true");
        orderByer.setAttribute("disabled", "true");
    } else {
        feedNav.innerHTML = tocConfig.loading;
    }

    let urlParams = `max-results=${tocConfig.feedNum}&orderby=${orderByer.value}&alt=json-in-script&callback=showFeedList`;
    let baseUrl = (tocConfig.url === "" ? window.location.protocol + "//" + window.location.host : tocConfig.url);
    
    // Construir URL según el filtro de etiqueta o búsqueda
    if (window.location.hash === "#search" && postSearchInput.value) {
        baseUrl += "/feeds/posts/summary";
        urlParams = `q=${encodeURIComponent(postSearchInput.value)}&max-results=9999&orderby=published&alt=json-in-script&callback=showFeedList`;
    } else if (labelSorterSelect.value && labelSorterSelect.value !== "") {
        baseUrl += `/feeds/posts/summary/-/${labelSorterSelect.value}`;
    } else {
        baseUrl += "/feeds/posts/summary";
    }

    if (isLoadMore && nextPage) {
        const nextUrl = new URL(nextPage);
        const startIndex = nextUrl.searchParams.get('start-index');
        urlParams = `start-index=${startIndex}&max-results=${tocConfig.feedNum}&orderby=${orderByer.value}&alt=json-in-script&callback=showFeedList`;
        
        // Si hay una etiqueta seleccionada, añadirla a la URL de nextPage
        if (labelSorterSelect.value && labelSorterSelect.value !== "" && !nextUrl.pathname.includes(`/-/${labelSorterSelect.value}`)) {
             const pathParts = nextUrl.pathname.split('/feeds/posts/summary');
             if (pathParts.length > 1) {
                baseUrl = baseUrl + pathParts[1]; // Mantener la base original
             }
        }
    } else if (currentPageIndex > 1) {
        // Esto no debería ser necesario si nextPage se maneja correctamente, pero como fallback
        // Para asegurar que la paginación funciona si se pierde nextPage
        urlParams = `start-index=${((currentPageIndex - 1) * tocConfig.feedNum) + 1}&${urlParams}`;
    }


    currentFeedUrl = `${baseUrl}?${urlParams}`;
    loadScript(currentFeedUrl);
}

// Función para cargar las etiquetas
function fetchLabels() {
    const url = (tocConfig.url === "" ? window.location.protocol + "//" + window.location.host : tocConfig.url) + "/feeds/posts/summary?max-results=0&alt=json-in-script&callback=showLabels";
    loadScript(url);
}

// Cargar un script JSONP
function loadScript(src) {
    const oldScript = getID("temporer-script");
    if (oldScript) {
        oldScript.parentNode.removeChild(oldScript);
    }
    const script = document.createElement("script");
    script.type = "text/javascript";
    script.src = src;
    script.id = "temporer-script";
    head.appendChild(script);
}

// Event handlers
postSearcherForm.onsubmit = function (e) {
    e.preventDefault(); // Prevenir el envío del formulario
    window.location.hash = "#search";
    loadPosts();
    return false;
};

orderByer.onchange = function () {
    window.location.hash = "#0"; // Resetear hash
    postSearchInput.value = ""; // Limpiar búsqueda
    loadPosts();
};

labelSorterSelect.onchange = function () {
    const selectedLabel = this.value;
    if (selectedLabel) {
        window.location.hash = `#${decodeURIComponent(selectedLabel)}`;
    } else {
        window.location.hash = "#0";
    }
    postSearchInput.value = ""; // Limpiar búsqueda
    loadPosts();
};

function loadMorePosts() {
    currentPageIndex++;
    loadPosts(true);
}

// Inicializar al cargar la ventana
window.onload = function () {
    fetchLabels(); // Primero cargar las etiquetas
    loadPosts(); // Luego cargar las publicaciones iniciales

    // Manejar el hash de la URL para cargar etiquetas o búsqueda al recargar
    if (window.location.hash === "#search") {
        // La búsqueda se activará con postSearchInput.value
    } else if (window.location.hash && window.location.hash !== "#0") {
        // Se manejará cuando showLabels configure el select
    } else {
        window.location.hash = "#0"; // Si no hay hash, establecer uno por defecto
    }
};
</script>
Precaución: Asegúrate de copiar todo el código completo, sin omitir ninguna parte.

Publicar

Una vez que hayas pegado el código, haz clic en el botón "Publicar".

¡Listo!

Ahora dirígete a la nueva página que creaste y ¡mira cómo funciona tu nuevo Sitemap automático! Verás cómo se cargan todas tus publicaciones de forma ordenada.

¡Felicidades! Has creado exitosamente un Sitemap automático para tu blog de Blogger.

¿Necesitas ayuda?

Si tienes alguna pregunta o algo no funciona como esperabas, no dudes en dejarme un comentario. Estoy aquí para ayudarte.

¿Te gustó este tutorial? Si necesitas aprender sobre otros tipos de Sitemaps, puedes revisar estas otras guías:

¡Espero que te sea de mucha utilidad!

Recuerda suscribirte:

Obtenga nuestro boletín de noticias diario | Suscríbete gratuitamente SUSCRIBIRSE
¿Te ha resultado útil este artículo, recomiendanos?
Si



Share:

Luis Chávez

Soy el fundador del sitio web Ayudadeblogger.com - Considerado un Pro Blogger profesional, Consultor SEO y desarrollador Web adicto, ejecuto una serie de sitios web desde mi Oficina Quito-Ecuador.

Related post

Comentarios

3 comentarios:

  1. Hola. Es muy bueno como se presentan los artículos, sin embargo tiene dos defectos este código:

    1. El "load more" y "Loading" no se pueden cambiar todos a español (solo algunos).

    2. Cuando buscas una palabra clave para buscar, aparece varios artículos pero ninguno relacionado con la misma.

    Ojalá pudieras arreglarlo. Saludos.

    ResponderEliminar
    Respuestas
    1. Muy buena tu pregunta, puedes realizar los cambios desde el siguiente link https://googledrive.com/host/0B4Z9VSqNHBcpdzBzbTFMaTdoZFE el cual tiene el código, edita la parte que dice load more y loading, cambialos a español, y luego lo guardas en google drive y copias el link que te proporciona. y veras los cambios... Saludos..

      Eliminar
  2. Muy bueno, lo he utilizado en mi blog y esta funcionando de maravilla http://vidasanaxsiempre.blogspot.com/p/table-outer-table-width100margin0paddin.html#0

    ResponderEliminar