Add a color gradient to text as it animates without displaying any code tags (HTML, CSS, JS)

I have a unique text animation that loads when the page loads. The characters will be gradually written to the screen with some words having a gradient effect. While I've managed to successfully apply the gradient, there seems to be a pause when it reaches the span tag before continuing to output each character with the gradient applied.

To see my closest working version of this requirement, check out my CodePen: https://codepen.io/Kelly-Gold/pen/oNPJaYL

In order to demonstrate the issue, I captured screenshots at the beginning and end of the gradient application showing unexpected HTML characters. Once the entire line is output, the result is as desired, but the process isn't smooth and displays unwanted characters along the way.

Text appearance upon arrival at the span tag: https://i.stack.imgur.com/3sW8o.png

Text appearance after completing the gradient and reaching the closing span tag: https://i.stack.imgur.com/17Ica.png

Text appearance at the conclusion of the line: https://i.stack.imgur.com/pf9wv.png

I'm struggling to make the animation fluid without pauses and prevent the display of characters at the beginning and end of the span tag.

Desired functionality:

  1. Output characters one by one per line with a brief pause at the end of each line
  2. Apply gradient effect to specific words
  3. Ensure no pause when reaching a gradient word, allowing for smooth flow like regular black text
  4. Avoid any visible HTML tags during runtime

Answer №1

This snippet of code checks if the letterCount is positioned at the beginning of a <span... or </span... tag, and adjusts it accordingly to skip over that tag.

It's not flawless though. One issue is that the html output between the opening and closing span tags doesn't include the closing tag. Additionally, I accidentally messed up the blink effect.

const lines = [
  "This is the first line",
  "This is the second line with a <span class='gradient'>gradient</span> word",
  "This is the last line with a <span class='gradient'>gradient</span> word",
];


let lineCount = 0;
let letterCount = 0;
let currentText = "";
let isCursorBlinking = true;

const textElement1 = document.querySelector(".line1");
const textElement2 = document.querySelector(".line2");
const textElement3 = document.querySelector(".line3");

function type() {
  if (letterCount === lines[lineCount].length) {
    pauseTyping();
    return;
  }

  letterCount++;
  currentText = lines[lineCount];
  
  const startOpenGradient = currentText.indexOf("<span class='gradient'>");
  const endOpenGradient = startOpenGradient + "<span class='gradient'>".length;
  const startCloseGradient = currentText.indexOf("</span>");
  const endCloseGradient = startCloseGradient + "</span>".length;
  
  
  if (letterCount === startOpenGradient)
    letterCount = endOpenGradient;
  else if (letterCount === startCloseGradient)
    letterCount = endCloseGradient;
  
  currentText = lines[lineCount].slice(0, letterCount);

    textElement1.innerHTML = currentText;

  isCursorBlinking = true;
  textElement3.classList.toggle("blink");

  
  
  setTimeout(type, 100);
}

function pauseTyping() {
  setTimeout(() => {
    letterCount = 0;
    lineCount++;

    if (lineCount === lines.length) {
      return;
    }

    type();
  }, 1000);
}

type();
.text-animation {
  font-size: 24px;
  font-weight: bold;
  color: #000;
  text-align: center;
  margin-top: 50px;
}

h1 {
  display: inline-block;
}

.blink {
  animation: blink 0.7s infinite;
}

@keyframes blink {
  0% {
    opacity: 1;
  }
  50% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}

