Le0
Le0

Reputation: 107

Javascript Safari not running line before confirm() Method

This is actually a example from the book Head First JavaScript ch05. I have no problem with the codes in Firefox but Safari, and I am not sure where the problem is.

<html>
  <head>
    <title>Mandango - The Macho Movie Ticket Finder</title>

    <script type="text/javascript">
      var seats = [ false, true, false, true, true, true, false, true, false ];
      var selSeat = -1;

      function initSeats() {
        // Initialize the appearance of all seats
        for (var i = 0; i < seats.length; i++) {
          if (seats[i]) {
            // Set the seat to available
            document.getElementById("seat" + i).src = "seat_avail.png";
            document.getElementById("seat" + i).alt = "Available seat";
          }
          else {
            // Set the seat to unavailable
            document.getElementById("seat" + i).src = "seat_unavail.png";
            document.getElementById("seat" + i).alt = "Unavailable seat";
          }
        }
      }

      function findSeat() {
        // If seat is already selected, reinitialize all seats to clear them
        if (selSeat >= 0) {
          selSeat = -1;
          initSeats();
        }

        // Search through all the seats for availability
        for (var i = 0; i < seats.length; i++) {
          // See if the current seat is available
          if (seats[i]) {
            // Set the seat selection and update the appearance of the seat
            selSeat = i;
            document.getElementById("seat" + i).src = "seat_select.png";
            document.getElementById("seat" + i).alt = "Your seat";

            // Prompt the user to accept the seat
            var accept = confirm("Seat " + (i + 1) + " is available. Accept?");
            if (accept) {
              // The user accepted the seat, so we're done
              break;
            }
            else {
              // The user rejected the seat, so clear the seat selection and keep looking
              selSeat = -1;
              document.getElementById("seat" + i).src = "seat_avail.png";
              document.getElementById("seat" + i).alt = "Available seat";
            }
          }
        }
      }
    </script>
  </head>

  <body onload="initSeats();">
    <div style="margin-top:75px; text-align:center">
      <img id="seat0" src="" alt="" />
      <img id="seat1" src="" alt="" />
      <img id="seat2" src="" alt="" />
      <img id="seat3" src="" alt="" />
      <img id="seat4" src="" alt="" />
      <img id="seat5" src="" alt="" />
      <img id="seat6" src="" alt="" />
      <img id="seat7" src="" alt="" />
      <img id="seat8" src="" alt="" /><br />
      <input type="button" id="findseat" value="Find Seat" onclick="findSeat();" />
    </div>
  </body>
</html>

It supposes to loop through the seats looking for available(True) seat, update the seat image and ask the user by confirm method.

The problem is findSeat function:

It runs perfectly with Firefox but when I ran it in Safari (version 9.1), the seat image would not update before I click "OK" in the confirm box. i.e. below codes wouldn't run until confirm method return True.

document.getElementById("seat" + i).src = "seat_select.png"

document.getElementById("seat" + i).alt = "Your seat"

I guess this is the problem with confirm method. Anyone could help? Thanks.

seat_select.png seat_select.png

seat_avail.png seat_avail

seat_unavail.png seat_unavail

Upvotes: 1

Views: 348

Answers (2)

Kraig Walker
Kraig Walker

Reputation: 812

There appears to be a bug in Safari where the confirm method fires before preceding code has time to execute. One solution as seen here is to run your confirm within a setTimeout callback function.

E.g

var accept;

function doConfirm() {
    accept = confirm("Seat " + (i + 1) + " is available. Accept?");
}

// run the confirm after 200ms
window.setTimeout(doConfirm(), 200);

Upvotes: 0

T.J. Crowder
T.J. Crowder

Reputation: 1074266

JavaScript on browsers runs with a single UI thread1, which may (depending on the browser architecture) also have other work that it does in addition to running JavaScript, typically UI-related tasks like updating images.

The confirm method (and its cousin alert) bring that thread to a screeching halt on many browsers, preventing it from doing anything until/unless the user dismisses the dialog.

The code you've quoted assumes that either the seat image will update synchronously with the assignment to src (not a safe assumption, esp. as it may have to be downloaded) or that it will update while the confirm dialog is showing (not a safe assumption).

Instead, the code should be using a load event callback on the img element, something like this (largely untested), see the comments:

function findSeat() {
  var seatDisplay;

  // If seat is already selected, reinitialize all seats to clear them
  if (selSeat >= 0) {
    selSeat = -1;
    initSeats();
  }

  // Find an available seat
  var availableSeat = null;
  seats.some(function(seat, index) {
    if (seat) {
      availableSeat = seat;
      selSeat = index;
      return true; // Stop looping
    }
  });
  if (availableSeat) {
    // Set the seat selection and update the appearance of the seat
    var seatDisplay = document.getElementById("seat" + selSeat);
    seatDisplay.src = "";
    seatDisplay.addEventListener("load", seatReady, false);
    seatdisplay.src = "seat_select.png";
    seatdisplay.alt = "Your seat";
  }

  function seatReady() {
    // Remove this handler
    seatDisplay.removeEventListener("load", seatReady, false);

    // Prompt the user to accept the seat
    var accept = confirm("Seat " + (selSeat + 1) + " is available. Accept?");
    if (accept) {
      // The user accepted the seat, so we're done
    }
    else {
      // The user rejected the seat, so clear the seat selection and keep looking
      selSeat = -1;
      seatDisplay.src = "seat_avail.png";
      seatDisplay.alt = "Available seat";
    }
  }
}

They probably didn't do that because they wanted to avoid the complexity at this stage of the book, but unfortunately apparently didn't test sufficiently on different browsers and were unaware of the possible race condition.


1 "JavaScript on browsers runs with a single UI thread..." But is not single-threaded, as you'll hear people constantly claim. There's only one UI thread, but you can have as many other threads as you like, using web workers.

Upvotes: 1

Related Questions