Passphrase Generator with JavaScript by Cristian Villafane

Passphrase Generator with JavaScript by Cristian Villafane

Passphrase Generator with JavaScript by Cristian Villafane

Evolving from passwords to passphrases: A guide to building a stronger, more memorable credential generator with JavaScript.

In the quest for better online security, we’re moving beyond complex, hard-to-remember passwords. Enter the passphrase: a sequence of random words that is both significantly harder for computers to crack and surprisingly easy for humans to remember. A passphrase like Correct-Horse-Battery-Staple is far more secure and memorable than P@ssw0rd1!. This project will guide you through building your own passphrase generator, leveling up your skills in HTML, CSS, and modern JavaScript logic, for free! only on JavaScript by Cristian Villafane

Phase 1: The Blueprint – Structuring the HTML

The foundation of our tool is the HTML structure. We need to provide users with clear, intuitive controls. Instead of character types, we’ll focus on the components of a passphrase: the number of words, separators, and optional additions like capitalization and numbers.

Crafting the User Controls

Our interface will have a main container for all the settings. The key elements are:

  • Word Count: An <input type="range"> slider is perfect for allowing the user to choose the number of words in their passphrase.
  • Customization Options: We’ll use <input type="checkbox"> for enabling capitalization of each word and for adding a number to the end.
  • Separators: A group of <button> elements will let the user select the character that joins the words, such as a hyphen, dot, or space.
<!-- Word Count Slider -->
<div class="option">
  <label for="word-count-slider">Words: <span id="word-count-value">4</span></label>
  <input type="range" id="word-count-slider" min="3" max="8" value="4">
</div>

<!-- Separator Control -->
<div class="option">
    <span>Separator</span>
    <div class="separator-buttons">
        <button class="separator-btn active" data-separator="-">-</button>
        <button class="separator-btn" data-separator=".">.</button>
    </div>
</div>

Phase 2: The Core Logic – Generating Passphrases with JavaScript

This is where our application comes to life. The JavaScript logic will handle fetching a list of words, randomly selecting from it based on user settings, and assembling the final passphrase.

Building the Generation Engine

The process starts with a word list. For this tutorial, we’ll embed a curated array of words directly in our script. Our main function, generatePassphrase(), will read the user’s choices: number of words, capitalization, number inclusion, and the selected separator. It will then loop, picking a random word from our list for each iteration, and apply any requested transformations.

The Selection Loop

With our word list and user options defined, a simple for loop is used to build an array of random words. For each word, we check if capitalization is enabled. After the loop, these words are joined together using the chosen separator, and a final random number might be appended. This method produces a highly random and customizable result.

const words = [];
for (let i = 0; i < wordCount; i++) {
  let randomWord = wordList[Math.floor(Math.random() * wordList.length)];
  if (useCapitals) {
    randomWord = randomWord.charAt(0).toUpperCase() + randomWord.slice(1);
  }
  words.push(randomWord);
}
let passphrase = words.join(separator);

Phase 3: Making It Interactive

A great tool responds instantly. We’ll attach event listeners to all our controls. Any change—sliding the word count, ticking a box, or choosing a new separator—will immediately trigger the generatePassphrase() function, providing a fluid and seamless user experience. The same principles of asynchronous font loading and efficient DOM manipulation apply here to keep the tool fast and responsive.

Passphrase Generator in Action

Here is the final, functional passphrase generator. Experiment with the settings to see how different options create unique and strong passphrases. This is the result of combining our structured HTML with dynamic JavaScript.

Your-Passphrase-Here
Passphrase Strength:
Time to crack:

The Complete Generator Code

For easy implementation, here is the complete, self-contained code for the passphrase generator. You can copy and paste this directly into an HTML file to see it in action.

Building a passphrase generator is a fantastic way to engage with modern security concepts while honing your JavaScript skills. I hope this Javascript by Cristian Villafane tutorial has been insightful. Use this project as a launchpad to explore further enhancements. The journey of learning is limitless! 🔐

Keep coding, Cristian Villafane

Password Generator with JavaScript by Cristian Villafane

Password Generator with JavaScript by Cristian Villafane

Password Generator: A Javascript Tutorial by Cristian Villafane

A practical guide to creating an essential security tool, mastering random generation logic and DOM manipulation.

In today’s digital world, password security is more crucial than ever. Creating strong, unique passwords for each service is a cornerstone of good security practice. And what better way to understand how it’s done than by building your own generator? This project is an excellent exercise to solidify your skills in HTML, CSS, and most importantly, JavaScript. In this guide on Javascript by Cristian Villafane, we will walk you through creating a functional and aesthetically pleasing tool, step by step.

Phase 1: The Structure – HTML for the Options

