Reputation: 107
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.
Upvotes: 1
Views: 348
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
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