Jump to content
Search Community

GSAP splitText wraps characters in divs instead of span?

nightlock82
Moderator Tag

Go to solution Solved by Rodrigo,

Recommended Posts

Posted

I'm doing a splitText specifically on separate characters on react. The issue is splitText uses divs to separate the characters instead of spans, this becomes a problem for screen-readers. I've tried applying the effect on the container and inline text but the output is still the same. Is there a way to make it into spans? 

 

image.png.92964d87d9a7de8e1b2a7938dbd85b68.png

  • Solution
Posted

Hi @nightlock82 and welcome to the GreenSock forums!

 

Your can specify the tag in the SplitText config object

 

tag

String - by default, SplitText wraps things in <div> elements, but you can define any tag like tag: "span"

 

Happy Tweening!

  • Like 2
Posted

Just so you know, the reason SplitText wraps things in <div> instead of <span> by default is because most browsers will not render transforms on elements that have display: inline (which is what spans do). So just be careful about that - if you try animating x/y/rotation/scale, for example, and it’s not working with <span>, that’s why.  

  • Thanks 1
  • 3 weeks later...
Posted

@GreenSock I noticed this happening. Are there any possible workarounds I saw some samples from websites on awwwards for example but I couldn't understand how they were doing this. My main concern with <div>'s is the accessibility, a screen reader would read it on a per-letter basis and that's a real head-scratcher.

Posted

Hi,

 

I'm not an a11y expert at all, but you could check if the user is on a screen reader and revert the SplitText instance or perhaps never create it? I would try that. Maybe I'm wrong in my consideration, but a text being animated letter by letter is not something super useful on a screen reader, right?

 

Again I know virtually nothing about a11y so I'm just proposing an idea based on zero knowledge and just what I consider kind of useful on the particular scenario of a screen reader.

 

Hopefully this helps.

Happy Tweening!

Posted

I guess you could try setting display: inline-block on the <span> to make them work with transforms. I’m half-conscious in bed with COVID on my iPad, so I can’t verify at the moment, but you could give it a shot.

  • Like 1
Posted
Quote

My main concern with <div>'s is the accessibility, a screen reader would read it on a per-letter basis and that's a real head-scratcher.

Nah there's tons of ways round this!

 

One route is doubling up content. You visually hide one  so sighted people don't see it, but screenreaders read it out still, then whack aria-hidden on the one that's being split.

 

https://www.a11yproject.com/posts/how-to-hide-content/#:~:text=visually-hidden class is applied,focus indicator had gone to.

<h2 class="visually-hidden">This is a sentence</h2>
<h2 class="split" aria-hidden="true">This is a sentence</h2>
.visually-hidden {
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  height: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}

 

Another option is using aria hidden on the header and an aria label and role on a containing div

<div role="heading" aria-level="2" aria-label="word">
  <h2 class="title" aria-hidden="true">
    <div>w</div>
    <div>o</div>
    <div>r</div>
    <div>d</div>
  </h2>
</div>


Another one would be to loop round the children that splittext creates and whack aria-hidden on them and an aria label on the text element.

 

I raised this casually with Jack the other day about seeing if we can bake an option in so it's a little easier for people.

 

Either way though, plenty of ways around this!

  • Like 3
Posted

Here's a demo of the best (but more fiddly) way.

Ideally you'd have an aria label on the text element itself rather than assigning aria attributes to a non semantic element like a div.

So something like this.

I would go for this route with headings, but I'd likely duplicate text for long sections of text like paragraphs.

   

Another route I've gone in the past is adding aria-hidden on split text elements, then calling revert on them once they've animated in and removing the aria-hidden. Like so...

See the Pen poZaJQa?editors=1010 by GreenSock (@GreenSock) on CodePen.



Obviously all these routes are worth testing with folks that actually use screenreaders if you have the capacity to do so.

  • Like 3
  • Thanks 1
Posted

Oh, I haven't been on for a bit, thank you folks for the responses. ❤️ Gonna try it out and see this is awesome. 

  • Like 1
  • 3 months later...
Levin Riegner
Posted
On 1/3/2023 at 12:23 PM, Rodrigo said:

Hi @nightlock82 and welcome to the GreenSock forums!

 

Your can specify the tag in the SplitText config object

 

tag

String - by default, SplitText wraps things in <div> elements, but you can define any tag like tag: "span"

 

Happy Tweening!

There is no option for "tag", Chrome is complaining that i cannot have Divs inside a <p> tag, so it need it to be a span

Posted

Hi @Levin Riegner,

 