Every web application needs a skeleton. For our generator, the HTML structure will define all the options the user can control. We’ll need a place to display the generated password, a slider for length, and checkboxes for character types.

Designing the User Interface

We start with a main container. Inside, the most important element is the password display area. A <div> or <span> is perfect for this. Next to it, a “copy” button is essential for usability. Then, we create a section for the options:

  • Password Length: An <input type="range"> is the most intuitive choice. We’ll give it an id, and min and max attributes to define the length range.
  • Character Types: We’ll use <input type="checkbox"> to allow the user to include or exclude uppercase letters, numbers, and special symbols. Each checkbox will be associated with a <label> for better accessibility.

Phase 2: The Logic – Real-Time Generation with JavaScript

This is where the magic happens. Our JavaScript script will be responsible for taking the user’s options, building a set of allowed characters, and then randomly selecting from that set to form the password in real-time.

Gathering Inputs and Defining Character Sets

First, we define the possible character sets as strings: lowercase letters (which we’ll include by default), uppercase, numbers, and symbols. Then, we create a main function, let’s say generatePassword(). Inside this function, the first thing is to get the current values from our HTML elements: the length from the slider and the checked state of each checkbox.

Based on the checked boxes, we build a master string called characterPool that contains all allowed characters. If the “Include Numbers” box is checked, we add the numbers string to our characterPool. We repeat this for uppercase and symbols. This dynamic approach is key in Javascript by Cristian Villafane for creating flexible tools.

The Generation Loop

With our characterPool ready and the desired length known, the next step is a for loop that will run as many times as the password length. In each iteration, we generate a random index corresponding to a position within the characterPool string. We use Math.random() to get a decimal between 0 and 1, multiply it by the pool’s length, and use Math.floor() to get an integer index. The character at that position is then appended to our password variable.

An important refinement is to ensure the final password contains at least one character of each selected type to guarantee its strength. This can be achieved by pre-loading the password with one random character from each required set and then filling the rest.

Phase 3: Interactivity and Performance Optimization

A functional tool is good, but a fast and intuitive one is better. We’ll connect our logic to user events and ensure the experience is smooth by addressing common performance issues like render-blocking requests and layout shifts.

Event Listeners and Asynchronous Loading

We add event listeners to the slider and checkboxes. Whenever their value changes, our password generation logic is triggered, creating a real-time experience. A new randomize button also allows for generating a new password with the same settings. For performance, external resources like fonts are loaded asynchronously. This prevents them from blocking the initial page render, leading to a faster perceived load time for the user.

Password Generator in Action

Below, you can try out the optimized password generator. Play with the options and see the functionality for yourself. This is the final result of applying the concepts from this tutorial.

Generate your password
Password Strength:
Time to crack:

The Complete Generator Code

Here is the self-contained, performance-optimized code for the password generator component. You can copy and paste this into your own project.

I hope you found this tutorial on Javascript by Cristian Villafane useful. Building a password generator not only reinforces your knowledge of JavaScript, but also introduces you to fundamental security concepts. Use this project as a foundation to explore and add new features. The only limit is your curiosity! 🔐

Happy coding, Cristian Villafane

Currency Converter with JavaScript by Cristian Villafane

Currency Converter with JavaScript by Cristian Villafane

Currency Converter: A Javascript Tutorial by Cristian Villafane

A practical guide to building a financial tool by fetching real-time data from the FxRatesAPI.

In our interconnected world, knowing the real-time value of different currencies is essential for travel, business, and investment. A currency converter is a perfect project to practice asynchronous JavaScript and API interaction. This guide on Javascript by Cristian Villafane will walk you through creating a sleek, functional currency converter using the free and simple FxRatesAPI. We’ll focus on fetching data, updating the DOM dynamically, and providing a clean user experience.

Phase 1: The Structure – HTML for the Converter

The foundation of our application is the HTML structure. We need input fields for the user to enter the amount and select the currencies they wish to convert between. A clear and intuitive layout is key.

Designing the User Interface

We’ll use a main container for our tool. Inside, we’ll have a prominent display area for the result. The core of the interface will be the options section:

  • Amount: An <input type="number"> allows the user to enter the value they want to convert.
  • From & To Currencies: Two <select> dropdowns will hold the list of available currencies. We’ll populate these dynamically using JavaScript.
  • Swap Button: A simple button to quickly swap the “From” and “To” currencies is a great usability feature.

Phase 2: The Logic – Real-Time Conversion with JavaScript

This is where our application comes to life. The JavaScript will fetch the list of currencies, populate our dropdowns, and perform the conversion calculation whenever the user changes an input.

Fetching Data with the `fetch` API

On page load, we make a single call to the FxRatesAPI endpoint https://api.fxratesapi.com/latest. This returns all available exchange rates relative to a base currency (USD). We store these rates and use their keys to populate our currency selection dropdowns. This single-fetch approach is highly efficient.

