Displaying Word Phonetics

Each word in the API response includes phonetics. A phonetic is pronunciation text like /həˈloʊ/ together with an audio clip. The code follows the same pattern as the definitions. Small helpers each build one piece, and one orchestrating function puts the pieces together. The new thing here is the <audio> element, so let’s start there.

The HTML <audio> Element

HTML has a built-in <audio> element for embedding sound. In its simplest form, it looks like this:

<audio controls>
  <source src="path/to/file.mp3" type="audio/mpeg" />
  Your browser does not support the audio element.
</audio>

A few things to understand:

Part Purpose
<audio> The container element. Adding the controls attribute tells the browser to show play/pause, volume, and a progress bar.
<source> A child element that specifies the actual audio file and its format. You can include multiple <source> elements for different formats — the browser picks the first one it supports.
Fallback text Plain text placed inside <audio> that only displays if the browser does not support audio at all. This is rare in modern browsers, but it is good practice.

In our app, we create these elements dynamically in JavaScript because we do not know the audio URLs ahead of time — they come from the API.

Preparing the Section

Just like with definitions, we grab the container, clear it, and set up layout classes:

const createPhoneticsSection = () => {
  const phoneticsSection = document.getElementById("phonetics");
  phoneticsSection.innerHTML = "";
  phoneticsSection.classList.add("flex", "flex-col", "gap-4");
  return phoneticsSection;
};

The Helper Functions

The heading, container, and phonetic text label follow the same create-style-return pattern from the definitions section:

const createPhoneticsHeading = () => {
  const phoneticsHeading = document.createElement("h1");
  phoneticsHeading.classList.add("text-2xl", "font-semibold");
  phoneticsHeading.innerText = "Phonetics";
  return phoneticsHeading;
};

const createPhoneticsDiv = () => {
  const phoneticsDiv = document.createElement("div");
  phoneticsDiv.classList.add("bg-stone-100");
  return phoneticsDiv;
};

const createPhoneticElement = (text) => {
  const phoneticText = document.createElement("p");
  phoneticText.classList.add("px-4", "py-3", "text-white", "bg-stone-700");
  phoneticText.innerText = text;
  return phoneticText;
};

Creating the Audio Player

createAudioControl builds the <audio> element, sets its width, and adds the controls attribute so the browser renders playback controls. createAudioSource builds the <source> child with the URL from the API:

const createAudioControl = () => {
  const audioControl = document.createElement("audio");
  audioControl.style = "width: 100%";
  audioControl.setAttribute("controls", "true");
  return audioControl;
};

const createAudioSource = (audio) => {
  const source = document.createElement("source");
  source.setAttribute("src", audio);
  source.setAttribute("type", "audio/mpeg");
  return source;
};

The orchestrating function assembles these and appends the fallback text node as the last child of <audio>.

The Orchestrating Function

displayWordPhonetic loops over the phonetics array. Some entries from the API are incomplete (missing text or audio), so we skip those with an early return:

const displayWordPhonetic = (phonetics) => {
  const phoneticsSection = createPhoneticsSection();

  const phoneticsHeading = createPhoneticsHeading();
  phoneticsSection.appendChild(phoneticsHeading);

  phonetics.forEach((phonetic) => {
    const { text, audio } = phonetic;

    if (!text || !audio) return;

    const phoneticsDiv = createPhoneticsDiv();
    phoneticsSection.appendChild(phoneticsDiv);

    const phoneticText = createPhoneticElement(text);
    phoneticsDiv.appendChild(phoneticText);

    const audioControl = createAudioControl();
    phoneticsDiv.appendChild(audioControl);

    const source = createAudioSource(audio);
    audioControl.appendChild(source);

    audioControl.appendChild(
      document.createTextNode(
        "Your browser does not support the audio element.",
      ),
    );
  });
};

A couple of things to notice:

  • The if (!text || !audio) return inside forEach acts like a continue. It skips the current iteration and moves on to the next phonetic entry.
  • The fallback text node is created fresh inside the loop each time. A DOM node can only live in one place. If you create a single text node and append it repeatedly, the browser moves it on each iteration, so only the last entry would have it.