Kamilo Stevan Alomías Correa
En mi caso como no soy creador de contenido (aún) reemplacé los links por algunos certificados que he obtenido, también cambié los emojis por IDs visuales de cada curso en mención
Construir la lógica que da vida a una aplicación web suele ser el momento donde JavaScript demuestra su poder. Aquí aprenderás cómo manipular el DOM con JavaScript para leer datos, crear elementos y renderizar enlaces dinámicos en una página construida con Webpack y Tailwind, ideal si estás dando tus primeros pasos en proyectos frontend automatizados.
La clave está en entender que no hay magia: solo lógica estructurada que lee un archivo de datos, itera sobre él y construye nodos HTML listos para insertarse en el documento.
¿Cómo se estructura la función main para manipular el DOM?
Todo arranca dentro del archivo index.js, donde se define una constante con una arrow function llamada main. Esa función concentra los pasos para leer el archivo de data, crear elementos del DOM y modificarlos antes de insertarlos en el HTML.
Antes de escribir lógica, conviene limpiar el index.html de los textos placeholder que vienen por defecto, porque esos valores se van a popular desde JavaScript. También es buen momento para corregir cualquier typo y dejar el marcado ordenado.
¿Qué hace document.createElement en JavaScript? Crea un nuevo nodo HTML en memoria que aún no está insertado en el documento. Después puedes asignarle contenido y agregarlo al DOM con
appendChild.
¿Cómo creo el nodo del nombre con optional chaining?
La primera variable se declara con let name y se asigna a document.createElement, construyendo el nodo correspondiente al título principal. Para acceder al valor del archivo de data sin riesgo de romper la app, se usa optional chaining con el símbolo de pregunta: data?.name.
Esto evita errores si la propiedad no existe, porque en lugar de lanzar una excepción simplemente devuelve undefined. Es un patrón defensivo muy útil cuando trabajas con datos externos.
¿Cómo iterar sobre un array de links con map y template literals?
La parte interesante viene con los enlaces. Si analizas el recurso, data.links contiene cada red social como un objeto con propiedades como name, url, color y emoji. La meta es iterar por cada uno y construir su HTML correspondiente.
Para eso se declara let links = data?.links.map(...), donde map recibe una función anónima que toma cada link y retorna un template literal con la estructura HTML deseada.
Dentro del template literal se reemplazan los valores estáticos por interpolaciones dinámicas usando handlebars de JavaScript:
${link.color} para las clases de color de Tailwind como bg-yellow-200, text-yellow-800 y text-yellow-600.${link.url} para el atributo href del enlace.${link.name} para el texto visible.${link.emoji} para el ícono de cada red.Esto significa que el color asignado en el objeto de data se transmite directamente a las clases utilitarias de Tailwind, dándote control total desde un solo archivo.
¿Por qué necesito usar join después de map?
Cuando map termina su iteración, devuelve un array de strings. Si lo insertas tal cual en el HTML, vas a ver las comas que separan cada elemento del array.
La solución es encadenar .join('') con un string vacío como separador. Así obtienes una sola cadena continua lista para inyectarse en el DOM sin caracteres extra.
¿Qué diferencia hay entre map y forEach al manipular el DOM?
mapretorna un nuevo array con los valores transformados, ideal cuando quieres construir HTML.forEachsolo ejecuta la función pero no retorna nada, así que no sirve para acumular strings.
¿Cómo inserto los elementos creados en el documento?
Con los templates listos, falta crear el contenedor que los albergará. Se declara let newItem = document.createElement('section'), generando una nueva sección en minúsculas.
A ese nodo se le asigna el contenido con newItem.innerHTML = links, donde links es el string resultante del map y el join. Después se ejecuta links.appendChild(newItem) para insertarlo en el contenedor del DOM destinado a los enlaces.
Para el título, se hace lo equivalente con appendChild pasándole la variable name, que reemplaza el H1 original del HTML.
¿Cómo ejecuto la función main al final?
Todo el bloque anterior define la lógica, pero no la dispara. La última línea del archivo simplemente invoca main() como función, lo que ejecuta toda la cadena: lectura de data, creación de nodos, iteración con map, ensamblado con join e inserción con appendChild.
¿Qué es innerHTML y cuándo conviene usarlo? Es una propiedad que define el contenido HTML interno de un elemento. Funciona bien para inyectar plantillas generadas dinámicamente, aunque debes tener cuidado si los datos vienen de fuentes no confiables por riesgo de inyección.
¿Qué herramientas y conceptos clave aparecen en esta lógica?
Esta implementación combina varias habilidades fundamentales del desarrollo frontend moderno:
document.createElement y appendChild para crear e insertar nodos.?. que evita errores cuando una propiedad puede no existir.${} para construir HTML dinámico.bg-yellow-200 que se parametrizan vía JavaScript.La belleza de este patrón es que separa los datos (en el archivo de data) de la presentación (el template literal) y de la lógica de inserción (la función main). Si mañana cambias un emoji o agregas una red social, solo tocas el objeto de datos.
¿Qué cambios harías tú al template para adaptarlo a tu propio portafolio? Cuéntalo en los comentarios y comparte el resultado con la comunidad.
Kamilo Stevan Alomías Correa
En mi caso como no soy creador de contenido (aún) reemplacé los links por algunos certificados que he obtenido, también cambié los emojis por IDs visuales de cada curso en mención
Kenneth Angulo L
¿Puedo usar esto para inyectar clases dinámicas?
Absolutamente. Los template strings (las comillas francesas o invertidas `) son perfectos para construir sistemas de diseño dinámicos. En lugar de escribir docenas de clases estáticas, puedes tratar los nombres de las clases de CSS como variables.
Por ejemplo, si usas un framework de utilidad como Tailwind CSS, puedes inyectar propiedades directamente desde tu objeto de datos usando la sintaxis ${link.color}. Si tu base de datos dice que el color es yellow, tu código generará dinámicamente clases como bg-yellow-200 o text-yellow-800.
Esto convierte tu código en una fábrica de componentes. Solo necesitas diseñar la estructura HTML una vez (el esqueleto) y dejar que los datos decidan la pintura y la decoración. Es una técnica extremadamente poderosa para crear tarjetas o botones que cambian de apariencia automáticamente.
Jefred Bedoya
Estructura de la Lógica Principal
La función main actúa como el motor que orquesta la manipulación del DOM, reemplazando el contenido estático del HTML por datos dinámicos.
Pasos Clave de Implementación
document.createTextNode para inyectar datos de forma segura..map() sobre el array de datos para generar bloques HTML mediante template strings.(${link.propiedad}) para inyectar dinámicamente valores como URLs, colores y emojis..join('') es fundamental para convertir el array resultante en una cadena de texto limpia sin comas.document.createElement.innerHTML.appendChild para montar los nuevos elementos en el árbol del DOM.Harry A. Jiménez A.
Max Andy Diaz Neyra
yeferson soto
lo ideal sería usar el DocumentFragment(); para una mejor optimización del DOM
Cristian Blandon
En este caso la "optimización" al usar DocumentFragment sería insignificante, ya que solo se hace una inserción en el DOM (y DocumentFragment es ideal para múltiples inserciones). Un poco de información para evitar la desinformación: Does using a document fragment really improve performance?
Yo lo desarrollé de la siguiente manera:
// HTML //<h2></h2> (Hay un error con el editor de Platzi, me lo cambia cuando lo publico, lo tuve que comentar) <p id="description"></p> <ul id="social-links"></ul> // JS const $name = document.querySelector('h1'); const $text = document.getElementById('description'); const $social = document.getElementById('social-links'); const data = { name: "Harry J. Arena", description: "UI/UX Designer, Front-End.", social: [ { name: "LinkedIn", url: "https://linkedin.com/in/", username: "hadevelopux", }, { name: "Behance", url: "https://behance.net/", username: "hadevelopux", }, { name: "GitHub", url: "https://github.com/", username: "hadevelopux", }, { name: "Platzi", url: "https://platzi.com/p/", username: "hadevelopux", }, ], }; const text = () => { let name = document.createTextNode(data?.name); let description = document.createTextNode(data?.description); $name.appendChild(name); $text.appendChild(description); } const main = () => { let social = data?.social?.map((link) => { return `<li><a href="${link.url}${link.username}" class="link">${link.name}</a></li>`; }).join(''); let newItem = document.createElement("section"); newItem.innerHTML = social; $social.appendChild(newItem); } text(); main();
Dejo el código de la clase con la data completa:
const $links = document.getElementById("links"); const $name = document.querySelector("h1"); const data = { name: "Max Diaz", nickname: "maxter", description: "...", avatar: "...", social: [ { name: "platzi", url: "https://platzi.com/", username: "", }, { name: "youtube", url: "https://www.youtube.com/", username: "", }, { name: "github", url: "https://github.com/", username: "", }, { name: "instagram", url: "https://instagram.com/", username: "", }, { name: "twitter", url: "https://twitter.com/", username: "", }, { name: "linkedin", url: "https://www.linkedin.com/", username: "", }, { name: "twitch", url: "https://twitch.tv/", username: "", }, { name: "discord", url: "https://discord.gg/", username: "", }, ], links: [ { name: "mentorías", url: "https://example.com/", color: "fuchsia", emoji: "💻", }, { name: "blog", url: "https://example.com/", color: "red", emoji: "📖", }, { name: "podcast", url: "https://example.com/", color: "yellow", emoji: "💬", }, { name: "cursos", url: "https://example.com/", color: "lime", emoji: "🎒", }, { name: "sponsors", url: "https://example.com/", color: "rose", emoji: "🚀", }, ], footer: "Made with Love on Perú", }; const main = () => { let name = document.createTextNode(data.name); let links = data.links .map((link) => { return ` <div class="bg-${link.color}-200 px-4 py-5 w-full flex justify-between"> <a class="text-sm font-bold text-${link.color}-600 text-center hover:text-${link.color}-800 cursor-pointer" href=${link.url} target="_blank" > ${link.name} </a> <span>${link.emoji}</span> </div>`; }) .join(""); let newItem = document.createElement("section"); newItem.innerHTML = links; $links.appendChild(newItem); $name.appendChild(name); }; main(); ```const $links = document.getElementById("links");const $name = document.querySelector("h1"); const data = { name: "Max Diaz", nickname: "maxter", description: "...", avatar: "...", social: \[ { name: "platzi", url: "https://platzi.com/", username: "", }, { name: "youtube", url: "https://www.youtube.com/", username: "", }, { name: "github", url: "https://github.com/", username: "", }, { name: "instagram", url: "https://instagram.com/", username: "", }, { name: "twitter", url: "https://twitter.com/", username: "", }, { name: "linkedin", url: "https://www.linkedin.com/", username: "", }, { name: "twitch", url: "https://twitch.tv/", username: "", }, { name: "discord", url: "https://discord.gg/", username: "", }, ], links: \[ { name: "mentorías", url: "https://example.com/", color: "fuchsia", emoji: "💻", }, { name: "blog", url: "https://example.com/", color: "red", emoji: "📖", }, { name: "podcast", url: "https://example.com/", color: "yellow", emoji: "💬", }, { name: "cursos", url: "https://example.com/", color: "lime", emoji: "🎒", }, { name: "sponsors", url: "https://example.com/", color: "rose", emoji: "🚀", }, ], footer: "Made with Love on Perú",}; const main = () => { let name = document.createTextNode(data.name); let links = data.links .map((link) => { return ` \<div class="bg-${link.color}-200 px-4 py-5 w-full flex justify-between"> \<a class="text-sm font-bold text-${link.color}-600 text-center hover:text-${link.color}-800 cursor-pointer" href=${link.url} target="\_blank" > ${link.name} \</a> \<span>${link.emoji}\</span> \</div>`; }) .join(""); let newItem = document.createElement("section"); newItem.innerHTML = links; $links.appendChild(newItem); $name.appendChild(name);}; main();