.gradient {
  background: linear-gradient(to right, #FFC107, #FF5733);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}
<div class="text-animation">
  <h1 class="line1"></h1>
  <h1 class="line2"></h1>
  <h1 class="line3"></h1>
</div>

Answer №2

Thanks to @james for the helpful guidance on handling the span tags within the JS code

const lines = [
  "Greetings, I am <span class='gradient'>Kelly</span>",
  "I enjoy experimenting with <span class='gradient'>new technology</span>, expanding my knowledge, and exploring the outdoors.",
  "Currently working as a <span class='gradient'>Solutions Engineer</span>, bridging app ecosystems through APIs."
];

let lineCount = 0;
let letterCount = 0;
let currentText = "";

const textElements = [
  document.querySelector(".line1"),
  document.querySelector(".line2"),
  document.querySelector(".line3")
];

function type() {
  if (letterCount === 0) {
    // Start blinking cursor when typing begins on a new line
    textElements[lineCount].classList.add('cursor');
  }

  if (letterCount === lines[lineCount].length) {
    // Stop blinking cursor when typing ends on a line
    textElements[lineCount].classList.remove('cursor');
    textElements[lineCount].classList.add('cursor-blink');
    pauseTyping();
    return;
  }

  letterCount++;
  currentText = lines[lineCount];

  const startOpenGradient = currentText.indexOf("<span class='gradient'>");
  const endOpenGradient = startOpenGradient + "<span class='gradient'>".length;
  const startCloseGradient = currentText.indexOf("</span>");
  const endCloseGradient = startCloseGradient + "</span>".length;

  if (letterCount === startOpenGradient)
    letterCount = endOpenGradient;
  else if (letterCount === startCloseGradient)
    letterCount = endCloseGradient;

  currentText = lines[lineCount].slice(0, letterCount);
  textElements[lineCount].innerHTML = currentText;

  setTimeout(type, 100);
}

function pauseTyping() {
  setTimeout(() => {
    textElements[lineCount].classList.remove('cursor-blink');
    letterCount = 0;
    lineCount++;

    if (lineCount === lines.length) {
      // Make the cursor blink indefinitely at the end of the last line
      textElements[lineCount - 1].classList.add('cursor-blink');
      return;
    }

    type();
  }, 2000); // Change to 2000 for a 2-second pause between lines
}

type();
.text-animation {
  font-size: 24px;
  font-weight: bold;
  color: #000;
  text-align: left;
  margin-top: 50px;
  margin-left: auto;
  margin-right: auto;
  max-width: 600px;
}

.line { /* Add this class to apply margin to each line */
  margin-bottom: 10px; /* Adjust the space between lines */
}

h1 {
  display: inline-block;
}

.gradient {
  background: linear-gradient(to right, #FFC107, #FF5733);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

.cursor::after {
  content: "";
  display: inline-block;
  width: 20px;
  height: 35px;
  background-color: #000;
  margin-left: 3px;
  opacity: 1; /* Change the initial opacity to 0 */
}

@keyframes blink {
  0%, 50% { opacity: 1; }
  50.01%, 100% { opacity: 0; }
}
.no-cursor::after {
  content: none;
}
.cursor-blink::after {
  content: "";
  display: inline-block;
  width: 20px;
  height: 35px;
  background-color: #000;
  margin-left: 3px;
  opacity: 0;
  animation: blink 0.75s infinite;
}
HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Typing Animation with Blinking Cursor</title>
</head>
<body>
  <div class="text-animation">
    <div class="line">
      <h1 class="line1 text-container cursor"></h1>
    </div>
    <div class="line">
      <h1 class="line2 text-container"></h1>
    </div>
    <div class="line">
      <h1 class="line3 text-container"></h1>
    </div>
  </div>
</body>
</html>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Breaking down a intricate JavaScript expression in order to reformat it into a different structure

As I explore the task of refactoring a legacy application, I find myself faced with complex JavaScript expressions stored in a database column. These expressions contain validation and conditional rendering logic that need to be translated into structured ...

Enabling communication between my React frontend and Express server

I currently have a Digital Ocean Ubuntu VPS set up with Apache, Node, and Mongo running. I can successfully retrieve data from my database using CURL on the server and my node backend is running on port 3000. For my React frontend, I am using Fetch and it ...

Is there a way to invert the orientation of an object within a canvas?

As I was experimenting with Javascript, I attempted to implement a feature where my 'Player' character would fall down after reaching a jumpDistance of 50. The idea was to simulate a small jump-like motion for the player. While the code may not ...

The issue with IE-9 is that it is mistakenly picking up the placeholder value as

My HTML code looks like this: <input id="SLOCriteriaOtherText" name="SLOCriteriaOtherText" style="width: 100%;" type="text" data-role="autocomplete" placeholder="Enter name for 'other' metric..." class="k-input" autocomplete="off" role="textb ...

Angular.js encountered an error at line 13550: [ng:areq] The argument 'popCntrl' is expected to be a function, but it was undefined

I have been diving into learning AngularJS on my own recently, and I've been attempting to create a basic popup feature. However, I keep encountering the error mentioned in the title. While searching for solutions, I haven't found any examples th ...

Disabling the intellisense feature for locale suggestions in Monaco is recommended

Switch the keyboard language to a different one (in this case Japanese using alt + shift), and when typing in Monaco editor, an intellisense menu appears with options to remove and search. Monaco Editor Version: V0.33.0 https://i.stack.imgur.com/SIyeV.pn ...

Dynamic Component Interactions in VueJS using Mouse Events

Just starting out with Vue and other frameworks, so my approach may not be very "Vue-like". I am attempting to create a versatile button component that can have different behaviors based on a prop, in order to maintain just one button component. The desir ...

Implementing jQuery/JavaScript to efficiently iterate through JSON data

I have implemented a form that allows users to select an item from a multi-select dropdown (A). If the desired item is not listed, users can manually add it using another text input box (B). Once added, an AJAX call saves the item to the database. After su ...

How do popular social media platforms such as Facebook and Twitter utilize real-time APIs to display live updates in news feeds

I'm curious about the technology being used for real-time updates, and I doubt they rely on AJax methods like setInterval() to make server requests every second. This approach could be inefficient with a large number of concurrent users. Do you think ...

Swapping link positions with jQuery

Is it possible to rearrange links using jQuery? Under the navigation menu, there will be content div for each tab. The selected link should always be positioned next to the first "sixrevision" link. The code snippet is shown below: <ul id="crumbs" ...

Enhanced Slider Display featuring Multiple Posts for Improved Performance

My Sample Page features a slider that displays over 200 posts, each containing 5 images. However, the slider loads all the images at once, causing my page speed to be very slow. I am looking for an optimized way to display the slider without compromising l ...

Hiding elements in FireBase and Angular based on user identification

Currently venturing into the world of Firebase in conjunction with AngularJS after previously working with php and server-side rendered pages. I am grappling with how to securely hide specific parts of an application from certain users. I have three disti ...

Error: Trying to send FormData to ajax results in an Illegal Invocation TypeError being thrown

Successfully sending a file to the server for processing using the code below: var formData = new FormData(); formData.append('file', $('#fileUpload')[0].files[0]); options = JSON.stringify(options); // {"key": "value"} $.ajax({ ...

Problem with Flickity's is-selected feature

I am currently exploring Flickity and its functionality. I have a carousel that auto-plays, with the selected cell always placed in the middle and highlighted with a grey background. However, I would like to customize it so that the selected cell is positi ...

Identifying the differences between a select2 dropdown and select2 multiselect: a guide

I currently have two different controls on my page: a select2 dropdown and a jquery multi value select Select2 Dropdown <select id="drp_me" class="select2-offscreen"> <option value="1">one</option> <option value="2">two</op ...

Changing the height of one Div based on another Div's height

I currently have a display of four divs positioned side by side. I am seeking a solution to ensure that the height of each div remains consistent, and should adjust accordingly if one of them increases in size due to added text content. For reference, ple ...

What is the correct way to establish and terminate a MongoDB connection in a Node.js application?

Hey everyone, I have a piece of code at this link (https://github.com/MrRav3n/Angular-Marketplace/blob/master/server.js) and I'm curious if I am properly starting and ending my database connection. Should I connect and end the db connection in every a ...

Netlify Lambda function with Expressjs generates a fresh session for each incoming request

Good Evening, After successfully running my Expressjs API locally without utilizing lambda functions, I encountered an issue where every request created a new session once the lambda function was introduced. Below is the implementation of server.js and Das ...

Verifying if a div in jQuery holds a specific element

I am currently developing drag and drop widgets using jQuery. After dropping them, I need to verify if my draggable and droppable widget is located inside another div. <div id="droptarget"> <div class="widget">I'm a widget!</div> ...

Is tsconfig.json Utilized by Gatsby When Using Typescript?

Many blog posts and the example on Gatsby JS's website demonstrate the use of a tsconfig.json file alongside the gatsby-plugin-typescript for TypeScript support in Gatsby. However, it seems like the tsconfig.json file is not actually utilized for conf ...