0

I have this piece of Javascript. It works with sockets.io and server-side Node.js. Here it gets an array of data from the server and displays it on the screen.

socket.on('score', function(score) {
    //"score" is an array of things to be displayed on the client
    $('#scoreb').html(score[0]);
    $('#scoreb_total').html(score[2]);
    $('#scorer').html(score[1]);
    $('#scorer_total').html(score[3]);

    if(score[5] != "N"){
        //find this seat's chair on this client's screen
        var ch = findChair(score[4]);

        $('#player'+ch+'_trump').html("Victory!");
    }
});

score is something like [40, 0, 40, 0, 3, "H"] (using console.log confirms that it's properly formatted).

findChair does some calculations and returns a number between 1 and 4. If I put console.log(ch) after the function is called, I get 1 (correct).

The code before the if condition works fine.

And now comes the weird part. I have this in my HTML:

<div id="player1_trump"></div>
<div id="player2_trump"></div>
<div id="player3_trump"></div>
<div id="player4_trump"></div>

So after ch is initialized, the appropriate div (in this case player1_trump) should be filled with the word Victory!. But this doesn't happen! Weirder still, when instead of console.log I use alert here:

var ch = findChair(score[4]);
alert(ch);
$('#player'+ch+'_trump').html("Victory!");

When I click 'OK' on the alert prompt, the word Victory is displayed correctly in player1_trump! So what's going on here? There's nothing asynchronous here, this is plain client-side Javascript, and ch is apparently correct after the findChair function runs. So why does it only work when I put an alert between one line and the other?

EDIT (here is the function):

function findChair(s){
    var ch = -1;
    $.each(chairs, function (chair, seat) {
        if(s == seat)
            ch = chair;
        else
            $('#player'+chair+'_trump').html('');
    });

    return ch;
}

EDIT 2:

If I replace $('#player'+ch+'_trump').html("Victory!"); with document.getElementById('#player'+ch+'_trump').innerHTML="Victory!" I get an error saying TypeError: document.getElementById(...) is null. However, if I look at the source code before any of this happens (it all happens when a button is clicked, not on page load) the target div is present.

Using $('#player'+ch+'_trump').delay(200).html("Victory!"); also doesn't work, regardless of how long a delay I put in it. So it's not just a matter of timing.

Using .text instead of .html also doesn't work.

sveti petar
  • 3,637
  • 13
  • 67
  • 144
  • where are elements with ID scoreb, scoreb_total, scorer, scorer_total? I don't see them in the HTML you provided – Joshua Smickus Jan 30 '14 at 16:22
  • 2
    Just a thought, but the alert() will wait for you to hit OK, meaning other javascript can run to completion. My gut feeling is you have a race condition where the the DIV does not yet exist in the DOM when you try to update it's html() element... The artificial delay introduced by calling alert() and waiting for user to click allows the DIV to be created in time. – David Fleeman Jan 30 '14 at 16:22
  • 3
    does *"some calculations"* include any asynchronous actions? – Kevin B Jan 30 '14 at 16:23
  • @JoshSmickus They don't really matter, they're just empty divs to begin with, and their content is set correctly. – sveti petar Jan 30 '14 at 16:23
  • @DavidFleeman All the divs are loaded with the page, not later. – sveti petar Jan 30 '14 at 16:24
  • @KevinB No, it's just a `jQuery each` loop, and after that, `return`. – sveti petar Jan 30 '14 at 16:24
  • 2
    I don't believe you, because an alert wouldn't impact the result *unless it contains asynchronous actions*. can you show us? – Kevin B Jan 30 '14 at 16:25
  • 3
    It's not "plain client-side JS" if there is socket IO involved. – Dave Newton Jan 30 '14 at 16:26
  • @KevinB Sure, `findChair` has been added to the question. It empties all the player_trump divs except the one with number `ch`, and returns `ch`. – sveti petar Jan 30 '14 at 16:26
  • @DaveNewton socket.io is just the source of the data, I don't see how that impacts what Javascript does with that data on the client afterwards? – sveti petar Jan 30 '14 at 16:27
  • 1
    @jovan Because "afterwards" means "after what?" and we don't know how your page is set up and what order things happen in. – Dave Newton Jan 30 '14 at 16:28
  • @jovan What gives `$('#player'+ch+'_trump').length` without an alert? – plalx Jan 30 '14 at 16:29
  • @DaveNewton In the code above, Javascript waits for something named 'score' to come from Socket IO. After that something comes, I think what Javascript does with it is 'plain client-side Javascript'. No? – sveti petar Jan 30 '14 at 16:30
  • @plalx It gives 1, both before and after I attempt to set the HTML content. – sveti petar Jan 30 '14 at 16:32
  • 1
    @jovan And if it comes before your DOM is ready it will fail. We do not have access to your monitor or code; we have no idea how anything is working. If it only works after a time delay then there's something timing-dependent. – Dave Newton Jan 30 '14 at 16:32
  • 1
    If i mock up a page using just the code you have provided and a custom chairs array, your code works. something outside of the code you have provided is impacting the result. ***something asynchronous***, such as socket.io. http://jsfiddle.net/3nHVX/ – Kevin B Jan 30 '14 at 16:33
  • Have you tried using .Text() instead of .Html() I have seen some strange behavior in the past where new html won't be rendered until you 'poke' the DOM –  Jan 30 '14 at 16:35
  • @plalx Tried it, didn't work (please see edit to question). – sveti petar Jan 30 '14 at 16:43
  • @DaveNewton I tried putting a delay on it (see edit above) but it still didn't work, so I don't think it's just timing. – sveti petar Jan 30 '14 at 16:44
  • @JustinM I tried it just now, same thing. – sveti petar Jan 30 '14 at 16:44
  • @jovan Alright, you said jQuery found a node since `length` was `1`. Could you check which node it found? `console.log($('#player'+ch+'_trump')[0])` – plalx Jan 30 '14 at 16:45
  • @plalx That logs `
    ` - which, I think, makes it more baffling.
    – sveti petar Jan 30 '14 at 16:47
  • @jovan Try again with `document.getElementById` but do not put an `#` there. It's not supposed to be there... if it still doesn't find anything, that's very wierd. – plalx Jan 30 '14 at 16:49
  • @plalx Right, sorry. Well, now there's no error but it still didn't put the word in the div. I have the div outlined on the screen to make sure it's visible when the script runs, and I just checked to make sure nothing else has the same ID. – sveti petar Jan 30 '14 at 16:53
  • @jovan I'm sorry, `delay()` will not delay `html()`. I'm sure it's still a timing issue somehow. Just try with `setTimeout` to see what it gives. – plalx Jan 30 '14 at 16:54
  • @plalx I tried with `setTimeout('4000')` to give it plenty of time, but still the same thing. – sveti petar Jan 30 '14 at 16:57
  • Right, sorry. Yes, it works now with the timeout. Any idea how to troubleshoot it at this point? – sveti petar Jan 30 '14 at 17:01
  • @plalx Actually, I found the root cause. The `findChair` function was also running from a different location in the script, which also responds to io.socket. As you noticed, this function clears all `player_trump` divs except one. So all I had to do was change the code up a bit, taking the part that clears divs out of that function, and now it all works. Sorry for wasting everyone's time. – sveti petar Jan 30 '14 at 17:30
  • @jovan As you have a solution, please answer your own question. – Ben Smith Jan 30 '14 at 23:32

1 Answers1

0

I found the answer myself.

The comments to my question were correct in assuming it's an issue with the asynchronous nature of socket.io.

The function findChair was emptying the player_trump divs, all except one. The problem is, in addition to the score socket listener, there was another socket listener also running that function, often simultaneously. The solution was simply to take the code that empties the divs out of the function so the other function call wouldn't empty them:

var ch = findChair(score[4]);
$('.player_trump').html('');
$('#player'+ch+'_trump').html("Victory!");

function findChair(s){
    var ch = -1;
    $.each(chairs, function (chair, seat) {
        if(s == seat)
            ch = chair;
    });

    return ch;
}
sveti petar
  • 3,637
  • 13
  • 67
  • 144