Mer
Mer

Reputation: 93

Typewriter effect in text input not working in Firefox

This code produces a typewriter animation effect in the input when a user hovers over it, it animates the placeholder text. When a user moves away, I want the animation to stop and the input to go back to its original state.

$(function() {

    var sppInput,
        sppInputName = $('#spp-input-name'),
        sppInputNamePlace = sppInputName.attr('placeholder');

    // Typewriter Effect
    function sppInputStart(elm, n, text) {
        if (n < (text.length)) {
            $(elm).attr('placeholder', text.substring(0, n + 1));
            n++;
            sppInput = setTimeout(function () {
                sppInputStart(elm, n, text);
            }, 80);
        }
    }
    function sppInputStop(elm, place) {
        clearTimeout(sppInput);
        $(elm).attr('placeholder', place);
    }

    // Typewriter Effect for Name
    sppInputName.mouseover(function () {
        sppInputStart(this, 0, sppInputName.data('typewriter'));
    });
    sppInputName.mouseout(function () {
        sppInputStop(this, sppInputNamePlace);
    });

});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input placeholder="Name" data-typewriter="Insert the Name" type="text" id="spp-input-name" name="name" required>

This code works with all browsers (including IE), but NOT firefox.

Why?

Upvotes: 1

Views: 726

Answers (2)

Keith
Keith

Reputation: 155692

In Firefox mouseover and mouseout fire multiple times. In fact a number of things can cause them to refire. Here changing the placeholder property does it, probably because FX is recalculating the layout after it changes something visual. There are likely to be similar issues in other browsers that you haven't found yet.

You can't rely on mouseover and mouseout (or indeed any position events) only firing once to start your effect and once again to stop it.

Instead make this a state of the input and toggle a flag:

$(function() {

    var sppInput,
        sppInputName = $('#spp-input-name'),
        sppInputNamePlace = sppInputName.attr('placeholder'),
        inputAnimating = false;

    // Typewriter Effect
    function sppInputStart(elm, n, text) {
        if(!inputAnimating) {
            setTimeout(function () {
                sppInputStart(elm, n, text);
            }, 500);
            return;
        }
        
        if (n < (text.length)) {
            $(elm).attr('placeholder', text.substring(0, n + 1));
            n++;
            sppInput = setTimeout(function () {
                sppInputStart(elm, n, text);
            }, 80);
        }
    }
    function sppInputStop(elm, place) {
        clearTimeout(sppInput);
        $(elm).attr('placeholder', place);
    }

    // Typewriter Effect for Name
    sppInputName.mouseover(function () {
        inputAnimating = true;
    });
    sppInputName.mouseout(function () {
        inputAnimating = false;
    });
    
    sppInputStart(sppInputName, 0, sppInputName.data('typewriter'));

});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input placeholder="Name" data-typewriter="Insert the Name" type="text" id="spp-input-name" name="name" required>

This works in FX.

However, consider using requestAnimationFrame or using something other than the placeholder property, as this is likely to cause dropped frames, especially on mobile.

You may also cause A11Y issues, as screen readers will have trouble reading a property that you're changing ever 80ms.

Upvotes: 0

Jaromanda X
Jaromanda X

Reputation: 1

Looks like changing the placeholder value retriggers mouseover event

a "hack" that works:

$(document).ready(function() {

    var sppInput,
        sppInputName = $('#spp-input-name'),
        sppInputNamePlace = sppInputName.attr('placeholder');

    // Typewriter Effect
    function sppInputStart(elm, n, text) {
        if (n < (text.length)) {
            $(elm).attr('placeholder', text.substring(0, n + 1));
            n++;
            sppInput = setTimeout(function() {
                sppInputStart(elm, n, text);
            }, 80);
        }
    }

    function sppInputStop(elm, place) {
        clearTimeout(sppInput);
        $(elm).attr('placeholder', place);
    }

    // Typewriter Effect for Name
    sppInputName.mouseover(function() {
        // hack
        if ($(this).data('flag') != '1') {
            $(this).data('flag', '1');
            sppInputStart(this, 0, sppInputName.data('typewriter'));
        }
    });
    sppInputName.mouseout(function() {
        // hack
        $(this).data('flag', '0');
        sppInputStop(this, sppInputNamePlace);
    });

});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input placeholder="Name" data-typewriter="Insert the Name" type="text" id="spp-input-name" name="name" required>

Upvotes: 1

Related Questions