Client-Side Conversion

Once we have all the rates, we don’t need to call the API again for every conversion. The logic is handled directly in the browser. To convert from currency A to currency B, we use the stored rates and a simple formula: (amount / rate of A) * rate of B. This makes the converter feel instantaneous. This approach is central in Javascript by Cristian Villafane for building fast, data-driven applications.

Currency Converter in Action

Below, you can try out the live currency converter. Enter an amount, select your currencies, and see the real-time result. This is the final product of applying the concepts from this tutorial.

Loading rates…

The Complete Converter Code

Here is the self-contained, performance-optimized code for the currency converter component. You can copy and paste this into your own project.

I hope you found this tutorial on Javascript by Cristian Villafane useful. Building a currency converter is an excellent way to practice working with external APIs and handling asynchronous operations in JavaScript. Use this project as a foundation to explore and add new features, such as historical data charts or a portfolio tracker. The only limit is your curiosity! 💹

Happy coding, Cristian Villafane

Web Calculator with JavaScript by Cristian Villafane

Web Calculator with JavaScript by Cristian Villafane

Web Calculator: A Tutorial on Javascript by Cristian Villafane

A complete, step-by-step guide to mastering DOM manipulation and event logic in your first major interactive Javascript project.

Every developer remembers that first project that made them feel like they were “making magic” in the browser. For many, that project was a simple calculator. Far from being just an academic exercise, building a calculator from scratch is a rite of passage that solidifies our understanding of the pillars of web development: structure with HTML, style with CSS, and most importantly, interactivity and logic. In this tutorial on Javascript by Cristian Villafane, we will get ready to put your skills into practice and create something tangible.

Phase 1: The Foundation – HTML & Javascript User Input

Before we can even think about performing calculations, we need to build the user interface and ensure our code can effectively listen for and interpret user interactions. We’ll start with the Document Object Model (DOM) manipulation, which is central to how Javascript interacts with a web page.

Crafting the Interface with HTML

The first step is to create the calculator’s body using HTML. This typically involves a container div, a div to act as our display screen (often an <input> element with the readonly attribute is a good choice here to prevent keyboard input), and a series of <button> elements for the numbers (0-9), the decimal point, the core arithmetic operators (+, -, *, /), a ‘clear’ button (C or AC), and the all-important ‘equals’ button (=). Each of these buttons should be given appropriate id or class attributes, and for the numbers and operators, data-* attributes can be incredibly useful for storing their respective values.

<!-- Example button for the number 7 -->
<button data-number="7">7</button>

<!-- Example button for the addition operator -->
<button data-operator="+">+</button>

This semantic approach makes our Javascript code cleaner and more maintainable.

Listening for Clicks with JavaScript

With the HTML structure in place, we pivot to Javascript. Our primary goal is to attach event listeners to our buttons. We’ll use the DOMContentLoaded event to ensure our script only runs after the entire HTML document has been loaded and parsed. Inside this event listener, we’ll select all our buttons. A common and efficient way to do this is using document.querySelectorAll() to grab all elements with a specific class, say .calculator-button. We then iterate over this collection of buttons, often with a forEach loop, and attach a click event listener to each one.

Inside the callback function for this event listener, we have access to the event object. The event.target property is our golden ticket, as it gives us a reference to the specific button that was clicked. From this target, we can then access the dataset property to retrieve the values we stored in our data-* attributes. The logic then becomes a series of conditional statements. This is a core concept in web development, and this guide to Javascript by Cristian Villafane aims to make it clear.

Phase 2: State Management & Calculation Logic in Javascript

So, we have user input flowing in. The critical next step is to create a state machine, albeit a simple one. In Javascript, this “state” is just a set of variables that hold the crucial pieces of information at any given moment. We need to know the first number in our equation (firstOperand), the mathematical operator the user has selected, and the second number (secondOperand). We also need a way to know if the display should be cleared before the next number is entered, for instance, right after an operator has been pressed. Let’s call this a shouldResetDisplay flag. These variables are the memory of our calculator.

Handling Operations and the Equals Button

When a user clicks an operator button (+, -, *, /), our logic needs to kick in. First, we check if a firstOperand and an operator have already been set. If they have, it means the user is chaining operations (e.g., 5 + 10 -). In this scenario, we should perform the pending calculation first before storing the new operator. If not, we capture the current value shown on the display and store it in our firstOperand variable. It’s important to convert this value from a string, which is what we get from the display, into a number using parseFloat() to handle decimals.

