0

I am working through a loan calculator example using HTML, CSS, Javascript, and the element from Javascript: The Definitive Guide (6th Edition.)

Here's the JSfiddle: http://goo.gl/PWTtLi

My issues:

1) The graph is not showing up. Why? 2) When I input numbers in the JSFiddle version, nothing happens when I click the "calculate" button. However the page will work when I open the HTML page on my computer. Why?

I've read the errata for the book. Admittedly the canvas code is beyond my current skill level, but I'm comfortable with the HTML/CSS/JavaScript. I hand coded the example from the book. JsHint/console.log gives no errors. Any thoughts?

HTML

<table>
    <tr>
        <th>Enter Loan Data:</th>
        <td></td>
        <th>Loan Balance, Cumulative Equity, and Interest Payments</th>
    </tr>
    <tr>
        <td>Amount of the loan ($):</td>
        <td>
            <input id="amount" onchange="calculate();">
        </td>
        <td rowspan=8>
            <canvas id="graph" width="400" height="250"></canvas>
        </td>
    </tr>
    <tr>
        <td>Annual interest (%):</td>
        <td>
            <input id="apr" onchange="calculate();">
        </td>
    </tr>
    <tr>
        <td>Repayment period (years):</td>
        <td>
            <input id="years" onchange="calculate();">
        </td>
        <tr>
            <td>Zipcode (to find lenders):</td>
            <td>
                <input id="zipcode" onchange="calculate();">
            </td>
            <tr>
                <th>Approximate Payments:</th>
                <td>
                    <button onclick="calculate();">Calculate</button>
                </td>
            </tr>
            <tr>
                <td>Monthly payment:</td>
                <td>$<span class="output" id="payment"></span>
                </td>
            </tr>
            <tr>
                <td>Total payment:</td>
                <td>$<span class="output" id="total"></span>
                </td>
            </tr>
            <tr>
                <td>Total interest:</td>
                <td>$<span class="output" id="totalinterest"></span>
                </td>
            </tr>
            <tr>
                <th>Sponsors:</th>
                <td colspan=2>Apply for your loan with one of these fine lenders:
                    <div id="lenders"></div>
                </td>
            </tr>
</table>

JS

function calculate() {
    // Look up the input and output elements in the 
    // document
    var amount = document.getElementById("amount");
    var apr = document.getElementById("apr");
    var years = document.getElementById("years");
    var zipcode = document.getElementById("zipcode");
    var payment = document.getElementById("payment");
    var total = document.getElementById("total");
    var totalinterest = document.getElementById("totalinterest");

    // Get the user's input from the input elements.  
    // Assume it is all valid.  Convert interest from 
    // a percentage to a decimal, and convert from an 
    // annual rate to a monthly rate.  Convert payment 
    // period in years to the number of monthly 
    // payments.

    var principal = parseFloat(amount.value);
    var interest = parseFloat(apr.value) / 100 / 12;
    var payments = parseFloat(years.value) * 12;

    // Now compute the monthly payment figure.
    var x = Math.pow(1 + interest, payments); // Math.pow() computes powers
    var monthly = (principal * x * interest) / (x - 1);

    //  If the result is a finite umber, the user's input was good and
    // we have meaningful results to display

    if (isFinite(monthly)) {
        // Fill in the output fields, rounding to 2 decimal places
        payment.innerHTML = monthly.toFixed(2);
        total.innerHTML = (monthly * payments).toFixed(2);
        totalinterest.innerHTML = ((monthly * payments) - principal).toFixed(2);

        // Save the user's input so we can restore it the next they visit
        save(amount.value, apr.value, years.value, zipcode.value);

        // Advertise: find and display local lenders, but ignore network errors
        try { // Catch any errors that occur within the curly braces
            // getLenders(amount.value, apr.value, years.value, zipcode.value);
        } catch (e) { /* And ignore those errors */
        }

        // Chat loan balance, and interest, and equity payments
        chart(principal, interest, monthly, payments);
    } else {
        // Reslt was Not-a-Number or infinite, which means the input was
        // incomplete or invalid.  Clear any peviously displayed output.
        payment.innterHTML = ""; // Erase the content of these elemts
        total.innerHTML = "";
        totalinterest.innerHTML = "";
        chart(); // With no arguments, clears the chart
    }

}

// Save the user's input as properties of the localStorage object.  Those
// properties will still be there when the user visits in the future
// This storage feature will not work in some browsers (Firefox, e.g.) if you
// run the example from a local file:// URL.  It does work over HTTP, however.
function save(amount, apr, years, zipcode) {
    if (window.localStorage) { // Only do this if the browser supports it
        localStorage.loan_amount = amount;
        localStorage.loan_amount = apr;
        localStorage.loan_amount = years;
        localStorage.loan_amount = zipcode;
    }
}

// Automatically attempt to restore input fields when the document first loads.
window.onload = function () {
    //If the browser supports localStorage and we have some stored data
    if (window.localStorage && localStorage.loan_amount) {
        document.getElementbyId("amount").value = localStorage.loan_amount;
        document.getElementbyId("apr").value = localStorage.loan_apr;
        document.getElementbyId("years").value = localStorage.loan_years;
        document.getElementbyId("zipcode").value = localStorage.loan_zipcode;
    }
};