In the config object passed to the SplitText constuctor, from the SplitText docs:

tag

String - by default, SplitText wraps things in <div> elements, but you can define any tag like tag: "span"

 

const split = new SplitText(yourElement, {
  tag: "span",
});

Hopefully this helps.

Happy Tweening!

Levin Riegner
Posted
1 hour ago, Rodrigo said:

Hi @Levin Riegner,

 

In the config object passed to the SplitText constuctor, from the SplitText docs:

tag

String - by default, SplitText wraps things in <div> elements, but you can define any tag like tag: "span"

 

const split = new SplitText(yourElement, {
  tag: "span",
});

Hopefully this helps.

Happy Tweening!

This doesnt work for me

Levin Riegner
Posted

Heres my example code:
 

// Imports
// ------------
import React, { useRef, useLayoutEffect } from 'react';
import { gsap } from 'gsap';
import { SplitText } from 'gsap/all';

// Styles
// ------------
import { Jacket } from './styles';

// Component
// ------------
const ScrollAnimatedText = ({ children }) => {
	// NOTE • refs
	const comp = useRef();

	// NOTE • Animation
	useLayoutEffect(() => {
		let timer;
		let split;

		const ctx = gsap.context(() => {
			timer = setTimeout(() => {
				const childText = comp.current.firstChild;

				split = new SplitText(childText, {
					tag: 'span',
					type: 'lines,words,chars',
					linesClass: 'line',
				});

				const lines = comp.current.querySelectorAll('.line');

				for (const line of lines) {
					gsap.to(line, {
						ease: 'none',
						opacity: 1,
						y: `0%`,
						scrollTrigger: {
							trigger: line,
							start: 'top bottom',
							end: 'top top+=60%',
							scrub: true,
							markers: false,
						},
						stagger: {
							each: 0.08,
							from: 'start',
						},
					});
				}
			}, 150);
		}, comp);

		return () => {
			clearTimeout(timer);
			ctx.revert();
		};
	}, []);

	return <Jacket ref={comp}>{children}</Jacket>;
};

export default React.memo(ScrollAnimatedText);

 

Posted

Hi,

 

Without a minimal demo there is not a lot we can do. This starter template has the GSAP Trial package so you can run the bonus plugins and tools:

https://stackblitz.com/edit/react-iqmjfx

 

Also I'm curious about why you're running that setTimeout inside the effect hook ?‍♂️

 

Also you can replace this:

const lines = comp.current.querySelectorAll('.line');

With this:

const lines = gsap.utils.toArray('.line');

Inside the GSAP Context instance you already defined a scope, to the toArray() method knows exactly where to look for that selector.

 

Happy Tweening!

Levin Riegner
Posted

Im having to run a settimeout, as for some reason, without it gsap doesnt seem to render any animations

Levin Riegner
Posted

Without the settimeout splittext puts single words onto different lines for some bizarre reason

Posted
1 minute ago, Levin Riegner said:

Im having to run a settimeout, as for some reason, without it gsap doesnt seem to render any animations

That's odd ?

Please create a minimal demo so we can take a better look.

 

Happy Tweening!

Levin Riegner
Posted

Im not sure how to recreate it outside of my react application - where do you usually import SplitText from with the business account?

Posted

Hi,

 

If you check the template I linked before:

https://stackblitz.com/edit/react-iqmjfx

 

You'll see that the import comes from the GSAP Trial package:

import gsap from 'gsap-trial';
import { ScrollTrigger } from 'gsap-trial/ScrollTrigger';
import { SplitText } from 'gsap-trial/SplitText';

Just fork that example and a minimal representation of your setup. Please don't include all your code as it will take longer to find the issue, just the pertinent part of it that's causing the problem you're facing.

 

Happy Tweening!

Levin Riegner
Posted

Why would i use the trial in my build when i pay for greensock?

Posted

That's just for the stackblitz template not your setup. There's no other way to use the bonus tools there. In your setup keep using the files you download or the npm token

Posted

Seems to work fine - am I missing something?:

See the Pen WNaPMRV?editors=1010 by GreenSock (@GreenSock) on CodePen.

 

Keep in mind that browsers won't honor transforms on display: inline elements. That has nothing to do with GSAP - it's just how CSS works. Just set them to inline-block then. 

  • Like 1
Levin Riegner
Posted

The problem here is youre importing splittext, but not from the import library, it seems to work when i create it in codepen with the trial, but not with the import in my react project

Levin Riegner
Posted

After a reinstall of NPM today, everything is now working again

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...