- 👤 Andrés Cruz
The JavaScript Master Guide: Frontend, Backend, and Modern APIs Development
JavaScript has come an extraordinary way. What was once a simple scripting language to add basic interactivity to web pages is today a technological powerhouse that drives everything from complex desktop and mobile applications to robust servers and backend systems. It is the language of the web, and mastering it is essential for any modern developer.
Here we have consolidated decades of practical knowledge into one definitive resource. It doesn't matter if you're taking your first steps manipulating the DOM, exploring the powerful browser APIs for hardware interaction, building reactive interfaces with frameworks like Alpine.js, delving into the 3D world with Three.js, or even troubleshooting environment issues in Node.js. This article is designed to be your primary reference, your complete map of the JavaScript universe.
Prepare yourself for a deep and detailed journey. We're going to break down every concept with code examples, clear explanations, and the practical tips that only experience can offer. Welcome to the JavaScript master guide!
JavaScript Fundamentals and DOM Manipulation
Before building skyscrapers, we must master the foundations. In JavaScript, this means understanding how to interact with the Document Object Model (DOM), gracefully handle errors, grasp the language's subtleties like its function types, and finally, master the asynchronicity that powers the modern web. This section lays the groundwork for everything that follows.
Selectors in JavaScript: Beyond jQuery
We often use jQuery for ease when selecting DOM elements and working with them. However, we can access the same elements in similar ways with pure JavaScript, using the selectors getElementById, getElementsByTagName, and getElementsByClassName, in addition to the powerful querySelector and querySelectorAll.
What is querySelector used for and how to use it?
If we have worked with CSS selectors, then it will be very easy to work with this JavaScript selector, as it allows us to get elements using the same syntax we apply when creating our CSS rules. With querySelector we can get the first DOM element that matches what we pass to it. It works exactly the same as jQuery selectors.
<ul id="ejemplo1">
<li class="uno" id="uno"> uno </li>
<li class="dos" id="dos"> dos </li>
<li class="tres" id="tres"> tres </li>
<li class="cuatro" id="cuatro"> cuatro </li>
</ul>document.querySelector('#ejemplo1 li');
// nos trae <li class="uno" id="uno"> uno </li>
document.querySelector('#ejemplo1 .uno');
//nos trae <li class="uno" id="uno" > uno </li>
document.querySelector('#ejemplo1 #dos.dos');
//nos trae <li class="dos" id="dos" > dos </li>
document.querySelector('#ejemplo1 > #dos.dos');
//nos trae <li class="dos" id="dos" > dos </li>What is querySelectorAll used for and how to use it?
querySelectorAll works in the same way as the previous selector (querySelector), but it brings us all the elements that match the given condition, instead of just the first one; this collection of elements will be represented in an array (NodeList) over which we can iterate.
var lis = document.querySelectorAll('#ejemplo1 li');
for(var i=0; i<lis.length; i++){
console.log(lis[i]);
}Mastering these native selectors is the first step to freeing yourself from unnecessary dependencies and writing cleaner, more efficient code.
For more details and examples, see our original article: Selectors in JavaScript.
Error Handling in JavaScript with Try...Catch
When we build an application, we will always encounter errors. Whether due to our own coding mistakes or unexpected user input, a good application must anticipate and handle these errors so as not to fail catastrophically. For this, JavaScript provides us with the try...catch structure.
The syntax is simple:
try {
// código que podría fallar
} catch (error) {
// manejo del error
}The try block contains the error-prone code. If everything inside try executes smoothly, the catch block is completely ignored.
try {
console.log("Primer bloque try");
console.log("Último bloque try");
} catch (error) {
console.log("No ocurre error, este código es ignorado");
}
// output:
// Primer bloque try
// Último bloque tryHowever, if an error occurs within the try block, the execution of that block stops immediately and control passes to the catch block. The error object passed to the catch block contains valuable information about what went wrong.
try {
console.log("Primer bloque try"); // (1)
errorCode; // Variable no definida, esto genera un error (2)
console.log("Último bloque try"); // (3) Esta línea nunca se ejecuta
} catch (error) {
console.log("¡Ocurrió un error!"); // (4)
console.log(error.name); // ReferenceError
console.log(error.message); // errorCode is not defined
}
// output:
// Primer bloque try
// ¡Ocurrió un error!
// ReferenceError
// errorCode is not definedProper error handling is fundamental to the robustness of any application. The try...catch block is your main tool to achieve this.
Learn more about this and other aspects of error handling in our post: Error handling in JavaScript.
Declarative Functions vs. Function Expressions
In JavaScript, not all functions are created equal. Normally, when we modularize our programs, we use two main ways to define functions, and although they look similar, the JavaScript interpreter treats them very differently due to a concept called "hoisting."
Function Expressions
A function expression is assigned to a variable. You have surely seen it in many tutorials:
var sumar = function (a,b) { return a+b; }With this type of function, the variable sumar is "hoisted" during compilation, but its assignment (the function itself) is not. This means you cannot call the function before its declaration.
console.log(sumar(2,3)); // Error!
var sumar = function (a,b) { return a+b; }
// TypeError: sumar is not a functionDeclarative Functions
This is the classic form, known in many other programming languages:
function sumar (a,b) { return a+b; }In this case, both the function name and its body are hoisted to the top of its scope. This allows the function to be called even before it appears in the code.
console.log(sumar(2,3)); // Funciona!
function sumar(a,b) { return a+b; }
// output: 5The key difference is "hoisting." Function declarations are processed before any code is executed, while function expressions are processed only when the interpreter reaches that line of code. Understanding this difference is crucial to avoid unexpected errors and structure your code better.
Delve into this concept in our article: Declarative functions and functions of expressions in JavaScript.
The Secret to Smooth Animations: requestAnimationFrame()
When it comes to creating animations on the web, performance is key. Historically, functions like setInterval or setTimeout were used to create animation loops. For example:
function dibujar() {
setTimeout(dibujar, 100);
// codigo para mover un elemento
}
dibujar();The problem with this approach is that it is not optimized. The browser will execute the code regardless of whether the tab is visible, whether the device can handle it, or whether it is synchronized with the screen's refresh cycle. This often results in choppy animations (jank) and unnecessary CPU consumption.
The modern solution is window.requestAnimationFrame(). This function tells the browser: "I want to perform an animation and I request that you execute my update function just before the next screen repaint."
The browser is responsible for optimizing everything:
- It synchronizes with the monitor's refresh rate (usually 60fps).
- It pauses the animation if the tab is not active, saving battery and CPU.
- It groups multiple animations into a single repaint cycle for greater efficiency.
The basic syntax is very simple:
function animar(timestamp) {
// Mueve elementos basándote en el tiempo transcurrido
// ...
// Solicita el siguiente frame
requestAnimationFrame(animar);
}
// Inicia el bucle de animación
requestAnimationFrame(animar);requestAnimationFrame is the foundation of all high-performance animations on the web today, from simple UI transitions to complex 2D and 3D games with Canvas or WebGL. It is the correct and professional way to animate in JavaScript.
Find out more about how to implement it in: The secret of animations in JavaScript (requestAnimationFrame())
Executing JavaScript in Parallel with Web Workers
By nature, JavaScript runs on a single thread (single-threaded). This means that if you execute a computationally intensive task (like processing a large image or calculating thousands of prime numbers), the user interface will freeze until the task finishes. The user will not be able to click on buttons or interact with the page.
Web Workers are the solution to this problem. They offer a mechanism to execute JavaScript scripts in a secondary thread, in the background, without blocking the main UI thread.
Getting Started with Web Workers
To create a Web Worker, you need a separate JavaScript file that contains the heavy task code. Then, from your main script, you create a new instance of Worker.
// Main thread (main.js)
var worker = new Worker('tarea_pesada.js');Communication between the main thread and the worker is done through an asynchronous messaging system.
Message Passing
From the main thread, you can send data to the worker using postMessage() and listen for results with the onmessage event.
// Main thread (main.js)
worker.postMessage({ numero: 45 }); // Enviamos un número para calcular
worker.onmessage = function(event) {
console.log("Resultado del Worker:", event.data); // Recibimos el resultado
};The worker, in turn, listens for messages with its own onmessage and returns the result using postMessage().
// Secondary thread (tarea_pesada.js)
self.onmessage = function(event) {
const numero = event.data.numero;
// Tarea pesada: calcular si es primo
const resultado = esPrimo(numero);
self.postMessage({ resultado: resultado }); // Devolvemos el resultado
};
function esPrimo(n) { /* ... lógica intensiva ... */ }Web Workers are indispensable for web applications that need to perform complex calculations, real-time data processing, or any task that could compromise the fluidity of the user experience.
Explore a practical example of prime number calculation in our article: Web workers to execute JavaScript in parallel
The Browser as a Platform: Hardware and UI APIs
JavaScript no longer lives solely in an isolated sandbox. Thanks to a growing set of Browser APIs, it can now interact directly with the device's hardware and with operating system interface elements. From knowing the user's location to making the phone vibrate or receiving voice commands, these APIs transform a simple web page into a rich and powerful application, bringing it closer than ever to the experience of a native app.
Geolocation with JavaScript and HTML5
With the emergence of mobile devices, geolocation has become one of the most useful features. The Geolocation API allows obtaining the user's position from the browser, as long as three key conditions are met: the browser supports it, the site is served over HTTPS, and, most importantly, the user grants their explicit permission.
How does the browser obtain the location?
The device combines different data sources to determine the location, each with a different level of precision:
- GPS: Very accurate, ideal on mobiles with a clear sky.
- Nearby WiFi Networks: Good accuracy in urban areas.
- Cellular Towers: Less precise, but useful as a backup.
- IP Address: The least precise, offers a city or region level location.
Basic Implementation
Access to the API is done through the navigator.geolocation object. The main method is getCurrentPosition().
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
let lat = position.coords.latitude;
let lon = position.coords.longitude;
console.log("Latitud: " + lat + ", Longitud: " + lon);
}, function(error) {
console.error("Error al obtener la ubicación: ", error.message);
});
} else { alert("Geolocation is not supported by this browser."); }The method receives three arguments:
- A callback function in case of success, which receives a Position object with the coordinates.
- A callback function in case of error (permission denied, timeout, etc.).
- An options object (optional) to configure precision and timeout.
Observing Position in Real Time
For applications that need to track the user, such as a running app, watchPosition() is used, which works similarly but invokes the callback every time the device's position changes.
const watchId = navigator.geolocation.watchPosition(successCallback, errorCallback);
// Para dejar de observar
// navigator.geolocation.clearWatch(watchId);This API is the gateway to creating location-based experiences, from interactive maps to personalized content based on the user's area.
Learn how to integrate it with Google maps and handle all its details in our article: Geolocation with JavaScript and HTML5
The Vibration API
One of the most subtle yet effective APIs for improving the user experience on mobile devices is the Vibration API. It allows control of the device's vibration motor directly from JavaScript, providing haptic feedback for actions, notifications, or alerts.
The method is incredibly simple and is accessed through navigator.vibrate().
Simple Vibration
For a single vibration, the duration in milliseconds is passed.
// Vibrate for 200 milliseconds
navigator.vibrate(200);Vibration Patterns
The true power of the API lies in the ability to create complex patterns by passing an array of values. Numbers in even positions represent vibration durations, and those in odd positions represent pause durations.
// Pattern: vibrate 100ms, pause 50ms, vibrate 150ms
navigator.vibrate([100, 50, 150]); This is perfect for creating distinctive notifications. For example, an "SOS" pattern (three short, three long, three short):
// SOS: · · · — — — · · ·
navigator.vibrate([100, 50, 100, 50, 100, 200, 300, 50, 300, 50, 300, 200, 100, 50, 100, 50, 100]);Stop Vibration
To cancel any ongoing vibration, an empty array or the value 0 is passed.
navigator.vibrate([]); // or navigator.vibrate(0);It is important to check if the browser supports the API before using it:
window.navigator = window.navigator || {};
if (window.navigator.vibrate === undefined) {
alert("Your browser does not support the Vibration API");
}Although its support is mostly limited to mobile browsers, it is an excellent tool for adding a layer of interaction that goes beyond the visual.
Learn more about its use cases in: The Vibration API in HTML5 with JavaScript
Ambient Light Events
What if your web application could adapt its interface according to the ambient lighting? The Ambient Light Sensor API allows exactly that, detecting the level of ambient light through the device's sensor (common on phones and tablets).
This API exposes the devicelight event, which provides the light level in units of lux.
According to the Mozilla Foundation, we can use these ranges as reference:
- Less than 50 lux: Dark or very dim (equivalent to a room at night).
- Between 50 and 10,000 lux: Normal lighting (indoors, cloudy day).
- More than 10,000 lux: Very bright (direct sunlight).
Implementation
To use it, we simply add a listener to the devicelight event on the window object.
if ('ondevicelight' in window) {
window.addEventListener('devicelight', function(event) {
let lux = event.value;
console.log("Nivel de luz actual: " + lux + " lux");
if (lux < 50) {
// Aplicar tema oscuro
document.body.classList.add('dark-theme');
document.body.classList.remove('light-theme');
} else {
// Aplicar tema claro
document.body.classList.add('light-theme');
document.body.classList.remove('dark-theme');
}
});
} else {
console.log("La API de luz ambiental no es soportada por este navegador.");
}The use cases are varied:
- Automatic theme switching: Activate a dark mode when there is low light to reduce eye strain.
- Brightness and contrast adjustment: Increase text contrast in bright light conditions.
- Adaptive content: Display different visual resources. For example, a video with more vivid colors if there is a lot of ambient light.
Although it is an experimental API with limited support, it opens the door to creating truly contextual and adaptive web applications.
Explore more about this fascinating capability in: Ambient Light Events with JavaScript to detect light levels
Accessing the Camera and Microphone (Media Devices API)
Being able to access a user's video and audio stream directly from the browser changed the game. The navigator.mediaDevices.getUserMedia() API is the gateway to a world of interactive applications: QR code readers, custom avatars, video calls, and much more.
Key Requirements
- HTTPS Mandatory: For security, browsers only allow camera access on secure sites.
- User Permission: The call to getUserMedia() will always trigger a permission request that the user must accept.
- Compatibility: Using navigator.mediaDevices.getUserMedia() is the modern standard. Older versions with prefixes (webkitGetUserMedia) are obsolete.
Implementation: Displaying Live Video
The process consists of requesting the stream and, if granted, assigning it to the srcObject attribute of a <video> element.
<video id="videoPlayer" autoplay playsinline></video>
<button id="captureBtn">Capture Photo</button>
<canvas id="canvas" style="display:none;"></canvas>const video = document.getElementById('videoPlayer');
const constraints = {
video: { width: 1280, height: 720 },
audio: false
};
async function startCamera() {
try {
const stream = await navigator.mediaDevices.getUserMedia(constraints);
video.srcObject = stream;
} catch (err) {
console.error("Error al acceder a la cámara: ", err);
alert("No se pudo acceder a la cámara. Asegúrate de dar permiso y usar HTTPS.");
}
}
startCamera();Capturing a Photo with Canvas
Once we have the stream, we can "draw" a frame of the video onto a <canvas> element to capture an image.
const captureBtn = document.getElementById('captureBtn');
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
captureBtn.addEventListener('click', () => {
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
context.drawImage(video, 0, 0, canvas.width, canvas.height);
// Opcional: Obtener la imagen como Data URL
const dataUrl = canvas.toDataURL('image/png');
console.log(dataUrl);
// Opcional: Descargar la imagen
const link = document.createElement('a');
link.href = dataUrl;
link.download = 'captura.png';
link.click();
});This API is one of the most powerful in the frontend developer's arsenal, allowing the creation of experiences that were previously exclusive to native applications.
Know all the details and error handling in our complete guide: Accessing a device's camera and microphone with JavaScript
Detecting Device Orientation (Device Orientation Events)
Thanks to the gyroscope and accelerometer present in most mobile devices, JavaScript can detect how a user is holding their phone or tablet. The DeviceOrientationEvent API gives us access to this information in real time, allowing us to create immersive experiences that react to physical movement.
Difference between DeviceOrientation and DeviceMotion
- DeviceOrientationEvent: reports on the static orientation of the device.
- DeviceMotionEvent: reports on acceleration and rotation speed.
For most UI use cases, like rotating elements, DeviceOrientationEvent is sufficient.
The Axes: Alpha, Beta, and Gamma
The event provides three key values:
- alpha (Z-axis): Represents rotation in the horizontal plane, like a compass (0° to 360°).
- beta (X-axis): Represents forward or backward tilt (-180° to 180°).
- gamma (Y-axis): Represents side-to-side tilt (-90° to 90°).
Implementation
We listen to the deviceorientation event on the window object.
if (window.DeviceOrientationEvent) {
window.addEventListener('deviceorientation', function(event) {
const alpha = event.alpha; // Rotación Z
const beta = event.beta; // Inclinación X
const gamma = event.gamma; // Inclinación Y
// Ejemplo: Rotar un elemento en la página
const elemento = document.getElementById('miElemento');
elemento.style.transform = `rotateZ(${alpha}deg) rotateX(${beta}deg) rotateY(${gamma}deg)`;
console.log(`Orientación: alpha=${alpha.toFixed(2)}, beta=${beta.toFixed(2)}, gamma=${gamma.toFixed(2)}`);
}, true);
} else {
alert("The Device Orientation API is not supported.");
}Important: As with other sensitive APIs, DeviceOrientationEvent requires a secure context (HTTPS) and, in some browsers like Safari, the user must explicitly grant permission.
This API is fundamental for browser-based games, virtual reality (VR) experiences, and any application that benefits from physical interaction with the device.
Discover how to handle permissions and see a functional example in: Detecting device orientation with the javaScript API
Detecting Screen Resolution and the Viewport
Creating responsive layouts often requires knowing more than just the viewport width. JavaScript gives us access to three distinct "levels" of size, and understanding the difference is crucial to avoid calculation errors in our designs.
1. The Real Screen (screen)
The screen object describes the properties of the user's physical monitor, regardless of the browser window size.
- screen.width / screen.height: Total width and height of the screen in pixels.
- screen.availWidth / screen.availHeight: Available width and height, excluding operating system bars like the Windows taskbar or macOS Dock.
console.log(`Resolución total: ${screen.width}x${screen.height}`);
console.log(`Resolución disponible: ${screen.availWidth}x${screen.availHeight}`);2. The Browser Window (window)
These properties describe the size of the entire browser window, including borders, toolbars, and scrollbars.
window.outerWidth/window.outerHeight: The total size of the browser window.
3. The Viewport (window and document)
This is the most important size for web design. It represents the visible area inside the browser window where your content is rendered.
window.innerWidth/window.innerHeight: The width and height of the viewport, including the scrollbar if it is visible.document.documentElement.clientWidth/document.documentElement.clientHeight: The width and height of the viewport, excluding the scrollbar. This is often the most useful and consistent measurement.
console.log(Viewport (with scroll): ${window.innerWidth}x${window.innerHeight});
console.log(Viewport (without scroll): ${document.documentElement.clientWidth}x${document.documentElement.clientHeight});Knowing which one to use is key: use screen to get device information, but rely on clientWidth/clientHeight for your layouts and responsive design calculations.
Know each property and its differences in depth in our article: How to get screen resolution with JavaScript?
The Battery API (Battery Status API)
The Battery Status API is a powerful tool, although currently with limited support, that allows web applications to access the device's battery status. This opens the door to intelligent optimizations to save energy when the battery level is low.
The API is accessed through navigator.getBattery(), which returns a promise that resolves with a BatteryManager object.
BatteryManager Object Properties
- charging (boolean): true if the device is charging.
- chargingTime (number): Seconds remaining until the battery is fully charged (or Infinity if not charging).
- dischargingTime (number): Seconds remaining until the battery runs out.
- level (number): The battery level, a value between 0.0 and 1.0.
Implementation
if ('getBattery' in navigator) {
navigator.getBattery().then(function(battery) {
console.log("Nivel de batería: ", battery.level * 100 + "%");
console.log("Cargando: ", battery.charging);
// Función para actualizar el estado
function updateBatteryStatus() {
document.getElementById('level').textContent = Math.round(battery.level * 100);
document.getElementById('charging').textContent = battery.charging ? 'Sí' : 'No';
// Caso de uso: Activar modo de ahorro de energía
if (battery.level < 0.2 && !battery.charging) {
document.body.classList.add('power-saving-mode');
} else {
document.body.classList.remove('power-saving-mode');
}
}
// Actualizar al inicio
updateBatteryStatus();
// Escuchar cambios en el estado
battery.addEventListener('levelchange', updateBatteryStatus);
battery.addEventListener('chargingchange', updateBatteryStatus);
});
} else {
alert("La API de Batería no es soportada.");
}Use cases:
- Power-saving mode: Reduce or stop animations, lower the frequency of AJAX requests, and dim the UI brightness.
- User alerts: Notify when the battery is low so they can save their work.
- Prevent costly operations: Avoid starting large downloads or intensive processing if the battery is low and not charging.
Explore a functional example in: The Battery API in HTML5 with JavaScript and HTML5
Native Notifications (Notifications API)
Push notifications are fundamental for modern web interaction. The Notifications API allows web applications to display messages to the user outside the browser window, similar to desktop or mobile application notifications.
Permissions
The first, and most crucial, step is to request the user's permission. You cannot send notifications without their explicit consent. Permission can have three states: default (user has not decided), granted (allowed), or denied (blocked).
if (!('Notification' in window)) {
alert('Este navegador no soporta notificaciones de escritorio');
} else if (Notification.permission === 'granted') {
// Si ya tenemos permiso, podemos crear una notificación
new Notification("¡Hola de nuevo!");
} else if (Notification.permission !== 'denied') {
Notification.requestPermission().then(function (permission) {
if (permission === 'granted') {
new Notification("¡Gracias por permitir las notificaciones!");
}
});
}Creating a Notification
Once permission is obtained, creating a notification is as simple as instantiating the Notification object. The constructor accepts a title and an options object to customize it.
const options = {
body: "Este es el cuerpo de la notificación. Aquí puedes poner más detalles.",
icon: "/path/to/icon.png", // URL de un icono
badge: "/path/to/badge.png", // Icono para Android
image: "/path/to/image.jpg", // Una imagen grande
vibrate: [200, 100, 200], // Patrón de vibración
tag: "mensaje-unico", // Agrupa notificaciones, reemplazando la anterior con el mismo tag
renotify: true // Vibra y suena aunque el tag sea el mismo
};
const notif = new Notification("Título de la Notificación", options);Notification Events
You can attach events to interact with the notification:
- onclick: Fires when the user clicks on the notification.
- onclose: Fires when the notification is closed.
- onerror: Fires if an error occurs while displaying it.
- onshow: Fires when the notification is displayed.
notif.onclick = function(event) {
event.preventDefault(); // Previene que el navegador enfoque la pestaña
window.open('https://www.desarrollolibre.net', '_blank');
}This API is essential for applications like email clients, social networks, or any system that needs to alert the user of important events in real time.
Discover all the customization options in: Introduction to notifications in JavaScript
The Page Visibility API
Did you know that your web application can tell if the user is viewing it or has switched to another tab? The Page Visibility API is a simple yet extremely useful tool for optimizing performance and improving user experience, by pausing unnecessary tasks when the page is not visible.
The API exposes two main properties on the document object:
- document.hidden (boolean): Is true if the page is not visible.
- document.visibilityState (string): Can be visible, hidden, prerender, or unloaded.
In addition, it fires the visibilitychange event every time the state changes.
Implementation
let hidden, visibilityChange;
if (typeof document.hidden !== "undefined") { // Soporte estándar
hidden = "hidden";
visibilityChange = "visibilitychange";
} else if (typeof document.msHidden !== "undefined") {
hidden = "msHidden";
visibilityChange = "msvisibilitychange";
} else if (typeof document.webkitHidden !== "undefined") {
hidden = "webkitHidden";
visibilityChange = "webkitvisibilitychange";
}
function handleVisibilityChange() {
if (document[hidden]) {
// La página está oculta
console.log("Pestaña oculta. Pausando video...");
video.pause();
document.title = "¡No te vayas!";
} else {
// La página está visible
console.log("Pestaña visible. Reanudando video...");
video.play();
document.title = "¡Bienvenido de vuelta!";
}
}
document.addEventListener(visibilityChange, handleVisibilityChange, false);Practical Use Cases
- Video/Audio Players: Pause playback when the user switches tabs and resume it when they return.
- Games and Animations: Stop the requestAnimationFrame loop so as not to uselessly consume CPU.
- Server Requests: Reduce the frequency of polling (periodic AJAX requests) if the page is not active.
- Analytics: More accurately measure the time a user spends "actively" on the page.
- State Saving: In long document or book viewers, save the user's reading position just when they leave the tab.
Integrating this API is one of the simplest and highest-impact optimizations you can make in your projects.
Learn more in depth about this API in: Page Visibility API in JavaScript and HTML5
The Fullscreen API
The Fullscreen API allows a specific HTML element (or the entire page) to be displayed in fullscreen mode, removing all browser and operating system interface distractions. It is essential for immersive experiences such as video players, image galleries, games, or document viewers.
Requirements
- User interaction: The fullscreen request must be initiated by a user action (such as a click or key press) for security reasons.
- Prefix Handling: The API has undergone changes and still requires handling browser prefixes for full compatibility.
Implementation
The process involves two main actions: requesting to enter fullscreen and requesting to exit it.
const miElemento = document.getElementById('video-player');
const toggleBtn = document.getElementById('fullscreen-btn');
toggleBtn.addEventListener('click', function() {
if (!document.fullscreenElement) {
// Entrar en pantalla completa
if (miElemento.requestFullscreen) {
miElemento.requestFullscreen();
} else if (miElemento.mozRequestFullScreen) { // Firefox
miElemento.mozRequestFullScreen();
} else if (miElemento.webkitRequestFullscreen) { // Chrome, Safari y Opera
miElemento.webkitRequestFullscreen();
} else if (miElemento.msRequestFullscreen) { // IE/Edge
miElemento.msRequestFullscreen();
}
} else {
// Salir de pantalla completa
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
}
}
});
// También puedes escuchar cambios de estado
document.addEventListener('fullscreenchange', function() {
console.log('El modo de pantalla completa ha cambiado');
});It is possible to apply specific styles for fullscreen mode using the :fullscreen pseudo-selector in CSS.
#video-player:fullscreen {
background-color: black;
width: 100%;
height: 100%;
}This API is an excellent resource for focusing the user's attention on the most important content.
Explore a complete and functional example in: The FullSreen API in JavaScript
Speech Recognition and Synthesis (Web Speech API)
The Web Speech API is one of JavaScript's most futuristic and powerful APIs. It is divided into two parts: speech recognition (Speech-to-Text) and speech synthesis (Text-to-Speech), allowing our applications to listen and speak.
Speech Recognition (SpeechRecognition)
This part of the API converts audio from the user's microphone into text. It is ideal for creating voice-controlled interfaces, text dictation, or command systems.
Implementation focuses on the SpeechRecognition object (often with the webkit prefix).
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
if (SpeechRecognition) {
const recognition = new SpeechRecognition();
recognition.lang = 'es-VE'; // Configurar el idioma
recognition.continuous = false; // true para escuchar continuamente
recognition.interimResults = false; // true para obtener resultados parciales
recognition.onresult = (event) => {
const transcript = event.results[0][0].transcript;
console.log("Has dicho: ", transcript);
// Aquí puedes procesar el comando
};
recognition.onend = () => {
console.log("La escucha ha terminado.");
};
// Iniciar la escucha
document.getElementById('start-btn').onclick = () => recognition.start();
}Speech Synthesis (SpeechSynthesis)
This part of the API allows the browser to read text aloud. It is perfect for accessibility, voice-guided tutorials, or giving the user audio feedback.
It is based on the SpeechSynthesisUtterance (the message) and speechSynthesis (the controller) objects.
const utterance = new SpeechSynthesisUtterance("Hola, mundo. Esto es una prueba de síntesis de voz.");
// Opcional: configurar voz, tono y velocidad
utterance.lang = 'es-ES';
utterance.pitch = 1; // Tono (0 a 2)
utterance.rate = 1; // Velocidad (0.1 a 10)
// Obtener lista de voces disponibles
let voices = [];
speechSynthesis.onvoiceschanged = () => {
voices = speechSynthesis.getVoices();
// utterance.voice = voices[4]; // Asignar una voz específica
};
// Hablar
document.getElementById('speak-btn').onclick = () => speechSynthesis.speak(utterance);The Web Speech API opens up a range of possibilities for creating more natural and interactive user interfaces.
Learn how to configure both parts in detail in our articles: The Speech Recognition API in JavaScript: speechRecognition() and Speech synthesis with native JavaScript.
Copying Text to the Clipboard (Clipboard API)
Giving the user the ability to copy text with a single click is a micro-interaction that greatly enhances usability. The modern and secure way to do this is with the Clipboard API, specifically with navigator.clipboard.writeText().
The old method, document.execCommand('copy'), is obsolete, synchronous, and requires complex DOM manipulations (creating a textarea, selecting its content, etc.). The new API is asynchronous, promise-based, and much safer.
Modern Implementation
The navigator.clipboard.writeText() method takes the text to be copied and returns a promise. Since it is an API that interacts with the operating system, it requires a secure context (HTTPS) and user permission, which is generally granted automatically if the page is in focus.
const copyBtn = document.getElementById('copyButton');
const textToCopy = "Este es el texto que se copiará.";
copyBtn.addEventListener('click', async () => {
try {
await navigator.clipboard.writeText(textToCopy);
copyBtn.textContent = '¡Copiado!';
setTimeout(() => { copyBtn.textContent = 'Copiar'; }, 2000);
} catch (err) {
console.error('Error al copiar el texto: ', err);
alert('No se pudo copiar el texto. Asegúrate de estar en un contexto seguro (HTTPS).');
}
});This approach is clean, readable, and is the recommended standard today.
Ability to Read from the Clipboard
The API also allows reading the clipboard content with navigator.clipboard.readText(), although this requires explicit user permission for obvious security reasons.
async function pasteText() {
try {
const text = await navigator.clipboard.readText();
document.getElementById('myTextarea').value = text;
} catch (err) {
console.error('Error al leer del portapapeles: ', err);
}
}Correctly implementing this functionality is a sign of a polished and modern web application.
Know all the details and compatibility handling in: How to Copy Text to the Clipboard with JavaScript using the Clipboard API
Text Selection (window.getSelection())
Knowing what text a user is selecting on your page opens the door to interactive features like contextual pop-ups (think Medium), text highlighting, or the ability to add notes. The key function for this is window.getSelection().
This function returns a Selection object that contains detailed information about the range of text selected by the user.
Getting the Selected Text
The most direct way to get the text as a string is to use the .toString() method of the returned object.
document.addEventListener('mouseup', () => {
const selection = window.getSelection();
const selectedText = selection.toString().trim();
if (selectedText.length > 0) {
console.log("Texto seleccionado:", selectedText);
// Aquí podrías mostrar un tooltip o menú contextual
const rect = selection.getRangeAt(0).getBoundingClientRect();
showTooltipAt(rect.left, rect.top);
}
});The Selection object is much more powerful. It contains properties like:
- anchorNode and focusNode: The DOM nodes where the selection starts and ends.
- anchorOffset and focusOffset: The character position within those nodes.
- getRangeAt(0): Returns a Range object with geometric information (getBoundingClientRect()) and methods to manipulate the selected content.
This API is the basis for creating rich text editors and other annotation tools directly in the browser.
Dive into the complexities of text selection in: window.getSelection() in JavaScript - The torment of text selection
Observing Element Visibility (Intersection Observer)
Before the Intersection Observer API, detecting if an element was visible in the viewport required listening to the scroll event and constantly making calculations with getBoundingClientRect(). This approach was inefficient and prone to performance issues.
IntersectionObserver is a modern API that allows an asynchronous callback function to be executed every time an "observed" element enters or leaves the viewport (or another containing element).
Key Concepts
- Target: The element we want to observe.
- Root: The element that acts as the viewport. By default, it's the browser window.
- Threshold: A number (or array of numbers) between 0.0 and 1.0 that defines what percentage of the target must be visible for the callback to fire.
Implementation
It's ideal for functionalities like image "lazy loading" or implementing a "scroll-spy" (highlighting the navigation index link corresponding to the visible section).
const sections = document.querySelectorAll('section');
const navLinks = document.querySelectorAll('nav a');
const options = {
root: null, // viewport del navegador
threshold: 0.5, // 50% del elemento debe ser visible
rootMargin: '0px'
};
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
console.log(`${entry.target.id} está visible.`);
// Resaltar el enlace correspondiente
const activeLink = document.querySelector(`nav a[href="#${entry.target.id}"]`);
navLinks.forEach(link => link.classList.remove('active'));
activeLink.classList.add('active');
}
});
}, options);
// Observar cada sección
sections.forEach(section => {
observer.observe(section);
});This API is one of the most important performance optimization tools for modern web development.
Learn how to configure it in detail in our guide: IntersectionObserver - Observes HTML elements when they are visible via JavaScript scrolling.
Working with Data and Storage
Modern web applications need to manage data. Whether saving user preferences, handling locally uploaded files, or interacting with third-party APIs, JavaScript offers a comprehensive set of tools for data handling and client-side persistence. In this section we will explore everything from classic cookies to modern file and storage APIs, including best practices on which technology to use in each case.
Getting Started with Cookies in JavaScript
Cookies are small text strings that the browser stores and automatically sends to the server with every HTTP request. Although more modern storage options exist, cookies remain fundamental for session management and authentication.
In JavaScript, all cookies for a domain are accessible via document.cookie as a single string.
Create and Update a Cookie
To create a cookie, you assign a string in the format key=value to document.cookie. You can add additional attributes separated by semicolons.
function setCookie(name, value, days) {
let expires = "";
if (days) {
const date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
expires = "; expires=" + date.toUTCString();
}
// path=/ asegura que la cookie esté disponible en todo el sitio
document.cookie = name + "=" + (value || "") + expires + "; path=/";
}
// Crear una cookie llamada 'user' con el valor 'Andres' que expira en 7 días
setCookie('user', 'Andres', 7);Read Cookies
Reading a specific cookie requires parsing the document.cookie string.
function getCookie(name) {
const nameEQ = name + "=";
const ca = document.cookie.split(';');
for(let i=0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) == ' ') c = c.substring(1, c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length);
}
return null;
}
const user = getCookie('user'); // "Andres"Delete a Cookie
To delete a cookie, you simply set its expiration date to the past.
function eraseCookie(name) {
document.cookie = name+'=; Max-Age=-99999999;';
}
eraseCookie('user');Although localStorage and sessionStorage are simpler for general client-side data storage, cookies remain the standard tool for state communication between the client and the server.
Master cookie handling in: First steps with Cookies in JavaScript
The File API
The File API in JavaScript allows web applications to access the content of files selected by the user, without the need to upload them to a server first. This is ideal for client-side validations, image previews, or local data processing.
When a user selects files via an <input type="file">, we can access them through the input's files property, which is a FileList object.
1. Getting File Data
Each File object in the list contains read-only metadata:
file.name: The name of the file (e.g., "photo.jpg").file.size: The size in bytes.file.type: The MIME type (e.g., "image/jpeg").file.lastModifiedDate: The last modified date.
<input type="file" id="fileInput" multiple>document.getElementById('fileInput').addEventListener('change', (event) => {
const files = event.target.files;
for (const file of files) {
if (file.type.startsWith('image/')) {
console.log(`Archivo: ${file.name}, Tamaño: ${file.size} bytes`);
} else {
alert(`${file.name} no es una imagen!`);
}
}
});2. Accessing File Content with FileReader
To read the actual content of a file, we use the FileReader object. This operates asynchronously and uses events to notify when reading is complete.
FileReader reading methods:
readAsText(file): Reads the file as a string of text.readAsDataURL(file): Reads the file and returns its content as a Base64 data URL (ideal for image previews).readAsArrayBuffer(file): Reads the file as anArrayBuffer(for binary data).
function previewImage(file) {
if (!file.type.startsWith('image/')){ return }
const reader = new FileReader();
reader.onload = function(e) {
const img = document.createElement('img');
img.src = e.target.result; // e.target.result contiene el DataURL
img.width = 200;
document.body.appendChild(img);
};
reader.onerror = function(e) {
console.error("Error al leer el archivo", e);
};
reader.readAsDataURL(file);
}
document.getElementById('fileInput').addEventListener('change', (event) => {
Array.from(event.target.files).forEach(previewImage);
});The File API is fundamental for any application that needs to interact with the user's local files in a rich and secure way.
Delve into both parts of the API in our articles: getting file data and accessing its content.
Creating Downloadable Files on the Client
Generally, to download a file, the browser makes a request to a server. However, with JavaScript it is possible to dynamically generate content on the client and offer it as a downloadable file, without any backend interaction. This is achieved by creating a "Blob" and an object URL.
The Two-Step Process
- Create a Blob: A
Blob(Binary Large Object) is a file-like object that holds immutable, raw data. We can create it from text, JSON, or any other data. - Create an Object URL:
URL.createObjectURL(blob)generates a unique, temporary URL that points to the Blob in the browser's memory.
Implementation
Once we have the object URL, we assign it to the href attribute of an <a> link, add the download attribute to suggest a file name, and simulate a click on it.
<textarea id="myText" rows="5">This is the content of my file.</textarea> <button id="downloadBtn">Download as TXT</button>const downloadBtn = document.getElementById('downloadBtn');
const textarea = document.getElementById('myText');
downloadBtn.addEventListener('click', () => {
const textToSave = textarea.value;
// 1. Crear el Blob
const blob = new Blob([textToSave], { type: 'text/plain' });
// 2. Crear la URL del objeto
const url = URL.createObjectURL(blob);
// 3. Crear un enlace, configurar y hacer clic
const link = document.createElement('a');
link.href = url;
link.download = 'mi-archivo.txt'; // Nombre del archivo a descargar
document.body.appendChild(link); // Necesario para Firefox
link.click();
// 4. Limpiar
document.body.removeChild(link);
URL.revokeObjectURL(url); // Liberar memoria
});This method is extremely useful for exporting data from an application, such as configurations in JSON, content from a text editor, or even images generated with Canvas (converting the canvas to a Blob with canvas.toBlob()).
Learn more about this technique and Data URIs in: Creating a downloadable file with JavaScript.
Stop using localStorage!
Although localStorage has been the quick solution for persistent client-side storage for years, it has serious drawbacks that make it an outdated and potentially problematic option for modern applications.
Problems with localStorage
- Blocking and Synchronous: Every read or write operation in
localStorage(localStorage.getItem(),localStorage.setItem()) is synchronous. If you store or read large amounts of data, you can block the main thread and freeze the user interface. - Only Stores Strings:
localStoragecan only save strings. To store objects or arrays, you must serialize them to JSON (JSON.stringify) and deserialize them when reading (JSON.parse). Forgetting to do this is a common source of errors. - No Web Worker Support:
localStoragecannot be accessed from a Web Worker, which limits its usefulness in applications that seek to offload work from the main thread. - Security Risks: It is a common target for Cross-Site Scripting (XSS) attacks. If an attacker injects a script into your page, they can read all the content of
localStorage, including session tokens, JWTs, or other sensitive information that developers often save there for convenience. - Limited Size: Generally limited to 5MB per domain.
The Modern Alternative: IndexedDB
IndexedDB is the recommended solution for storing large amounts of structured data on the client.
- Asynchronous: All its operations are asynchronous, so it does not block the UI.
- Structured Storage: It is a NoSQL object database. It can store almost anything JavaScript can represent, including files, Blobs, and complex objects, without the need for serialization.
- Transactional: Ensures data integrity.
- Accessible from Web Workers: Allows data handling in secondary threads.
- Larger Capacity: It can store much larger amounts of data (depends on the browser and available disk space).
Although its API is more complex than localStorage's, there are libraries like localForage or Dexie.js that provide a simple wrapper, similar to localStorage's, but with all the power of IndexedDB underneath.
In short, for anything beyond saving a simple theme preference (dark/light), it's time to abandon localStorage and adopt more robust and performant solutions.
Discover all the arguments in our compelling article: Stop using localStorage in JavaScript!.
Creative Development with Canvas and 3D
JavaScript is not just for forms and UI. The <canvas> element and libraries like Three.js open up a universe of possibilities for creative development. From rendering 2D graphics and complex animations to creating interactive 3D scenes, these tools allow web developers to also become digital artists. In this section, we will explore how to draw, animate, and bring your ideas to life directly in the browser.
2D Graphics with Canvas
The HTML5 <canvas> element provides a 2D drawing surface controlled by JavaScript. It's a blank slate where you can draw shapes, text, images, and create complex animations.
Drawing Random Points
A basic exercise to familiarize yourself with Canvas is drawing shapes in random positions. The 2D rendering context (getContext('2d')) gives us the tools.
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
const n = 100; // Número de puntos
const radius = 3;
for (let i = 0; i < n; i++) {
const x = Math.random() * canvas.width;
const y = Math.random() * canvas.height;
ctx.beginPath();
ctx.arc(x, y, radius, 0, 2 * Math.PI);
ctx.fillStyle = 'red';
ctx.fill();
}This simple script demonstrates how to get the context, use Math.random() for coordinates, and draw a filled circle (arc, fill).
See more at: Drawing random points with Canvas.
Creating a Wave Effect with Trigonometric Functions
Fluid and organic animations are often based on mathematical principles. We can use Math.sin() or Math.cos() along with requestAnimationFrame() to create a hypnotic wave effect.
const canvas = document.getElementById('waveCanvas');
const ctx = canvas.getContext('2d');
let step = 0;
function drawWave() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let x = 0; x < canvas.width; x++) {
// La función seno genera la ondulación
const y = canvas.height / 2 + Math.sin(x * 0.05 + step) * 20;
ctx.fillRect(x, y, 1, 1);
}
step += 0.1; // Avanza la animación en cada frame
requestAnimationFrame(drawWave);
}
drawWave();The key here is Math.sin(), which generates a smooth oscillation. By incrementing the step variable in each frame, the wave appears to move.
Explore the full code at: How to create a wave effect with Canvas and JavaScript?.
Creating a Luminous Point and Particles
Canvas allows for more complex visual effects, such as gradients and transparencies, to create illusions of light and atmospheres.
- Luminous Point: A sun or light source can be simulated using a radial gradient (createRadialGradient) that goes from a bright color to transparent.
- Particle System: An array of "Particle" objects can be created, each with its own position, velocity, and life cycle. In each animation frame, the position of each particle is updated and it is redrawn, creating effects like snow, stars, or fire.
The animation loop for a particle system typically follows this structure:
function animate() {
// Dibuja un fondo semi-transparente para crear un efecto de estela
ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Actualiza y dibuja cada partícula
particles.forEach(p => p.update());
requestAnimationFrame(animate);
}These experiments are the basis for understanding how 2D game engines and interactive data visualizations work.
Dive into these effects at: Luminous Point, Particles, and Rings of Circles.
First Steps with Three.js for 3D Graphics
While Canvas is excellent for 2D, Three.js is the de facto library for creating 3D graphics in the browser. It abstracts the complexity of WebGL into a much friendlier and more powerful API.
The Fundamental Elements of a Three.js Scene
Every 3D scene in Three.js is made up of three essential elements:
- The Scene (
THREE.Scene): It is the main container, the "universe" where all your objects, lights, and cameras live. - The Camera (
THREE.Camera): It is the viewpoint from which the scene will be rendered. The most common is thePerspectiveCamera, which simulates human eye vision. - The Renderer (
THREE.WebGLRenderer): It is the engine that takes the scene and the camera and draws the result on a<canvas>element.
Furthermore, for objects to be visible, you need at least two more things:
- A Geometry (
THREE.Geometry): Defines the shape of the object (e.g., a cube, a sphere). - A Material (
THREE.Material): Defines the appearance of the object's surface (e.g., color, texture, whether it is metallic).
These two combine into a Mesh, which is the object finally added to the scene.
Creating Our First Figure: A Cube
// 1. Escena
const scene = new THREE.Scene();
// 2. Cámara
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
// 3. Renderizador
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 4. Geometría y Material
const geometry = new THREE.BoxGeometry(1, 1, 1); // Un cubo de 1x1x1
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); // Verde
// 5. Mesh
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// 6. Bucle de Animación
function animate() {
requestAnimationFrame(animate);
// Animación: rotar el cubo
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();This code creates a simple scene with a green cube that rotates on itself. It is the "Hello, World" of Three.js and the starting point for much more ambitious projects, from product visualizations to complete video games in the browser.
Start your 3D journey with our tutorials: First steps with Three.js and Creating our first geometric figure in Three.js.
Modern Frameworks: Light Reactivity with Alpine.js
Not all web applications need the weight and complexity of frameworks like React or Vue. Sometimes, you just need to add a bit of interactivity to a server-rendered page. This is where Alpine.js shines, a robust yet minimalist framework that offers the reactivity and declarative nature of its larger siblings, but with a minimal learning curve and without the need for a compilation step.
Introduction to Alpine.js: What is it and How to Get Started?
Alpine.js calls itself "the Tailwind for JavaScript". Its philosophy is to allow you to build complex interfaces directly in your HTML using a set of directives (special attributes).
To get started, you only need to include a script on your page. There is no npm install, no Webpack, and no compilers.
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.min.js" defer></script>A basic Alpine component looks like this:
<div x-data="{ open: false }">
<button @click="open = !open">Toggle Content</button
<div x-show="open">
Contenido que se muestra y se oculta.
</div>
</div>Here, x-data defines the component's "state" (a JavaScript object). @click (a shorthand for x-on:click) handles events, and x-show shows or hides an element based on the state. It is declarative, readable, and lives directly in your HTML.
Start your journey with Alpine.js at: What is Alpine.js? How to install it and take the first steps.
Fundamental Alpine.js Directives
The magic of Alpine lies in its set of directives. These are the most important:
x-data: Declares a new component scope and defines its initial state.x-init: Executes a JavaScript expression when the component is initialized.x-show: Shows or hides an element (usingdisplay: none).x-if: Unlikex-show, this actually adds or removes the element from the DOM. It requires being on a<template>tag.x-bind: Binds an HTML attribute to the value of a JavaScript expression. Its shorthand is the character:(e.g.,:class).x-on: Attaches an event listener to an element. Its shorthand is the character@(e.g.,@click).x-model: Provides two-way data binding, mainly for form elements.x-text: Updates thetextContentof an element with the value of an expression.x-html: Updates theinnerHTMLof an element. Use with caution for security.x-for: Iterates over an array to render a list of elements. Also requires a<template>tag.x-ref: Allows obtaining a direct reference to a DOM element within the component, accessible via the magic property$refs.
Mastering these directives is mastering Alpine. Delve into them at: Alpine.js Core Directives.
Magical methods
At Alpine, we have some magic methods to perform different operations; they differ from attributes in that they are functions, functions that implement specific functionality of the framework; among the main ones we have:
- $watch() To watch for changes in variables and execute a process.
- $el() To obtain the reference to the element as if it were a JS selector.
- $refs() Get reference to any HTML element as if it were a JS selector.
- $store() Store data globally and accessible to any Alpine component on a page.
- $nextTick() This is a magic property that allows a block of JavaScript to be executed after Alpine has done its reactive DOM updates.
Events
For the use of events in Alpine, we can use the prefix of x-on or @; for example, for the click event:
- x-on:click
- @click
Now, we are going to know the events that can be used in Alpine.js; All these events allow you to invoke a function as it is the basic operation that you have from vanilla JavaScript:
- @click Click event on an element.
- @keyup Keyboard event.
- @change Event used to change the state or selection of the checkboxes, radios, select, among others.
Practical Examples with Key Directives
Data Binding with x-model and x-text
Two-way binding is a pillar of modern frameworks. With x-model, what the user types in an input updates a variable, and with x-text, we show that variable in real time.
<div x-data="{ message: 'Hola Alpine' }">
<input type="text" x-model="message">
<p>El mensaje es: <span x-text="message"></span></p>
</div>See more at: Text fields and variables: x-model and x-text.
Dynamic Attributes with x-bind
x-bind allows your HTML attributes to react to state changes. It is perfect for dynamic classes and styles.
<div x-data="{ isActive: true }">
<!-- El botón estará deshabilitado si isActive es false -->
<button :disabled="!isActive">Acción</button>
<!-- La clase 'active-class' se aplica si isActive es true -->
<div :class="{ 'active-class': isActive }">Contenido Dinámico</div>
</div>Explore more uses at: x-bind Directive in Alpine.js.
Conditional Rendering and Loops with x-if and x-for
To render content dynamically, x-if and x-for are essential. Both must be used on a <template> tag for Alpine to correctly handle DOM manipulation.
<div x-data="{ show: true, items: ['Manzana', 'Banana', 'Cereza'] }">
<!-- Renderizado condicional -->
<template x-if="show">
<p>Este párrafo se elimina del DOM si 'show' es false.</p>
</template>
<!-- Bucle -->
<ul>
<template x-for="item in items" :key="item">
<li x-text="item"></li>
</template>
</ul>
</div>Master loops and conditionals at: Conditionals x-if x-show and Cycles x-for.
Extending Alpine.js with Plugins
Although Alpine is minimalist, it is extensible. You can add functionality through plugins. Two of the most useful are Persist and integration with third-party libraries like SortableJS.
Persisting Data with @alpinejs/persist
This plugin makes it trivial to save the state of your components in localStorage. The state survives page reloads.
<script src=".../alpine.min.js" defer></script>
<script src="https://cdn.jsdelivr.net/npm/@alpinejs/persist@3.x.x/dist/cdn.min.js" defer></script>
<div x-data="{ count: $persist(0) }">
<button @click="count++">Incrementar</button>
<span x-text="count"></span>
</div>By simply wrapping the initial value in $persist(), the count variable will be automatically saved and retrieved from localStorage.
Learn how to use it at: Persist data on the client with Alpine.js.
Orderable Lists with SortableJS
Integrating third-party libraries is simple. SortableJS allows you to create lists with drag & drop. The key is to initialize the library in the x-init directive.
<script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script>
<div x-data="{ items: ['Item 1', 'Item 2', 'Item 3'] }">
<ul x-init="new Sortable($el, { animation: 150 })">
<template x-for="item in items">
<li x-text="item"></li>
</template>
</ul>
</div>Here, $el is an Alpine magic property that refers to the current element (the <ul>), on which we initialize Sortable.
Implement orderable lists with our guide: How to integrate SortableJS with Alpine.
Real Project: To-Do List Application
The best way to consolidate knowledge is to build something. A To-Do List application combines all the directives and concepts we have seen: state management with x-data, iteration with x-for, input capture with x-model, events with x-on, and persistence with $persist.
The state might look like this:
{ task: '', tasks: $persist([]) } Functions to add, delete, and mark tasks as completed would be defined within x-data. The result is a fully functional, reactive, and persistent application with surprisingly concise and readable code.
Build your own To-Do List step by step with our tutorial: To Do List Application with Alpine JS.
Integration with Other Technologies
JavaScript rarely lives on an island. Its true power is manifested when it integrates with other technologies, whether consuming APIs from third-party services like YouTube or SoundCloud, working in conjunction with backend frameworks like Laravel, or enhancing the functionality of rich text editors like CKEditor. This section explores how JavaScript acts as the glue that binds different parts of a modern web application.
Consuming Third-Party APIs: YouTube and SoundCloud
Integrating media players from external platforms is a common task. Instead of simply embedding a static iframe, the JavaScript APIs of these services give us total control over playback.
YouTube IFrame Player API
This API allows you to create, control, and customize YouTube players programmatically. You can play videos, pause them, listen to events (like when a video ends), and build dynamic playlists.
The first step is to load the API and define a global function that will be called when it is ready. Then, a YT.Player object is created.
// Esta función es llamada por la API de YouTube
function onYouTubeIframeAPIReady() {
const player = new YT.Player('player', { // 'player' es el ID del div contenedor
height: '360',
width: '640',
videoId: 'M7lc1qf-DEQ', // ID del video de YouTube
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange
}
});
}
function onPlayerReady(event) {
event.target.playVideo(); // Reproducir automáticamente
}
function onPlayerStateChange(event) {
if (event.data == YT.PlayerState.ENDED) {
console.log("El video ha terminado.");
// Aquí podrías cargar el siguiente video de una playlist
}
}Master the YouTube API with our guide: How to use the YouTube IFrame Player API.
SoundCloud Widget API
Similarly, the SoundCloud API allows you to integrate widgets and control them. You can load tracks, playlists, and listen to events like PLAY, PAUSE, and FINISH.
const iframe = document.querySelector('.iframe');
const widget = SC.Widget(iframe);
widget.bind(SC.Widget.Events.FINISH, function() {
console.log("La canción ha terminado. Reproduciendo la siguiente...");
widget.next();
});
document.getElementById('playBtn').addEventListener('click', () => widget.play());Native JavaScript within an SPA Framework like Vue.js
Sometimes, even within a modern framework like Vue, the simplest and most robust solution is to use native JavaScript. Frameworks are excellent for handling state and reactivity, but for direct DOM manipulation (especially with dynamically loaded content like HTML), "old-school" JavaScript is still king.
A common use case is creating internal navigation (scroll spy) for a long document that has been loaded as an HTML string. Vue does not have a direct reference to the headers (h2, h3) within that string, but native JavaScript does.
// Inside a Vue component
import { onMounted } from 'vue';
onMounted(() => {
// El contenido HTML ya ha sido renderizado por Vue con v-html
const contentDiv = document.querySelector('.book-chapter');
const headers = contentDiv.querySelectorAll('h2, h3');
// Ahora podemos crear un índice dinámico o un scroll spy
headers.forEach(header => {
header.addEventListener('click', () => {
// Lógica de JavaScript nativo
console.log(`Has hecho clic en: ${header.textContent}`);
});
});
});Knowing when to "escape" the framework to use native tools is a key skill of an experienced developer. Don't be afraid to mix approaches if the result is simpler and more maintainable code.
Explore this concept at: Including native JavaScript in Vue.
Enhancing CKEditor with JavaScript
CKEditor is a powerful rich text editor, but we can extend its functionality with JavaScript to adapt it to our needs.
Create Custom Keyboard Shortcuts
Instead of clicking the toolbar buttons repeatedly, we can define keyboard shortcuts to apply formats. The key is to listen for the keydown event and, when our key combination is detected, use the CKEditor command API.
ClassicEditor.create(document.querySelector('#editor'))
.then(editor => {
window.addEventListener('keydown', (event) => {
// Ejemplo: aplicar formato de código con F2
if (event.key === 'F2') {
event.preventDefault();
editor.execute('code');
}
});
})
.catch(error => console.error(error));See the implementation at: CKEditor and creating Custom Keyboard Events.
Create a Document Outline
A premium feature of CKEditor is the "Document Outline" or side navigation index. We can replicate this functionality with native JavaScript. The strategy is to get the editor content, parse the headers (H1, H2, etc.), and build a list of links that point to the IDs of those headers.
function generateIndex(editorContent) {
const parser = new DOMParser();
const doc = parser.parseFromString(editorContent, 'text/html');
const headers = doc.querySelectorAll('h1, h2, h3');
const indexList = document.getElementById('index-container');
indexList.innerHTML = ''; // Limpiar índice anterior
headers.forEach(header => {
// Asegurarse de que el header tiene un ID
if (!header.id) header.id = 'header-' + Math.random().toString(36).substr(2, 9);
const li = document.createElement('li');
const a = document.createElement('a');
a.href = '#' + header.id;
a.textContent = header.textContent;
li.appendChild(a);
indexList.appendChild(li);
});
}Build your own document outline with our guide: CKEditor Document Outline/Document Scheme FREE and How to create an automated Index.
Troubleshooting and Environment
Even the most experienced developers encounter environment, permission, and configuration issues that can stop a project. Solving these problems is a crucial part of the daily development life.
Troubleshooting Common Node.js/NPM Errors on macOS
A classic error when working with Node.js on macOS (and other Unix systems) is the EACCES permission error when trying to install a package globally with npm install -g <package_name>.
npm ERR! Error: EACCES: permission deniedThis happens because, by default, npm tries to install global packages in a system directory (like /usr/local/lib/node_modules) that requires administrator (root) permissions.
The incorrect and dangerous solution is to use sudo npm install -g. This can cause more permission problems in the future and is a bad security practice.
The Correct Solution: Change the Ownership of NPM Directories
The solution recommended by npm's own documentation is to change the owner of the directories npm uses to your current user. This way, you will never need sudo to install global packages.
You can do it with the following commands:
# Change ownership of the npm cache directory
sudo chown -R whoami ~/.npm
# Change ownership of the global packages directory
sudo chown -R whoami /usr/local/lib/node_modulesThe whoami command is automatically replaced by your username. After running these commands, you will be able to install, update, and uninstall global packages without using sudo and without encountering permission errors.
Another common error, especially in React Native development, is Address already in use. This usually means that the Metro Bundler server (or some other process) is already running on the port you are trying to use. The solution is usually to find and stop the existing process:
# Find the process using a specific port (e.g., 8081)
lsof -i :8081
# Stop the process using its PID
kill -9 <PID>Learn more about how to solve these frustrating problems at: reactive run-android failed "could not install *smartsocket* listener..." in Node MacOS.
Conclusion: The Never-Ending JavaScript Journey
We have traveled through the vast and multifaceted JavaScript ecosystem. From the fundamentals of DOM manipulation and error handling, through the incredible ability to interact with device hardware via browser APIs, to diving into creative development with Canvas and the 3D power of Three.js. We have seen how light frameworks like Alpine.js can bring reactivity without complexity, and how JavaScript acts as the universal connector, integrating with all kinds of backend and third-party technologies.
This journey demonstrates that JavaScript is much more than a language: it is a complete platform for building almost anything imaginable in the digital world. However, the developer's journey never ends. APIs evolve, new frameworks emerge, and best practices change. The key to success is constant curiosity and the desire to keep learning and building.
We hope this master guide serves not only as a reference but as a source of inspiration for your next projects. Use the links, explore the code, and, above all, don't stop experimenting. The JavaScript universe is yours to explore.