3

2nd update 9/6/2015

So here's what I came up with for a solution—it's certainly much shorter, and works in all my Mac browsers so I'm assuming it works elsewhere too. Is there a way to condense it further or should I leave it at this?

var myForm = document.form1;
var radioNames = [myForm.proSpeed, myForm.ram, myForm.storage, myForm.graphics, myForm.cursorControl];
var lastPrice = [0, 0, 0, 0, 0];
var total = 2299;
var result = document.getElementById('result');
result.innerHTML = "$" + total + ".00";

function getLastPrice(radios, lastPriceIndex) {    
    for (var index = 0; index < radios.length; index++) {
        if (radios[index].checked) {
            var price = parseInt(radios[index].value);
            total = total + lastPrice[lastPriceIndex] + price;
            result.innerHTML = "$" + total + ".00";
            lastPrice[lastPriceIndex] = -price;
        }
    }    
}

function getProPrice() {
    getLastPrice(myForm.proSpeed, 0);
}
function getRamPrice() {
    getLastPrice(myForm.ram, 1);
}
function getStoPrice() {
    getLastPrice(myForm.storage, 2);
}
function getGraPrice() {
    getLastPrice(myForm.graphics, 3);
}
function getCurPrice() {
    getLastPrice(myForm.cursorControl, 4);
}

var priceFunctions = [getProPrice, getRamPrice, getStoPrice, getGraPrice, getCurPrice];

function addRadioListeners(radios, priceFunction) {
    for (var index = 0; index < radios.length; index++) {
        radios[index].addEventListener("change", priceFunction);
    }
}

for (var index = 0; index < 5; index++) {
    addRadioListeners(radioNames[index], priceFunctions[index]);
}

UPDATE 9/5/2015

@Barmar thanks again for all your help. I'm now combining the addPrice functions:

function addPrice(price, radios) {
    for (var index = 0; index < radios.length; index++) {
        if (radios[index].checked) {
            total = total - price + parseInt(radios[index].value);
            price = parseInt(radios[index].value);
            result.innerHTML = "$" + total + ".00";
            break;
        }
    }
}

proPrice = addPrice(proPrice, myForm.proSpeed);

Just not sure what to do next to remove the other functions and keep the form working. In other words where does proPrice go and what do I put in the change eventListener now that addProPrice no longer exists? proPrice comes back as undefined.


Original Post

I'm working on a JavaScript exercise that had me create a build-your-own-computer store page, so I decided to just try to mimic a page from the Apple Store. Here's the page I'm trying to copy:

Retina 5k iMac

And here's my (very) bare-bones version: mock-up of iMac store page

My version works in Chrome, but not in Firefox or Safari. I have no idea what the problem is. Any thoughts?

