1

I am trying to create a typewriter function but func in function typewrite becomes undefined.

var txtElems = document.querySelectorAll("[data-txt]");
txtElems = Array.from(txtElems);
typewriteAll(txtElems, 70);
function typewriteAll(elemArr, delay) {
	if (elemArr.length) {
		typewrite(
			elemArr[0],
			elemArr[0].dataset.txt,
			delay,
			typewriteAll, elemArr.slice(1), delay
		);
	}
}

function typewrite(Elem, t, delay, func, arg1, arg2) {
	let txt = String(t);
	(txt != "")
		? (() => {
			Elem.innerHTML += txt[0];
			setTimeout(
				() => {
					typewrite(
						Elem,
						txt.slice(1, txt.length),
						delay
					)
				}, delay)
			})()
		: (() => {
			setTimeout(
				() => {
					Elem.setAttribute("typing", "end");
					func(arg1, arg2)
				}, 10 * delay
			);
		})()
}
<div data-txt="Some text to type"></div>
<div data-txt="Some more text to type..."></div>

4
  • Have you tried making typewrite a fat arrow function? Commented May 5, 2020 at 5:51
  • 4
    Because there’s a call to typewrite in there where you’re not passing a forth argument…!? Commented May 5, 2020 at 5:54
  • a smaller snip will help members debug the issue in a better manner. Commented May 5, 2020 at 8:29
  • @DaneBrouwer That wouldn't help, the OP doesn't use this inside the function, which could cause similar issues... Commented May 5, 2020 at 17:02

1 Answer 1

2

Function calls work fine, the problem is, that inside the typewrite's setTimeout callback, (when you recall typewrite), you forgotten to pass the callback and its parameters to itself, so they are defined only during the first iteration.

To make it work, pass all arguments to itself:

var txtElems = document.querySelectorAll("[data-txt]");
txtElems = Array.from(txtElems);
typewriteAll(txtElems, 70);
function typewriteAll(elemArr, delay) {
    if (elemArr.length) {
        typewrite(
            elemArr[0],
            elemArr[0].dataset.txt,
            delay,
            typewriteAll, elemArr.slice(1), delay
        );
    }
}

function typewrite(Elem, t, delay, func, arg1, arg2) {
    let txt = String(t);
    if(txt != ""){
        Elem.innerHTML += txt[0];
        setTimeout(() => {
            typewrite(
                Elem,
                txt.slice(1, txt.length),
                delay,
                //here:
                func,
                arg1,
                arg2
            )
        }, delay)
    } else {
        setTimeout(() => {
                Elem.setAttribute("typing", "end");
                func(arg1, arg2)
        }, 10 * delay)
    }
}
<div data-txt="Some text to type"></div>
<div data-txt="Some more text to type..."></div>

And if you're creating typewriter effects, I highly recommend you to use setInterval and ES6 generator functions, as they make the code much readable... and helps you avoid making such mistakes:

function typewrite(element, text, delay){
  return new Promise((resolve, reject) => {
    const iterator = (function*() {
      try{
        for(const letter of text){
          element.textContent += letter
          yield
        }
        element.setAttribute("typing", "end");
        resolve()
      }catch(e){
        reject(e)
      }finally{
        clearTimeout(interval)
      }
    })()
    const interval = setInterval(() => iterator.next(), delay);
    iterator.next()
  })
}

function typewriteAll(elems, delay){
  return elems.reduce((acc,elem) => acc.then(() => typewrite(elem, elem.dataset.txt, delay)), Promise.resolve())
}

var txtElems = document.querySelectorAll("[data-txt]");
txtElems = Array.from(txtElems);
typewriteAll(txtElems, 70)
<div data-txt="Some text to type"></div>
<div data-txt="Some more text to type..."></div>

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.