How to extract first word from text and wrap it in span element using vanilla JavaScript

I’m trying to migrate from jQuery to plain JavaScript but running into issues with text manipulation.

I need to take the first word from an h2 element, wrap it in a span tag, and replace the original first word. Right now my vanilla JS code is adding the span-wrapped word before the original text instead of replacing it.

The problem seems to be with how I’m using insertAdjacentHTML but I can’t figure out what’s wrong.

Original jQuery version:

var element = $(".wrapper h2").contents().filter(function () {
  return this.nodeType == 3 }).first(),
  content = element.text(),
  firstWord = content.slice(0, content.indexOf(" "));

if(!element.length)
  return;
        
element[0].nodeValue = content.slice(firstWord.length);
element.before('<span>' + firstWord + '</span>');

My vanilla JavaScript attempt:

const modifyHeading = (() => {
  let element = document.querySelector(".wrapper h2");

  if (element) {
    let textContent = element.textContent.trim();
    let firstWord = textContent.slice(0, textContent.indexOf(" ")); 
    element.childNodes[0] = textContent.slice(firstWord.length);
    console.log(firstWord);
    console.log(textContent);
    firstWord = `<span>${firstWord}</span>`;
    element.insertAdjacentHTML("afterbegin", firstWord);
  }
})();

CSS:

h2 span {
  color: blue;
}

HTML:

<div class="wrapper">
  <h2>Hello world and more text here</h2>
</div>

What am I doing wrong with the text replacement?

You’re mixing up text nodes and element properties. The line element.childNodes[0] = textContent.slice(firstWord.length) won’t work because you’re trying to assign a string to a node reference. Don’t work with textContent - manipulate the actual text node directly. Here’s a cleaner approach that mimics your jQuery logic:

const modifyHeading = (() => {
  let element = document.querySelector(".wrapper h2");
  let textNode = element && element.childNodes[0];
  
  if (textNode && textNode.nodeType === 3) {
    let content = textNode.nodeValue;
    let firstWord = content.slice(0, content.indexOf(" "));
    
    textNode.nodeValue = content.slice(firstWord.length);
    element.insertAdjacentHTML("afterbegin", `<span>${firstWord}</span>`);
  }
})();

The key is working directly with the text node’s nodeValue property rather than trying to reassign the node itself. This preserves the existing text structure while properly replacing the first word.

the problem’s in this line: element.childNodes[0] = textContent.slice(firstWord.length); - you can’t assign strings directly to childNodes. use element.childNodes[0].nodeValue = textContent.slice(firstWord.length); instead. that’s how you modify text node content in vanilla js.

You’re on the right track, but there’s a problem with how you’re handling text nodes. You can’t do element.childNodes[0] = textContent.slice(firstWord.length) because you’re trying to replace a node object with a string. You need to use the nodeValue property instead.

Here’s the fix:

const modifyHeading = (() => {
  let element = document.querySelector(".wrapper h2");
  
  if (element && element.firstChild && element.firstChild.nodeType === 3) {
    let textNode = element.firstChild;
    let textContent = textNode.nodeValue.trim();
    let spaceIndex = textContent.indexOf(" ");
    
    if (spaceIndex > 0) {
      let firstWord = textContent.slice(0, spaceIndex);
      let remainingText = textContent.slice(spaceIndex);
      
      textNode.nodeValue = remainingText;
      element.insertAdjacentHTML("afterbegin", `<span>${firstWord}</span>`);
    }
  }
})();

Main changes: use nodeValue to modify the text content and make sure you’re actually dealing with a text node first.