(Below is the full code in case you don't want to follow the link)

HTML:

<!DOCTYPE html>

<html lang="en">
<head>
    <title>Apple Store Sim</title>
</head>

<body>

<form action="" name="form1" id="form1">
    <h1>iMac with Retina 5K display</h1>
    <p>1. Choose Processor</p>
    <p>
        <input type="radio" name="proSpeed" checked="checked" value="0" />
        <label>3.5GHz Quad-core Intel Core i5, Turbo Boost up to 3.9GHz</label><br />
        <input type="radio" name="proSpeed" value="250" />
        <label>4.0GHz Quad-core Intel Core i7, Turbo Boost up to 4.4GHz</label>
    </p>
    <p>2. Choose Memory</p>
    <p>
        <input type="radio" name="ram" checked="checked" value="0" />
        <label>8GB 1600MHz DDR3 SDRAM - 2x4GB</label><br />
        <input type="radio" name="ram" value="200" />
        <label>16GB 1600MHz DDR3 SDRAM - 2x8GB</label><br />
        <input type="radio" name="ram" value="600" />
        <label>32GB 1600MHz DDR3 SDRAM - 4x8GB</label>
    </p>
    <p>3. Choose Storage</p>
    <p>
        <input type="radio" name="storage" checked="checked" value="0" />
        <label>1TB Fusion Drive</label><br />
        <input type="radio" name="storage" value="150" />
        <label>3TB Fusion Drive</label><br />
        <input type="radio" name="storage" value="0" />
        <label>256GB Flash Storage</label><br />
        <input type="radio" name="storage" value="300" />
        <label>512GB Flash Storage</label><br />
        <input type="radio" name="storage" value="800" />
        <label>1TB Flash Storage</label>
    </p>
    <p>4. Choose Graphics</p>
    <p>
        <input type="radio" name="graphics" checked="checked" value="0" />
        <label>AMD Radeon R9 M290X 2GB GDDR5</label><br />
        <input type="radio" name="graphics" value="250" />
        <label>AMD Radeon R9 M295X 4GB GDDR5</label>
    </p>
    <p>5. Choose Mouse and Magic Trackpad</p>
    <p>
        <input type="radio" name="cursorControl" checked="checked" value="0" />
        <label>Apple Magic Mouse</label><br />
        <input type="radio" name="cursorControl" value="0" />
        <label>Magic Trackpad</label><br />
        <input type="radio" name="cursorControl" value="0" />
        <label>Apple Mouse</label><br />
        <input type="radio" name="cursorControl" value="69" />
        <label>Apple Magic Mouse + Magic Trackpad</label><br />
    </p>
    <p>6. Choose Apple Keyboard and Documentation</p>
    <p>
        <select name="keyboard" size="1">
            <option value="0" selected="selected">Apple Wireless Keyboard (English) &amp; User's Guide</option>
            <option value="0">Apple Wireless Keyboard (Arabic) &amp; User's Guide</option>
            <option value="0">Apple Wireless Keyboard (British) &amp; User's Guide</option>
        </select>
    </p>
</form>
<div id="result"></div>
<script src="appleStoreSim.js"></script>
</body>
</html>

Javascript:

var myForm = document.form1;
var proPrice = 0;
var ramPrice = 0;
var stoPrice = 0;
var graPrice = 0;
var curPrice = 0;
var total = 2299;
var result = document.getElementById('result');
result.innerHTML = "$" + total + ".00";

function addProPrice(radio) {
    var radios = myForm.proSpeed;
    
    for (var index = 0; index < radios.length; index++) { 
        if (radios[index].checked) {
            total = total - proPrice + parseInt(radios[index].value);
            result.innerHTML = "$" + total + ".00";
            addProListener();
        }
    }    
}

function addProListener(radio) {
    var radios = myForm.proSpeed;

    for (var index = 0; index < radios.length; index++) {
        if (radios[index].checked) {
            radios[index].removeEventListener("click", addProPrice);
            proPrice = radios[index].value;
        } else {
            radios[index].addEventListener("click", addProPrice)
        }
    }
}

for (var index = 0; index < myForm.proSpeed.length; index++) { 
    myForm.proSpeed[index].addEventListener("focus", addProListener);
}

function addMemPrice(radio) {
    var radios = myForm.ram;
    
    for (var index = 0; index < radios.length; index++) { 
        if (radios[index].checked) {
            total = total - ramPrice + parseInt(radios[index].value);
            result.innerHTML = "$" + total + ".00";
            addMemListener();
        }
    }
}

function addMemListener(radio) {
    var radios = myForm.ram;
    
    for (var index = 0; index < radios.length; index++) {
        if (radios[index].checked) {
            radios[index].removeEventListener("click", addMemPrice);
            ramPrice = radios[index].value;
        } else {
            radios[index].addEventListener("click", addMemPrice)
        }
    }
}

for (var index = 0; index < myForm.ram.length; index++) { 
    myForm.ram[index].addEventListener("focus", addMemListener);
}

function addStoPrice(radio) {
    var radios = myForm.storage;
    
    for (var index = 0; index < radios.length; index++) { 
        if (radios[index].checked) {
            total = total - stoPrice + parseInt(radios[index].value);
            result.innerHTML = "$" + total + ".00";
            addStoListener();
        }
    }
}

function addStoListener(radio) {
    var radios = myForm.storage;
    
    for (var index = 0; index < radios.length; index++) {
        if (radios[index].checked) {
            radios[index].removeEventListener("click", addStoPrice);
            stoPrice = radios[index].value;
        } else {
            radios[index].addEventListener("click", addStoPrice)
        }
    }
}

for (var index = 0; index < myForm.storage.length; index++) { 
    myForm.storage[index].addEventListener("focus", addStoListener);
}

function addGraPrice(radio) {
    var radios = myForm.graphics;
    
    for (var index = 0; index < radios.length; index++) { 
        if (radios[index].checked) {
            total = total - graPrice + parseInt(radios[index].value);
            result.innerHTML = "$" + total + ".00";
            addGraListener();
        }
    }
}

function addGraListener(radio) {
    var radios = myForm.graphics;
    
    for (var index = 0; index < radios.length; index++) {
        if (radios[index].checked) {
            radios[index].removeEventListener("click", addGraPrice);
            graPrice = radios[index].value;
        } else {
            radios[index].addEventListener("click", addGraPrice)
        }
    }
}

for (var index = 0; index < myForm.graphics.length; index++) { 
    myForm.graphics[index].addEventListener("focus", addGraListener);
}

function addCurPrice(radio) {
    var radios = myForm.cursorControl;
    
    for (var index = 0; index < radios.length; index++) { 
        if (radios[index].checked) {
            total = total - curPrice + parseInt(radios[index].value);
            result.innerHTML = "$" + total + ".00";
            addCurListener();
        }
    }
}

function addCurListener(radio) {
    var radios = myForm.cursorControl;
    
    for (var index = 0; index < radios.length; index++) {
        if (radios[index].checked) {
            radios[index].removeEventListener("click", addCurPrice);
            curPrice = radios[index].value;
        } else {
            radios[index].addEventListener("click", addCurPrice)
        }
    }
}

for (var index = 0; index < myForm.cursorControl.length; index++) { 
    myForm.cursorControl[index].addEventListener("focus", addCurListener);
}
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Jacob Smolowe
  • 369
  • 2
  • 11
  • 3
    What specifically is not working? – Adam Buchanan Smith Sep 04 '15 at 22:19
  • Uh whoops, that would have been helpful info for me to include! Thanks. The price at the bottom doesn't update in Safari or Firefox but does in Chrome. – Jacob Smolowe Sep 04 '15 at 22:21
  • 1
    @AdamBuchananSmith The price doesn't update – Ruan Mendes Sep 04 '15 at 22:21
  • 2
    I guess, you are Mac user? It works fine on PC.... :) Safari is garbage, almost like IE, not sure about Firefox for Mac... However, any errors in console? – sinisake Sep 04 '15 at 22:23
  • 1
    Errors in the browser console? – j08691 Sep 04 '15 at 22:25
  • correct, I'm a Mac user. No console errors. Just nothing happens. – Jacob Smolowe Sep 04 '15 at 22:26
  • 1
    I can confirm that everything works normally in Firefox 40.0.3 on Arch Linux, with the price updating properly – markasoftware Sep 04 '15 at 22:35
  • Thanks Markasoftware; that's interesting. I have Firefox 40.0.3 on OS X 10.10.5—strange, it must be a Mac thing? Are there known events-related issues between Mac and PC versions of the same browsers? – Jacob Smolowe Sep 04 '15 at 22:41
  • 1
    I'm seeing it working fine in Firefox on Windows. I think perhaps it might be an inconsistency in when the JavaScript is running? Is that a possibility: the for loops are running before the DOM is fully loaded, so they don't get all the necessary event listeners attached to them? Maybe think about moving your for loops into something that runs when the document is ready. – Ashelyn Dawn Sep 04 '15 at 22:46
  • @PawnStar that's a good idea—I'll check that out! – Jacob Smolowe Sep 04 '15 at 22:47
  • I can confirm that it fails in FF 40.0.3 on OS X 10.6.8. – Barmar Sep 04 '15 at 22:55
  • It looks like the problem is that FF on the Mac doesn't trigger the `focus` event when you click on a radio button. – Barmar Sep 04 '15 at 23:01
  • @PawnStar would I need jQuery to do that or can I do it in pure JavaScript? – Jacob Smolowe Sep 04 '15 at 23:02
  • Your code is very confusing. Why do you use a focus event that then adds a click event handler. – Barmar Sep 04 '15 at 23:02
  • Why not just add the click handlers in the first place? – Barmar Sep 04 '15 at 23:02
  • Or use a `change` handler if you don't want it to be triggered if the user clicks on a button that's already checked. – Barmar Sep 04 '15 at 23:03
  • @Barmar that was my primary concern. I'm not sure I understand `change` handlers very well. Specifically I wasn't sure I could use `change` handlers with radio buttons or where I'd need to add them. Also, I agree that my code is very confusing; in fact I'm not even sure why my code even works in Chrome. I was having trouble figuring out how to subtract the last price selected in each category when making a new selection, and I stumbled on a solution that didn't really make sense to me, and I have no idea which part of the code I was adding made it work :/ – Jacob Smolowe Sep 04 '15 at 23:12
  • @JacobSmolowe If you don't want to include the entire jQuery library you could use the document.ondomcontentloaded event, but from what I've been reading that's not fully supported (mostly unsupported in older versions of IE). You could always do document.onload, but if you put images and stuff in the site that will delay the onload event. This is the one I've been reading, so see here for more info: http://stackoverflow.com/questions/3698200/window-onload-vs-document-ready – Ashelyn Dawn Sep 04 '15 at 23:16