The ‘equals’ button is the grand finale of our logic loop. When clicked, it triggers the final calculation. We’ll grab the current number on the display as our secondOperand. Now, with firstOperand, operator, and secondOperand all in hand, we pass them to a dedicated calculate() function. A switch statement is a perfect tool here. It will check the value of the operator variable and execute the corresponding arithmetic. A crucial edge case to handle here is division by zero. If the operator is ‘/’ and secondOperand is 0, we should return an error message like “Error” instead of Infinity, which is Javascript‘s default. After the calculation, we must reset our state. The result becomes the new firstOperand for potential subsequent calculations.

The Reset Switch

The ‘Clear’ button (‘C’ or ‘AC’) has a simple but vital job: it’s our reset switch. A click on ‘clear’ should call a resetCalculator() function. This function will restore all our state variables to their initial values and clear the content of our display. This brings the calculator back to its starting state, ready for a fresh calculation.

Phase 3: Polishing the Experience (UX/UI)

So far, our calculator works, but it’s rigid. To elevate this project, our first order of business is implementing keyboard support. Users instinctively expect to be able to use their physical keyboard for an application like this.

  • Keyboard Support: We can achieve this by adding a single keydown event listener to the global window object. Inside this listener’s callback function, the event.key property tells us exactly which key was pressed. We can then map the event.key value to our calculator’s functions, for example, mapping the ‘Enter’ key to the equals button.
  • Input Validation: A simple calculator can easily break if we don’t handle edge cases. What happens if a user tries to type 5..5? It’s invalid input. Inside our number-handling logic, before appending a decimal point, we must first check if the current display string already includes(‘.’). If it does, we simply return and do nothing.
  • Visual Feedback: Visual feedback is another cornerstone of good UX. When a button is clicked, we can add a CSS class, like .active. Then, using setTimeout(), we remove that class after a short delay. This creates a quick flash that gives the user an unambiguous confirmation that their input was registered.
  • Backspace Function: Forcing the user to hit ‘Clear’ for a single typo is poor design. We can add a ‘DEL’ button. Its logic is straightforward: when clicked, it should take the current string on the display and use the slice(0, -1) method. This returns a new string with the last character removed.

Final Code: Putting It All Together

