Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

I need a workaround for a safari/chrome bug that is becoming a thorn in my side

So I have this neat little javascript function that I'm using to print text to the browser window in a cool command-prompty style. It takes a string and prints it one character at a time to the window at a set interval. Here it is: (I have cut out all the unnecessary parts so that this will work as a standalone example.)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.4.4.min.js"></script>
    <script type="text/javascript">
        var letterIndex = 0;
        var intervalId = 0;

        function writeOneLetter(myString)
        {
            var char = myString[letterIndex];
            $('#display').append(char);
            letterIndex++;
            if (letterIndex >= myString.length)
            {
                letterIndex = 0;
                clearInterval(intervalId);
            }
        }

        $(function ()
        {
            $('#caret').html('\u2588'); //This will help you visualize where the script is at in it's sequence and make it painfully obvious when the freezing issue occurs.

            // The following string sample is so long because it is important that you be able to duplicate this error to understand my question.
            var myString = "Quisque vestibulum consequat orci, in euismod tortor dapibus eu. Duis nec urna nec erat sagittis pretium vel ac diam. Nulla mi lorem, tempor ut cursus in, mattis non libero. Curabitur eget venenatis justo. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas blandit ante in ligula tincidunt quis vehicula massa scelerisque. Pellentesque nec posuere massa. Sed eget nunc a erat dictum faucibus. In vitae tempor lorem. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean vel imperdiet tellus. Suspendisse ultricies sem a libero sagittis feugiat. Ut convallis magna eu mauris molestie dapibus. Nulla feugiat urna non ante facilisis non ultrices nisi viverra. Aliquam vitae magna libero. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur at odio sit amet nisi dapibus scelerisque. In fringilla lorem at sapien rutrum scelerisque."

            intervalId = setInterval(function () { writeOneLetter(myString); }, 15);
        });

    </script>
</head>
<body>
    <span id="display">
    </span><span id="caret"></span>
</body>
</html>

Go here http://www.jsfiddle.net/Chevex/6n5VV/ to try out the sample code above.

If you are in IE or FF the code will run exactly as expected, writing out every character in the string until it finishes. However, if you run this code in Chrome or Safari you get an interesting bug. Sometimes, when the line hits the side of its container and the words wrap and drop to the next line, it freezes. The typing stops being rendered to the browser, but it is still happening in the DOM because if the page gets modified or the browser resized then the remaining text appears all at once.

A couple things I have noticed about this are that it only seems to happen when it drops down a line with a leading space. Also, if you resize the browser window while the script is still running or after the script has finished, it will suddenly burst back into action and you will see the rest of the text. Any resizing, maximizing, etc will start the letters showing up again; only to freeze again later of course.

It's extremely frustrating since it never shows the rest of the string unless the page is modified by more javascript afterward or the browser is resized. It completely defeats the entire purpose of writing the script in the first place when this happens.

Any ideas? I'm completely stumped and Google turns up nothing.

EDIT:

If you cannot reproduce the error it is probably because your screen is a different resolution than my own and you are getting lucky. Try to resize either the browser window, the jsFiddle display container, or both and then run the script again. It shouldn't take much before you see it freeze. Try to aim so that one of the lines will word wrap on a space, this seems to be where it happens mostly.

I have done this in chrome and safari on 3 different computers, one on a completely different network altogether. If you still can't see the error then run it in chrome and firefox side by side. If chrome seems to finish earlier than FF then that is the freezing glitch in action. If you resize the browser or modify the page in any way then suddenly all the remaining text will appear at once.

like image 426
Chev Avatar asked Nov 24 '10 10:11

Chev


2 Answers

It seems to work if you do not append each char as individual node but refresh the whole text content of the span each time the writeOneLetter-function is called:

    function writeOneLetter(myString)
    {
        var char = myString[letterIndex];
        $('#display').text($('#display').text()+char);
        letterIndex++;
        if (letterIndex >= myString.length)
        {
            letterIndex = 0;
            clearInterval(intervalId);
        }
    }

Try it here: http://www.jsfiddle.net/5R3JP/

like image 127
Simon Avatar answered Oct 20 '22 18:10

Simon


Weird bug, I agree. I get the same behaviour that you are describing (Chrome 7.0.517.44 under Windows). In my case, if the bug occurs, I don't even get the entire text displayed when the script finishes.

One trick that seems to work for me is to alternate the appearance of the caret (I guess it forces Chrome to do some rerendering). It's ugly, I know, and obviously a lot slower (albeit not visibly on my machine), but perhaps you can use it as a temporary workaround?

var letterIndex = 0;
var intervalId = 0;

function writeOneLetter(myString)
{
  var char = myString[letterIndex];
  $('#display').append(char);
  $('#caret').css("font-weight", letterIndex % 2 ? "bold" : "normal");
  letterIndex++;
  if (letterIndex >= myString.length)
  {
    letterIndex = 0;
    clearInterval(intervalId);
  }
}

Note: setting the caret character to bold doesn't actually visibly change its appearance, so you wont actually see the alternation for each character.

like image 1
Frode Avatar answered Oct 20 '22 18:10

Frode