1 Answers1

2

Firefox on the Mac doesn't trigger the focus event when you click on a radio button. I haven't researched whether this is a standard violation, but there's a simpler way to do what you're doing. Instead of using the focus event to add a click handler, just use the change event. This will be triggered whenever you click on a radio button that wasn't already checked. This can be bound directly to the addXXXPrice functions.

BTW, all your addXXXPrice functions are declared with a radio parameter that they never use. The actual argument to an event handler is an event object, not the radio button; the target of the event will be in this.

And all those addXXXPrice functions are identical, except for the set of buttons they loop over and the xxxPrice variable they update. I suggest you pull that out into a single function that takes those things as parameters, so you can do:

proPrice = addPrice(proPrice, myForm.proSpeed);

Here's the revised version of your code:

var myForm = document.form1;
var proPrice = 0;
var ramPrice = 0;
var stoPrice = 0;
var graPrice = 0;
var curPrice = 0;
var total = 2299;
var result = document.getElementById('result');
result.innerHTML = "$" + total + ".00";
//total = parseFloat(total).toFixed(2);

console.log(result);

function addProPrice(radio) {
  var radios = myForm.proSpeed;

  for (var index = 0; index < radios.length; index++) {
    if (radios[index].checked) {
      total = total - proPrice + parseInt(radios[index].value);
      proPrice = parseInt(radios[index].value);
      result.innerHTML = "$" + total + ".00";
      break;
    }
  }
}

