Jump to content
Search Community

SplitText loses font style when texts inside div are under misc tags

kakarlus test
Moderator Tag

Warning: Please note

This thread was started before GSAP 3 was released. Some information, especially the syntax, may be out of date for GSAP 3. Please see the GSAP 3 migration guide and release notes for more information about how to update the code to GSAP 3's syntax. 

Recommended Posts



SplitText does not preserve the HTML tags or additional styling nested inside the text you are trying to split.


As per the SplitText docs "Notes and Limitations"


  • SplitText only recognizes raw text and <br> tags, not child elements like <strong> or <em> elements



So for you example you really only need SplitText to bread #desc .desc into lines.

Take a look here:



  • Like 2
Link to comment
Share on other sites

  • 2 years later...

I know I'm coming to this post way late but I ran into the same issue where I was losing a class name I had wrapped part of the text I was then splitting into lines. I'm not sure if this is something that has since been supported and I'm also a noob at GSAP as I've only been using it for the past week. But, for any future visitors perhaps wondering if there's a workaround, I've devised a solution that grabs the text within elements of a specified class to then wrap that same text with spans with that same class. The only caveat (that I know of) is if a block of text (single word/character or more with the special class) occurs more than once in the entire split line text block, it will be applied to the first match.


consider the following markup:

<p class="target">this is some <span class="special">special</span> text.</p>


would merit the following js:

const target = document.querySelector('.target')
// target needs to be cloned because splitText mutates the passed element as well as any references
const original = target.cloneNode(true)
const splitTarget = new SplitText(target, {type: 'lines'})
const preservedSplitTarget = preserveClass(splitTarget, original, '.special')

// function to preserve a class on text split by lines. 
// ideally imported from a helper file
function preserveClass(splitText, original, className) {
  const texts = Array.from(original.querySelectorAll(className)).map(val => val.innerText.split(' '))
  // Check if any of the original element contains any children with the className
  if (texts.length === 0) return splitText
  // regex to match a text preceded by the beginning of the string or a space and has a space or non-word character or end of string after.
  const genWordRegex = word => new RegExp(`(?:^|\\s)${word}(?=\\s|\\W|$)`)
  // helper for quick splitting text on spaces and filtering out empty strings
  const splitInnerHTML = innerHTML => innerHTML.split(' ').filter(s => s.length > 0)
  const {lines} = splitText
  // template helper for wrapping text with spans using the className argument
  const spanTemplate = content => `<span class="${className.replace(/\./, '')}">${content}</span>`
  // tracks which occurrence of the class your tracking (the first level of the multidimensional array)
  let blockIndex = 0
  // the current block of text to wrap
  let textToPreserve = texts[blockIndex]
  // tracks which word in the block to look for
  let textIndex = 0

  // recursively checks the target text against the textArray (textToPreserve) and wraps matching text in target
  const recursiveSwap = (target, textArray) => {
    const textPiece = textArray[textIndex]
    const RE = genWordRegex(textPiece)

    // ensure that textPiece lives in the target before proceeding
    if (!RE.test(target)) return target

    const newTarget = target.replace(RE, first => {
      // since javascript doesnt support regex lookbehinds (yet), leading spaces are returned in the match so we want to preserve that
      const start = first.charAt(0) === ' ' ? ' ' : ''
      return `${start}${spanTemplate(textPiece)}`

    textIndex = textIndex + 1

    // if we have reached the end of the current textBlock
    if (textIndex >= textArray.length) {
      blockIndex = blockIndex + 1
      // if we have reached the end of all textBlocks
      if (blockIndex >= texts.length) return newTarget
      textToPreserve = texts[blockIndex]
      textIndex = 0

    return recursiveSwap(newTarget, textArray)

  return {
    lines: lines.map((l, i, linesArray) => {
      let lineText = l.innerHTML
      // return the line if the next piece of text to preserve doesn't match
      if (!genWordRegex(textToPreserve[textIndex]).test(lineText)) return l
      // if we've already started replacing pieces in the current textBlock or there's only one item in the block
      if (textIndex > 0 || textToPreserve.length === 1) {
        l.innerHTML = recursiveSwap(lineText, textToPreserve)
      } else {
        let splitLine = splitInnerHTML(lineText)
        let currWordIndex = splitLine.indexOf(textToPreserve[textIndex])
        let nextWord = textToPreserve[1]
        // check if there are more words in the current line
        if (currWordIndex < splitLine.length - 1) {
          // check if the next words of both the textToPreserve and line match to ensure we start replacing the correct occurrence of a common word
          if (nextWord === splitLine[currWordIndex + 1]) {
            l.innerHTML = recursiveSwap(lineText, textToPreserve)
        // else we've reached the end of the current line
        } else {
          // check if there is a next line and that the next textToPreserve word matches the first word in the next line
          if (i < linesArray.length - 1 && nextWord === splitInnerHTML(linesArray[i + 1].innerHTML)[0]) {
            l.innerHTML = recursiveSwap(lineText, textToPreserve)
      return l


This is the first iteration of a workaround and certainly has room for improvement and I would love to hear any feedback/concerns regarding this approach. Hope this is of some help.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Create New...