//Chart montly loan balance, interest and equity in an HTML <canvas> element.
//  If called with no arguments then just erase any previously drawn chart.
function chart(principal, interest, monthly, payments) {
    var graph = document.getElementById("graph"); // Get the <canvas> tag
    graph.width = graph.width; // Magic to clear and reset the canvas elemtn

    // If we're called with no arguments, or ig this browser does not support
    // graphics in a <canvas> elemtn, then just return now.
    if (arguments.length === 0 || !graph.getContect) return;

    // Get the "context" object for the <canvas> that defines the drawing API
    var g = graph.getContext("2d"); // All drawing is done with this object
    var width = graph.width,
        height = graph.height; //Get canvas size

    // These functions convert payment numbers and dollar amounts to pixels
    function paymentToX(n) {
        return n * width / payments;
    }

    function amountToY(a) {
        return height - (a * height / (monthly * payments * 1.05));
    }

    // Payments are a straight line from (0,0) to (payments, monthly*payments)
    g.moveTo(paymentToX(0), amountToY(0)); // Start at lower left
    g.lineTo(paymentToX(payments), // Draw to upper right
    amountToY(monthly * payments));
    g.lineTo(paymentToX(payments), amountToY(0)); // Down to lower right
    g.closePath(); // And back to start
    g.fillStyle = "#f88"; // Light red
    g.fill(); // Fill the triangle
    g.font = "bold 12px sans-serif"; // Define a font
    g.fillText("Total Interest Payments", 20, 20); // Draw text in legend

    //  Vumulative equity is non-linear and trickier to chart
    var equity = 0;
    g.beginPath(); // Begin a new shape
    g.moveTo(paymentToX(0), amountToY()); // starting at lower-left
    for (var p = 1; p <= payments; p++) {
        // For each payment, figure out how much is interest
        var thisMonthsInterest = (principal - equity) * interest;
        equity += (monthly - thisMonthsInterest); // The rest goes to equity
        g.lineTo(paymentToX(p), amountToY(equity)); //Line to this point
    }
    g.lineTo(paymentToX(payments), amountToY(0)); // Line back to X axis
    g.closePath(); // And back to start point
    g.fillStyle = "green"; // Now use green paint
    g.fill(); // And fill area under curve
    g.fillText("Total Equity", 20, 35); // Label it in green

    // Loop again, as above, but chart loan balance as a thick black line

    var bal = principal;
    g.beginPath();
    g.moveTo(paymentToX(0), amountToY(bal));
    for (var p = 1; p <= payments; p++) {
        var thisMonthsInterest = bal * interest;
        bal += (monthly - thisMonthsInterest); // The rest goes to equity
        g.lineTo(paymentToX(p), amountToY(bal)); // Draw line to this point
    }
    g.lineWidth = 3; //  Use a thick line
    g.stroke(); //  Deaw the balance curve
    g.fillStyle = "black"; //  Switch to black text
    g.fillText("Loan Balance", 20, 50); //  Legend entry

    // Now make a yearly tick marks and year numbers on X acis
    g.textAlign = "center"; // Center text over ticks
    var y = amountToY(0); // Y coordinate of X axis
    for (var year = 1; year * 12 <= payments; year++) { // For each year
        var x = paymentToX(year * 12); // Compute tick position
        g.fillRect(x - 0.5, y - 3, 1, 3); // Draw the tick
        if (year == 1) g.fillText("Year", x, y - 5); // Label the axis
        if (year % 5 === 0 && year * 12 !== payments) // Number every 5 years
        g.fillText(String(year), x, y - 5);

    }

    // Mark payment amounts along the right edge
    g.textAlign = "right"; //Right=justify text
    g.textBaseline = "middle"; // Center it vertically
    var ticks = [monthly * payments, principal]; // The two points we'll mark
    var rightEdge = paymentToX(payments); //X coordinate of Y axis
    for (var i = 0; i < ticks.length; i++) { // for each of the 2 points
        var y = amountToY(ticks[i]); // Compute Y position of tick
        g.fillRect(rightEdge - 3, y - 0.5, 3, 1); //Draw the tick mark
        g.fillText(String(ticks[i].toFixed(0)), //And label it.
        rightEdge - 5, y);
    }
}
Paul
  • 26,170
  • 12
  • 85
  • 119
IP Address
  • 15
  • 2
  • 6

1 Answers1

0

This is a common jsfiddle issue. See Inline event handler not working in JSFiddle

In your case, the JS Error Console prints a message that calculate is not defined whenever the button is pressed.

If you change the Javascript portion of the JSFIDDLE to explicitly define window.calculate, it will work.

Like this:

window.calculate = function(){

Instead of function calculate(){...

http://jsfiddle.net/nqb4wpnr/3/

Community
  • 1
  • 1
Paul
  • 26,170
  • 12
  • 85
  • 119