for (var index = 0; index < myForm.proSpeed.length; index++) {
  myForm.proSpeed[index].addEventListener("change", addProPrice);
}

function addMemPrice(radio) {
  var radios = myForm.ram;

  for (var index = 0; index < radios.length; index++) {
    if (radios[index].checked) {
      total = total - ramPrice + parseInt(radios[index].value);
      ramPrice = parseInt(radios[index].value);
      result.innerHTML = "$" + total + ".00";
      break;
    }
  }
}

for (var index = 0; index < myForm.ram.length; index++) {
  myForm.ram[index].addEventListener("change", addMemPrice);
}

function addStoPrice(radio) {
  var radios = myForm.storage;

  for (var index = 0; index < radios.length; index++) {
    if (radios[index].checked) {
      total = total - stoPrice + parseInt(radios[index].value);
      stoPrice = parseInt(radios[index].value);
      result.innerHTML = "$" + total + ".00";
      break;
    }
  }
}

for (var index = 0; index < myForm.storage.length; index++) {
  myForm.storage[index].addEventListener("change", addStoPrice);
}

function addGraPrice(radio) {
  var radios = myForm.graphics;

  for (var index = 0; index < radios.length; index++) {
    if (radios[index].checked) {
      total = total - graPrice + parseInt(radios[index].value);
      graPrice = parseInt(radios[index].value);
      result.innerHTML = "$" + total + ".00";
      break;
    }
  }
}

for (var index = 0; index < myForm.graphics.length; index++) {
  myForm.graphics[index].addEventListener("change", addGraPrice);
}

function addCurPrice(radio) {
  var radios = myForm.cursorControl;

  for (var index = 0; index < radios.length; index++) {
    if (radios[index].checked) {
      total = total - curPrice + parseInt(radios[index].value);
      curPrice = parseInt(radios[index].value);
      result.innerHTML = "$" + total + ".00";
      break;
    }
  }
}

