You can implement custom cursors using event callbacks, like so:
customCursorTE.onBeforeTyping(() => { customCursorExample[1].classList.remove("idle"); }); customCursorTE.onAfterTyping(() => { customCursorExample[1].classList.add("idle"); }); customCursorTE.onBeforeUntyping(() => { customCursorExample[1].classList.remove("idle"); }); customCursorTE.onAfterUntyping(() => { customCursorExample[1].classList.add("idle"); });
Here customCursorExample[1] is our cursor element from this HTML:
<div class="typing-container"> Some facts about cats: <span></span><span class="cursor idle"></span> </div>
And the CSS for the cursor:
.cursor { background-color: #00ffff; display: inline-block; width: 10px; height: 1.4em; vertical-align: middle; height: 1em; } .cursor.idle { animation: idleCursorAnimation 2s infinite; } With animation: @keyframes idleCursorAnimation { 0% { background-color: #22c55e; } 50% { background-color: #f43f5e; } 100% { background-color: #22c55e; } }
.cursor { margin-left: -10px; translate: 100% 0; }
Note the cursor extending over the container's edge; unfortunately, this is a drawback of current solution.
The container width should be set to contain entire string without cursor, if not - use resizeHere we pause typing when input is focused, and we hide the placehlder with CSS so it doesn't interfere with actual input value.
Here's JS:
const nameInputExample = document.querySelector( "#name-input-ex input" )!; const nameInputExampleTE = new TypingEffect( [ "Mick Jagger", "Freddie Mercury", "Jimi Hendrix", "David Bowie", "Kurt Cobain", "Jonathan David Douglas 'Jon' Lord", ], (string) => { nameInputExample.setAttribute("placeholder", string); } ).start(); nameInputExample.addEventListener("focus", () => { nameInputExampleTE.pause(); }); nameInputExample.addEventListener("blur", () => { nameInputExampleTE.resume(); });
And CSS:
input { padding: 6px 12px; font-size: 1em; } input::placeholder { opacity: 0.5; color: currentColor; } input:focus::placeholder { opacity: 0; }
This example demonstrates the layout shift, which can occur when setting element contend. Here the container's min-height is unset, unlike in other examples, and width is intentionally small to demonstrate the shift.
Some long important text about cat history, that user is trying to read. Cats have a fascinating history that dates back thousands of years. Originating in ancient Egypt, they were revered as sacred animals and often depicted in hieroglyphs. Over time, cats became valued for their hunting abilities, helping to control pests in human settlements. During the Middle Ages in Europe, cats were associated with superstitions but eventually regained favor as cherished companions. Today, cats are one of the most popular pets worldwide, known for their independent yet affectionate nature.
This block and it's padding simulate additional content.
Additional content and spacing are required to demonstrate the shift effectively; since it needs scroll NEITHER to be at 0%, 100%, NOR at he end of typing contaier to be noticeable.
Some long important text about cat history, that user is trying to read. Cats have a fascinating history that dates back thousands of years. Originating in ancient Egypt, they were revered as sacred animals and often depicted in hieroglyphs. Over time, cats became valued for their hunting abilities, helping to control pests in human settlements. During the Middle Ages in Europe, cats were associated with superstitions but eventually regained favor as cherished companions. Today, cats are one of the most popular pets worldwide, known for their independent yet affectionate nature.
This block and it's padding simulate additional content.
This solution uses hidden element with longest string in the set as it's content, and CSS position to overlap the elements.
Here, we add a wrapper element, and filler, which will dictate the height of our typing-container:
<div class="typing-container-position-wrap"> <div class="filler"></div> <div class="typing-container"> Some facts about cats: <span></span> </div> </div>
Position typing-container on top of filler, and hide the latter. It is important for filler and typing-container to have identical styles such as width, text-size, line-height, paddings and others that may influence the height of an element. I recommend setting a height for the filler element so that it doesn't jitter as much on load.
.typing-container-position-wrap { position: relative; } .typing-container-position-wrap .filler { visibility: hidden; max-width: 400px; height: 150px; } .typing-container-position-wrap .typing-container { position: absolute; top: 0; }
And JS:
// Find the longest string in array const layoutShiftExample_longestString = layoutShiftExampleStrigs.reduce( (longest, current) => { return current.length > longest.length ? current : longest; }, "" ); // Select filler element const layoutShiftExample_solution1_filler = document.querySelector( "#problem-layout-shift .dynamic-solution-position .filler" ); // Set filler content requestAnimationFrame(() => { layoutShiftExample_solution1_filler.style.height = "auto"; layoutShiftExample_solution1_filler.innerText = "Some facts about cats: " + layoutShiftExample_longestString + "|"; });
Setting the content and height within requestAnimationFrame is recommended, because it helps the browser to schedule this update before paint.
Or you can achieve similar result with grid areas:
.typing-container-position-wrap { display: grid; grid-template-areas: "overlap"; } .typing-container-position-wrap .filler { grid-area: overlap; } .typing-container-position-wrap .typing-container { grid-area: overlap; }