Here is the complete, unified code from this tutorial on Javascript by Cristian Villafane for you to copy, paste, and watch the magic happen in your own browser.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Calculator</title>
    <style>
        body { display: flex; justify-content: center; align-items: center; height: 100vh; background: #f0f0f0; }
        .calculator { border-radius: 10px; box-shadow: 0 0 25px rgba(0,0,0,0.15); overflow: hidden; max-width: 400px; }
        .calculator-display { background-color: #222; color: #fff; font-size: 2.5rem; padding: 20px; text-align: right; }
        .calculator-keys { display: grid; grid-template-columns: repeat(4, 1fr); grid-gap: 1px; }
        button { font-size: 1.5rem; border: none; padding: 25px; background: #e0e0e0; cursor: pointer; transition: background .2s; }
        button:hover { background: #d5d5d5; }
        button:active { box-shadow: inset 0px 0px 5px #c1c1c1; }
        .key--operator { background: #f5a623; color: white; }
        .key--operator:hover { background: #e09414; }
        .key--equal { background: #4a90e2; color: white; grid-column: -2; grid-row: 2 / span 4; }
        .key--equal:hover { background: #3a80d2; }
    </style>
</head>
<body>
    <div class="calculator">
        <div class="calculator-display">0</div>
        <div class="calculator-keys">
            <button class="key--operator" data-action="add">+</button>
            <button class="key--operator" data-action="subtract">-</button>
            <button class="key--operator" data-action="multiply">&times;</button>
            <button class="key--operator" data-action="divide">÷</button>
            <button>7</button>
            <button>8</button>
            <button>9</button>
            <button>4</button>
            <button>5</button>
            <button>6</button>
            <button>1</button>
            <button>2</button>
            <button>3</button>
            <button>0</button>
            <button data-action="decimal">.</button>
            <button data-action="clear">AC</button>
            <button class="key--equal" data-action="calculate">=</button>
        </div>
    </div>
    <script>
        document.addEventListener('DOMContentLoaded', () => {
            const calculator = document.querySelector('.calculator');
            const keys = calculator.querySelector('.calculator-keys');
            const display = calculator.querySelector('.calculator-display');

            keys.addEventListener('click', e => {
                if (!e.target.matches('button')) return;

                const key = e.target;
                const action = key.dataset.action;
                const keyContent = key.textContent;
                const displayedNum = display.textContent;
                const previousKeyType = calculator.dataset.previousKeyType;

                if (!action) {
                    if (displayedNum === '0' || previousKeyType === 'operator' || previousKeyType === 'calculate') {
                        display.textContent = keyContent;
                    } else {
                        display.textContent = displayedNum + keyContent;
                    }
                    calculator.dataset.previousKeyType = 'number';
                }

                if (action === 'decimal') {
                    if (!displayedNum.includes('.')) {
                        display.textContent = displayedNum + '.';
                    } else if (previousKeyType === 'operator' || previousKeyType === 'calculate') {
                        display.textContent = '0.';
                    }
                    calculator.dataset.previousKeyType = 'decimal';
                }

                if (action === 'add' || action === 'subtract' || action === 'multiply' || action === 'divide') {
                    const firstValue = calculator.dataset.firstValue;
                    const operator = calculator.dataset.operator;
                    const secondValue = displayedNum;

                    if (firstValue && operator && previousKeyType !== 'operator' && previousKeyType !== 'calculate') {
                        const calcValue = calculate(firstValue, operator, secondValue);
                        display.textContent = calcValue;
                        calculator.dataset.firstValue = calcValue;
                    } else {
                        calculator.dataset.firstValue = displayedNum;
                    }

                    key.classList.add('is-depressed');
                    calculator.dataset.previousKeyType = 'operator';
                    calculator.dataset.operator = action;
                }
                
                Array.from(key.parentNode.children).forEach(k => k.classList.remove('is-depressed'));

                if (action === 'clear') {
                    display.textContent = '0';
                    delete calculator.dataset.firstValue;
                    delete calculator.dataset.operator;
                    calculator.dataset.previousKeyType = 'clear';
                }

                if (action === 'calculate') {
                    let firstValue = calculator.dataset.firstValue;
                    const operator = calculator.dataset.operator;
                    let secondValue = displayedNum;

                    if (firstValue) {
                        if (previousKeyType === 'calculate') {
                            firstValue = displayedNum;
                            secondValue = calculator.dataset.modValue;
                        }
                        display.textContent = calculate(firstValue, operator, secondValue);
                    }
                    
                    calculator.dataset.modValue = secondValue;
                    calculator.dataset.previousKeyType = 'calculate';
                }
            });

            const calculate = (n1, operator, n2) => {
                const firstNum = parseFloat(n1);
                const secondNum = parseFloat(n2);
                if (operator === 'add') return firstNum + secondNum;
                if (operator === 'subtract') return firstNum - secondNum;
                if (operator === 'multiply') return firstNum * secondNum;
                if (operator === 'divide') return firstNum / secondNum;
            };
        });
    </script>
</body>
</html>

I hope you found this tutorial on Javascript by Cristian Villafane useful. The concepts you’ve applied here, DOM manipulation, event handling, and state management, are the bedrock of modern web development. Use this project as a stepping stone. Keep experimenting, keep building, and never stop learning. The web is your canvas. 🚀

Happy coding, Cristian Villafane

Dictionary App with JavaScript by Cristian Villafane

Dictionary App with JavaScript by Cristian Villafane

Dictionary App: A Javascript Tutorial by Cristian Villafane

A practical guide to fetching data from a free dictionary API, handling complex JSON, and building a dynamic, real-world application.

Creating a dictionary app is a step above a simple weather app, presenting a fantastic challenge for handling more complex data structures. It’s a perfect project for mastering HTML for structure, CSS for styling, and the asynchronous capabilities of JavaScript. In this guide on Javascript by Cristian Villafane, we will build a functional and modern dictionary app with a crucial autocomplete feature.

Phase 1: The Structure – HTML for the Interface

Every web application starts with a solid HTML structure. For our dictionary app, we need an input field for the word, a search button, a container for autocomplete suggestions, and a display area for the results, which will include the word, phonetics, audio, definitions, and more.

Designing the User Interface

We’ll create a main container with a search area and a display card. The UI will consist of:

  • Word Input: An <input type="text"> that allows the user to type a word. It will trigger a search for suggestions as the user types.
  • Search Button: A <button> integrated into the input field for a clean look.
  • Autocomplete List: A <div> that will be dynamically populated with word suggestions.
  • Display Card: A dedicated <div> to hold all the dictionary information.
<!-- Search Container -->
<div class="search-container">
  <input type="text" id="word-input" placeholder="Enter a word...">
  <button id="search-btn">
    <!-- SVG Search Icon -->
  </button>
  <div class="autocomplete-suggestions" id="autocomplete-list"></div>
</div>

<!-- Dictionary Display -->
<div class="dictionary-display" id="dictionary-display">
  <!-- Content injected by JS -->
</div>

Phase 2: The Logic – Two APIs for a Better Experience

This is where our application comes to life. To provide a great user experience with autocomplete, we will use two different APIs: one for word suggestions and another for the full dictionary data.

  • Autocomplete: We’ll use the Datamuse API (api.datamuse.com) for fast word suggestions as the user types. It’s free and requires no API key.
  • Definitions: Once a word is selected, we’ll use the Free Dictionary API (dictionaryapi.dev) to fetch detailed information.

Understanding the Dictionary API Response (JSON)

When we request a word from dictionaryapi.dev, it sends back a complex JSON object, often an array. This structure is more nested than a typical weather API response. A simplified version for the word “hello” looks like this:

[
  {
    "word": "hello",
    "phonetic": "/həˈloʊ/",
    "phonetics": [
      {
        "text": "/həˈloʊ/",
        "audio": "https://api.dictionaryapi.dev/media/pronunciations/en/hello-us.mp3"
      }
    ],
    "meanings": [
      {
        "partOfSpeech": "exclamation",
        "definitions": [
          {
            "definition": "Used as a greeting or to begin a phone conversation.",
            "example": "hello there, Katie!"
          }
        ],
        "synonyms": ["hi", "greetings"],
        "antonyms": ["goodbye"]
      }
    ]
  }
]

Notice the nested arrays for phonetics and meanings. To get the audio URL, we would access data[0].phonetics[0].audio. To get the first definition, we would use data[0].meanings[0].definitions[0].definition. Our JavaScript needs to navigate this structure carefully.

Phase 3: Interactivity and Displaying the Data

A great app feels responsive and fast. We’ll connect our logic to user events and ensure the experience is smooth.

Event Listeners and User Experience

We have multiple event listeners: one for the input event on the text field for autocomplete, another for the click event on the search button, and a listener for the `Enter` key. We also handle clicks on the suggestion items. A default word is loaded on page open to provide an immediate example.

Dictionary App in Action

Below, you can try out the live dictionary app. Enter a word and see how the autocomplete suggests options. Select one to view its full definition, pronunciation, and more. This is the final result of applying the concepts from this tutorial.

The Complete Dictionary App Code

Here is the self-contained, performance-optimized code for the dictionary app. You can copy and paste this into your own project. It uses free APIs that don’t require keys.

<!-- HTML Structure -->
<div class="dictionary-container">
    <div class="search-container">
        <input type="text" id="word-input" placeholder="Enter a word..." aria-label="Word">
        <button id="search-btn" aria-label="Search">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>
        </button>
        <div class="autocomplete-suggestions" id="autocomplete-list"></div>
    </div>
    <div id="dictionary-display" class="dictionary-display">
        <!-- Dictionary data will be dynamically inserted here -->
    </div>
</div>

<!-- JavaScript Logic -->
<script>
document.addEventListener('DOMContentLoaded', () => {
    const container = document.querySelector('.dictionary-container:not(:has(#word-input-live))');
    if (!container) return;

    const wordInput = container.querySelector('#word-input');
    const searchBtn = container.querySelector('#search-btn');
    const dictionaryDisplay = container.querySelector('#dictionary-display');
    const autocompleteList = container.querySelector('#autocomplete-list');

    const fetchWordData = async (word) => {
        dictionaryDisplay.innerHTML = '<p style="text-align:center; padding: 2rem;">Loading...</p>';
        const apiUrl = `https://api.dictionaryapi.dev/api/v2/entries/en/${word}`;

        try {
            const response = await fetch(apiUrl);
            if (!response.ok) {
                const errorData = await response.json();
                throw new Error(errorData.title || 'Word not found. Please try again.');
            }
            const data = await response.json();
            displayWordData(data, dictionaryDisplay);
        } catch (error) {
            displayError(error.message, dictionaryDisplay);
        }
    };

    const fetchAutocomplete = async (query, listElement) => {
        if (query.length < 2) {
            listElement.innerHTML = '';
            listElement.style.display = 'none';
            return;
        }
        const apiUrl = `https://api.datamuse.com/sug?s=${query}`;
        try {
            const response = await fetch(apiUrl);
            if (!response.ok) return;
            const suggestions = await response.json();
            displayAutocomplete(suggestions, listElement);
        } catch (error) {
            console.error('Autocomplete error:', error);
            listElement.style.display = 'none';
        }
    };
    
    const displayWordData = (data, displayElement) => {
        const entry = data[0];
        const phoneticInfo = entry.phonetics.find(p => p.audio) || entry.phonetics[0];
        
        let meaningsHtml = entry.meanings.map(meaning => `
            <div class="meaning-block">
                <h3 class="part-of-speech">${meaning.partOfSpeech}</h3>
                <ol class="definition-list">
                    ${meaning.definitions.map(def => `
                        <li class="definition-item">
                            ${def.definition}
                            ${def.example ? `<p class="definition-example">e.g., "${def.example}"</p>` : ''}
                        </li>
                    `).join('')}
                </ol>
                ${meaning.synonyms && meaning.synonyms.length > 0 ? `
                    <div class="synonyms-antonyms">
                        <h4>Synonyms:</h4>
                        ${meaning.synonyms.map(s => `<span>${s}</span>`).join('')}
                    </div>
                ` : ''}
                ${meaning.antonyms && meaning.antonyms.length > 0 ? `
                    <div class="synonyms-antonyms">
                        <h4>Antonyms:</h4>
                        ${meaning.antonyms.map(a => `<span>${a}</span>`).join('')}
                    </div>
                ` : ''}
            </div>
        `).join('');

        displayElement.innerHTML = `
            <div class="word-header">
                <div>
                    <h2 class="word-title">${entry.word}</h2>
                    <p class="phonetic">${phoneticInfo ? phoneticInfo.text : 'N/A'}</p>
                </div>
                ${phoneticInfo && phoneticInfo.audio ? `
                    <button class="audio-btn" onclick="this.querySelector('audio').play()" aria-label="Play audio">
                        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg>
                        <audio src="${phoneticInfo.audio}"></audio>
                    </button>
                ` : ''}
            </div>
            ${meaningsHtml}
        `;
    };

    const displayAutocomplete = (suggestions, listElement) => {
        if (!suggestions || suggestions.length === 0) {
            listElement.style.display = 'none';
            return;
        }
        listElement.innerHTML = suggestions.map(s => 
            `<div class="suggestion-item" data-word="${s.word}">${s.word}</div>`
        ).join('');
        listElement.style.display = 'block';
    };

    const displayError = (message, displayElement) => {
        displayElement.innerHTML = `<p id="error-message">Error: ${message}</p>`;
    };
    
    const performSearch = (word) => {
        if (word) {
            fetchWordData(word);
            wordInput.value = word;
            autocompleteList.style.display = 'none';
        }
    };

    searchBtn.addEventListener('click', () => performSearch(wordInput.value));
    wordInput.addEventListener('keydown', (e) => {
        if (e.key === 'Enter') performSearch(wordInput.value);
    });
    wordInput.addEventListener('input', () => fetchAutocomplete(wordInput.value, autocompleteList));

    autocompleteList.addEventListener('click', (e) => {
        if (e.target.classList.contains('suggestion-item')) {
            const word = e.target.dataset.word;
            performSearch(word);
        }
    });
    
    document.addEventListener('click', (e) => {
        if (!e.target.closest('.search-container')) {
            autocompleteList.style.display = 'none';
        }
    });

    fetchWordData('keyboard');
});
</script>

I hope you found this tutorial on Javascript by Cristian Villafane useful. Building a dictionary app is an excellent way to practice handling more complex API responses and creating a more sophisticated UI. Use this project as a starting point to explore and add new features, like saving favorite words or displaying usage source URLs. The only limit is your curiosity! 📚

Happy coding, Cristian Villafane

Weather App with JavaScript by Cristian Villafane

Weather App with JavaScript by Cristian Villafane

Weather App: A Javascript Tutorial by Cristian Villafane

A practical guide to connecting to a live API, handling data, and building a dynamic, real-world application.

Connecting your application to data from the real world is a fundamental skill for any web developer. A weather app is the perfect project for this, teaching you how to communicate with external services and display live information. This project is a fantastic exercise to master HTML, CSS, and the asynchronous capabilities of JavaScript. In this guide on Javascript by Cristian Villafane, we will build a functional and beautiful weather app from the ground up.

Phase 1: The Structure – HTML for the Interface

Every web application starts with a solid HTML structure. For our weather app, the HTML will define the user input area and the display for the weather data. We’ll need an input field for the city, a button to initiate the search, and a card to show the results. Now, we’ll also add a container for autocomplete suggestions.

Designing the User Interface

We begin with a main container. Inside, we’ll create a search area with an input and a button, a container for autocomplete suggestions, and a display card for the results. The user interface will consist of:

  • City Input: An <input type="text"> allows the user to type the name of a city. It will now trigger a search for city suggestions as the user types.
  • Search Button: A <button> with a search icon is now integrated into the input field for a cleaner look.
  • Autocomplete List: A <div> that will be dynamically populated with city suggestions from the API.
  • Display Card: A dedicated <div> will hold all the weather information: city name, local time, temperature, icon, condition, and other details like humidity, feels like temperature, and wind speed.
<!-- Search Container -->
<div class="search-container">
  <input type="text" id="city-input" placeholder="Enter city name...">
  <button id="search-btn">
    <!-- SVG Search Icon -->
  </button>
  <div class="autocomplete-suggestions" id="autocomplete-list"></div>
</div>

<!-- Weather Display -->
<div class="weather-display" id="weather-display">
  <!-- Content injected by JS -->
</div>

Phase 2: The Logic – Fetching and Displaying API Data

This is where our application comes to life. The JavaScript will listen for user input, make requests to the WeatherAPI, parse the response, and update the HTML to show the current weather conditions.

Understanding the API Response (JSON)

When we make a successful request to the API, it sends back a large block of text in JSON (JavaScript Object Notation) format. Our script then parses this text into a JavaScript object that we can easily work with. A simplified version of this object looks like this:

{
  "location": {
    "name": "London",
    "country": "United Kingdom",
    "localtime": "2023-10-27 13:00"
  },
  "current": {
    "temp_c": 12.0,
    "feelslike_c": 11.2,
    "humidity": 75,
    "wind_kph": 15.1,
    "uv": 3.0,
    "condition": {
      "text": "Partly cloudy",
      "icon": "//cdn.weatherapi.com/weather/64x64/day/116.png"
    }
  }
}

To access a piece of data, we use dot notation. For example, to get the temperature in Celsius, we would access data.current.temp_c, where data is the variable holding our parsed JSON object. To get the condition text (“Partly cloudy”), we need to go deeper: data.current.condition.text.

Customizing the Displayed Information

The best part of using an API is that you can choose exactly what to show. Let’s say you want to add the UV Index to our display.

  1. Find the data: Looking at the JSON above, the UV index is at current.uv.
  2. Add an HTML element: In our `displayWeather` function, we add a new `div` inside the `weather-details` section to hold this information.
    <div class="detail-item">
      <div class="detail-label">UV Index</div>
      <div class="detail-value">${current.uv}</div>
    </div>
  3. That’s it! By adding that snippet to our template literal in the JavaScript, the UV index will now appear on the card. The process is the same for any other data point you want to add. To remove an item, simply delete its corresponding `div` from the template.

Customizing the API Request (Language)

WeatherAPI allows us to customize the request using URL parameters. One of the most useful is `lang`, which changes the language of certain text fields in the response, like the weather condition.

In our code, the API URL is constructed like this:

const apiUrl = `https://api.weatherapi.com/v1/forecast.json?key=${apiKey}&q=${city}&lang=en`;

The part &lang=en tells the API to return the condition text in English (e.g., “Partly cloudy”). If you wanted the text in Spanish, you would simply change it to &lang=es. If you wanted French, you would use &lang=fr. This makes it easy to internationalize your application. The labels on your card (like “Humidity” or “Wind”) are hardcoded in our HTML template, so you would need to change those manually to match the language you choose.

Phase 3: Interactivity and Performance Optimization

A great app feels responsive and fast. We’ll connect our logic to user events and ensure the experience is smooth by using the same performance optimizations from the original template, such as non-blocking font loading.

Event Listeners and User Experience

We now have multiple event listeners: one for the `input` event on the text field to handle autocomplete, another for the `click` event on the search button, and a listener for the `Enter` key. We also handle clicks on the suggestion items to trigger a weather search. A default city is still loaded on page open to provide an immediate example.

Weather App in Action

Below, you can try out the live weather app. Enter a city name and see the new design, the added information, and the autocomplete functionality for yourself. This is the final result of applying the concepts from this tutorial.

The Complete Weather App Code

Here is the self-contained, performance-optimized code for the weather app component. You can copy and paste this into your own project. Remember to replace 'YOUR_API_KEY' with your actual key from WeatherAPI.com.

<!-- HTML Structure -->
<div class="weather-container">
    <div class="search-container">
        <input type="text" id="city-input" placeholder="Enter a city..." aria-label="City Name">
        <button id="search-btn" aria-label="Search">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>
        </button>
        <div class="autocomplete-suggestions" id="autocomplete-list"></div>
    </div>
    <div id="weather-display" class="weather-display">
        <!-- Weather data will be dynamically inserted here -->
    </div>
</div>

<!-- JavaScript Logic -->
<script>
// NOTE: This script block is for demonstration purposes only in this complete code view.
// In a real implementation, you would have a single script block at the end of your page
// to handle all interactivity, as shown in the final script of this document.
document.addEventListener('DOMContentLoaded', () => {
    const apiKey = 'YOUR_API_KEY'; // Replace with your key
    const cityInput = document.getElementById('city-input');
    const searchBtn = document.getElementById('search-btn');
    const weatherDisplay = document.getElementById('weather-display');
    const autocompleteList = document.getElementById('autocomplete-list');

    // ... (rest of the functions: fetchWeather, displayWeather, etc.)
});
</script>

I hope you found this tutorial on Javascript by Cristian Villafane useful. Building a weather app not only reinforces your knowledge of asynchronous JavaScript, but it also provides a practical foundation for working with any third-party API. Use this project as a starting point to explore and add new features, like a multi-day forecast or geolocation. The only limit is your curiosity! 🌦️

Happy coding, Cristian Villafane