for (var index = 0; index < myForm.cursorControl.length; index++) {
  myForm.cursorControl[index].addEventListener("change", addCurPrice);
}
<form action="" name="form1" id="form1">
  <h1>iMac with Retina 5K display</h1>
  <p>1. Choose Processor</p>
  <p>
    <input type="radio" name="proSpeed" checked="checked" value="0" />
    <label>3.5GHz Quad-core Intel Core i5, Turbo Boost up to 3.9GHz</label>
    <br />
    <input type="radio" name="proSpeed" value="250" />
    <label>4.0GHz Quad-core Intel Core i7, Turbo Boost up to 4.4GHz</label>
  </p>
  <p>2. Choose Memory</p>
  <p>
    <input type="radio" name="ram" checked="checked" value="0" />
    <label>8GB 1600MHz DDR3 SDRAM - 2x4GB</label>
    <br />
    <input type="radio" name="ram" value="200" />
    <label>16GB 1600MHz DDR3 SDRAM - 2x8GB</label>
    <br />
    <input type="radio" name="ram" value="600" />
    <label>32GB 1600MHz DDR3 SDRAM - 4x8GB</label>
  </p>
  <p>3. Choose Storage</p>
  <p>
    <input type="radio" name="storage" checked="checked" value="0" />
    <label>1TB Fusion Drive</label>
    <br />
    <input type="radio" name="storage" value="150" />
    <label>3TB Fusion Drive</label>
    <br />
    <input type="radio" name="storage" value="0" />
    <label>256GB Flash Storage</label>
    <br />
    <input type="radio" name="storage" value="300" />
    <label>512GB Flash Storage</label>
    <br />
    <input type="radio" name="storage" value="800" />
    <label>1TB Flash Storage</label>
  </p>
  <p>4. Choose Graphics</p>
  <p>
    <input type="radio" name="graphics" checked="checked" value="0" />
    <label>AMD Radeon R9 M290X 2GB GDDR5</label>
    <br />
    <input type="radio" name="graphics" value="250" />
    <label>AMD Radeon R9 M295X 4GB GDDR5</label>
  </p>
  <p>5. Choose Mouse and Magic Trackpad</p>
  <p>
    <input type="radio" name="cursorControl" checked="checked" value="0" />
    <label>Apple Magic Mouse</label>
    <br />
    <input type="radio" name="cursorControl" value="0" />
    <label>Magic Trackpad</label>
    <br />
    <input type="radio" name="cursorControl" value="0" />
    <label>Apple Mouse</label>
    <br />
    <input type="radio" name="cursorControl" value="69" />
    <label>Apple Magic Mouse + Magic Trackpad</label>
    <br />
  </p>
  <p>6. Choose Apple Keyboard and Documentation</p>
  <p>
    <select name="keyboard" size="1">
      <option value="0" selected="selected">Apple Wireless Keyboard (English) &amp; User's Guide</option>
      <option value="0">Apple Wireless Keyboard (Arabic) &amp; User's Guide</option>
      <option value="0">Apple Wireless Keyboard (British) &amp; User's Guide</option>
    </select>
  </p>
</form>
<div id="result"></div>
Barmar
  • 741,623
  • 53
  • 500
  • 612
  • I can confirm that this works even in Safari5 for Win, which is probably the worst browser in history, so, it must work in all modern browsers (even on Mac). :) – sinisake Sep 04 '15 at 23:17
  • Thanks! Confirmed to work in Safari and Firefox for Mac. Now to comb through it and make sure I know _why_ it works (your explanation makes perfect sense, I just have to go through the process myself because I learn by doing). – Jacob Smolowe Sep 05 '15 at 01:08
  • @Barmar your answer was super-helpful. I really appreciate it. I've almost figured everything out now, just one more question I think—see update in original question above. Thanks again! – Jacob Smolowe Sep 05 '15 at 17:17
  • @JacobSmolowe `addPrice` needs a `return` statement with the price from the checked radio button. – Barmar Sep 05 '15 at 23:44
  • @Barmar, thanks. I couldn't figure out how to make the return statement work—not very good with those—but I came up with a different solution (in original post above). What do you think? – Jacob Smolowe Sep 07 '15 at 06:05
  • Since the function doesn't have a `return` statement, you're assigning `undefined` to `proPrice`. You need to put `return price` at the end of the function. – Barmar Sep 08 '15 at 03:12
  • You can also replace `break` with `return price`. – Barmar Sep 08 '15 at 03:12