0

I'm trying to create a cool animation but in order to do so, I need to split the word where there is a space into its own div so it looks like this:

<h1 class="fancy-word">
<span class="word">
  <span class="word__inner">A</span>
  <span class="word__inner">b</span>
  <span class="word__inner">o</span>
  <span class="word__inner">u</span>
  <span class="word__inner">t</span>
</span>

<span class="word">
  <span class="word__inner">m</span>
  <span class="word__inner">y</span>
</span>

<span class="word">
  <span class="word__inner">s</span>
  <span class="word__inner">i</span>
  <span class="word__inner">t</span>
  <span class="word__inner">e</span>
</span>
</h1>

So far, I only have the ability to 'chunk' the whole word.

var h1 = document.querySelector(".fancy-word"); //  h1
var text = h1.textContent.split("");                 // Content into array

var result = ""; // Will hold output

// Loop
text.forEach(function(char){
  result += (char.trim() === "") ? "" : "<span>" + char + "</span>";
});

h1.innerHTML = result;  

console.log(h1.outerHTML); 
<h1 class="fancy-word">About my site</h1>

How best could I achieve this?

cmp
  • 420
  • 5
  • 22
  • 1
    So essentially you want to iterate over each character in a string? https://stackoverflow.com/questions/1966476/how-can-i-process-each-letter-of-text-using-javascript – tiguchi Oct 10 '19 at 19:04

4 Answers4

1
  1. Split at the space to get an array of each word.
  2. Map that array using another split with no delimiter to make each array entry another array of each letter.
  3. Map the inner array to wrap each letter in a span with the word_inner class, then map the outer array to wrap each resulting split word into a span with the word class.

Example:

const h1 = document.querySelector('h1');
let words = h1.textContent.split(' ');
words = words.map(word => {
  let letters = word.split('');
  letters = letters.map(letter => `<span class="word_inner">${letter}</span>`);
  return letters.join('');
});
words = words.map(word => `<span class="word">${word}</span>`);
h1.innerHTML = words.join(' ');
console.log(h1.innerHTML);
<h1>ABOUT MY SITE</h1>
IceMetalPunk
  • 5,476
  • 3
  • 19
  • 26
1

That's how I would do it:

// create and configure elements we're going to re-use
const wordTemplate = document.createElement('span');
const charTemplate = wordTemplate.cloneNode(false);

wordTemplate.classList.add('word');
charTemplate.classList.add('word__inner');

coolSplitString('About my site');

function coolSplitString(str) {
  const words = str.split(' '); // split the string into words

  words.forEach((word) => {
    const wordSpan = wordTemplate.cloneNode(false); // create a word-container

    word.split('').forEach((char) => { // split the word into letters
      const charSpan = charTemplate.cloneNode(false); // create a letter-container
      charSpan.textContent = char; // add the letter as text
      wordSpan.append(charSpan); // add letter-container to word-container
    });

    document.body.append(wordSpan); // add word-container to document
    console.log(wordSpan.innerHTML);
  });
}
Andre Nuechter
  • 2,141
  • 11
  • 19
1

Before splitting the string into individual characters, split it into an array of words. You can then loop through each word, loop through the characters in each word, and do what you need to do in between.

In the example I've got here, I've split things into a couple functions to break things up. This example also includes the ability to conditionally add css classes to the wrapping element.

The basic method here:

  1. Split the text up into an array of words.
  2. Loop over the words array, adding a wrapper to each word, and passing it to a function that wraps each character.
  3. push the result to a new array
  4. return the joined version of the array to create the new string.

Quick note: I used a bit of es6 syntax here, so if you don't want to use template literals, you can replace things with string contatination.

/**
 * Add wrappers to individual characters in a word
 * @method addWrappers
 * @param  {String}    word    - The word to be processed
 * @param  {String}    tag     - The HTML tag to be used for the wrappers
 * @param  {String}    classes - OPTIONAL - Any CSS classes to be applied
 * @return {String}            - Wrapped chracters as a new string.
 */
function addWrappers(word, tag, classes) {
  // Split into individual characters.
  const chars = word.split('');
  
  // The array the newly wrapped 
  // characters will be added to.
  const wrappedChars = [];
  
  // The loop
  chars.forEach(function(char) {
    // Check if any classes were passed.
    // If so add them, else just use a basic tag.
    const openTag = classes ? `${tag} class="${classes}"` : tag;
    wrappedChars.push(`<${openTag}>${char}</${tag}>`);
  });
  return wrappedChars.join('');
}

// A basic implementation.  
function initBasic() {
  const element = document.querySelector('.fancy-word')
  const text = element.textContent;
  
  // Split the string into words
  const words = text.split(' ');
  
  // Our array of processed words
  const newContent = [];
  
  // The main loop
  words.forEach(function(word) {
    newContent.push(`<span class="word">${addWrappers(word, 'span', 'word__inner')}</span>`);
  });
  // Add a space to keep the words looking 
  // right wirhout styling, but you can easily just
  // leave it out and use margins or padding 
  // to create the desired effect.
  element.innerHTML = newContent.join(' ');
  console.log(element.outerHTML);
}

initBasic();
<h1 class="fancy-word">About my site</h1>

EXAMPLE WITHOUT TEMPLATE LITERALS:

/**
 * Add wrappers to individual characters in a word
 * @method addWrappers
 * @param  {String}    word    - The word to be processed
 * @param  {String}    tag     - The HTML tag to be used for the wrappers
 * @param  {String}    classes - OPTIONAL - Any CSS classes to be applied
 */
function addWrappers(word, tag, classes) {
 const chars = word.split('')
  const wrappedChars = [];
  chars.forEach(function(char) {
    const openTag = classes ? tag + ' class="' + classes + '"' : tag;
    wrappedChars.push('<' + openTag + '>' + char + '</' + char + '>')
  });
  return wrappedChars.join('');
}

function initBasic() {
  const element = document.querySelector('.fancy-word')
  const text = element.textContent;
  
  const words = text.split(' ');
  const newContent = [];
  
  words.forEach(function(word) {
    newContent.push('<span class="word">' + addWrappers(word, 'span', 'word__inner') + '</span>');
  });
  element.innerHTML = newContent.join(' ');
  console.log(element.outerHTML);
}
joeyred
  • 21
  • 3
  • This is very useful. Thanks. I wonder how I could make this work If I had many references? – cmp Oct 10 '19 at 21:10
0

Is something like this what you're looking for?

var h1 = document.querySelector(".fancy-word"); //  h1
var text = h1.textContent.split(" ");                 // Content into array

var result = ""; // Will hold output

// Loop
text.forEach(function(word){
  innerResult = "";
  word.split("").forEach((letter) => {
      innerResult += (letter.trim() === "") ? "" : "<span>" + letter + "</span>";
  })
  result += "<span>" + innerResult + "</span>";
});

h1.innerHTML = result;  

console.log(h1.outerHTML); 
<h1 class="fancy-word">About my site</h1>
pinkwaffles
  • 463
  • 4
  • 12