11111111
<!DOCTYPE html> <html> <head> <title>JavaScript Loan Calculator</title> <style> /* 这是一个CSS样式:定义了程序输出的样式 */ .output {font-weight: bold;} /* 计算结果,定义为粗体 */ #payment {text-decoration: underline;} /* 定义id="payment"的元素样式 */ #graph {border: solid black 1px;} /* 定义id="graph"的图表边框为1px */ th,td {vertical-align: left;} /* 定义表格单元格对其方式,为左对齐 */ </style> </head> <body> <!-- PDF的文档在29页 这是一个HTML表格,其中包含的<input>元素,可以用来输入数据 程序将在<span>元素中显示计算结果,这些元素都具有类似"interset"和"years"的id 这些id将在表格下面的JavaScript代码中用到。我们也应注意到,有一些input元素 定义了“onchange”或“onclick”的事件处理程序,以便用户在输入数据或点击input时 执行指定的JavaScript代码段 --> <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="450" height="250"></canvas> </td> </tr> <tr> <td>Annual interset (%):</td> <td><input id="apr" onchange="calculate();"></td> </tr> <tr> <td>Repayment period (years):</td> <td><input id="years" onchange="calculate();"></td> </tr> <tr> <td>Zipcode (to find lenders):</td> <td><input id="zipcode" onchange="calculate();"></td> </tr> <tr> <td>Approximate Payments:</td> <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> <td>Sponsors:</td> <td colspan=2> Apply for your loan with one of these fine lenders: <div id="lenders"> </td> </tr> </table> <!-- 随后是JavaScript代码, 这些代码内嵌在了一个<script>标签里面 page30 --> <!-- 通常情况下,这些脚本代码应当放在<head>标签中 --> <!-- 将JavaScript代码放HTML代码之后仅仅为了便于理解 --> <script> "use strict"; //如果浏览器支持的话,则开户ECMAScript 5的严格模式 /* * 这里的脚本定义了caculate()函数, 在HTML代码中绑定事件处理程序时会调用它 * 这个函数从<input>元素中读取数据,计算代款赔付信息,并将结果信息显示在<span>元素中 * 同样,这里还保存了用户数据、展示了放贷人链接并绘制出了图表 */ function calculate(){ // 查找文档中用于输入输出的元素 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"); // 假设所有的输入都是合法的, 将从input元素中获取输入数据 // 将百分比格式转换为小数格式, 并从年利率转换为月利率 // 将年度赔付转换为月度赔付 var principal = parseFloat(amount.value); var interest = parseFloat(apr.value) / 100 / 12; var payments = parseFloat(years.value) * 12; // 现在计算月度赔付的数据 var x = Math.pow(1 + interest, payments); // Math.pow() 进行幂次运算 var monthly = (principal * x * interest) / (x - 1); // 如果结果没有超过JavaScript能表示的数字范围,且用户的输入也正确 // 这里所展示的结果就是合法的 if (isFinite(monthly)){ // 将数据填充至输出字段的位置,四舍五入到小数点后两位数字 payment.innerHTML = monthly.toFixed(2); total.innerHTML = (monthly * payments).toFixed(2); totalinterest.innerHTML =((monthly * payments) - principal).toFixed(2); // 将用户的输入数据保存下来, 这样下次访问时也能取到数据 save(amount.value, apr.value, years.value, zipcode.value); // 找到并展示本地放贷人, 但忽略网络错误 try{ getLenders(amount.value, apr.value, years.value, zipcode.value); } catch(e){ // 忽略这些异常 } // 最后, 用图表展示贷款余额、利息和资产收益 chart(principal, interest, monthly, payments); } else { // 计算结果不是数字或者无穷大, 意味着输入数据是非法或不完整的 // 清空之前的输出数据 payment.innerHTML = ""; // 清空元素的文本内容 total.innerHTML = ""; totalinterest.innerHTML = ""; // 不传参数的话就是清除图表 chart(); } } // 将用户的输入保存至localStorage对象的属性中 page31 // 这些属性在再次访问时还会继续保持在原位置 // 如果你在浏览器中,按照file://URL的方式直接打开本地文件 // 则无法在某些浏览器中使用存储功能(比如FireFox) // 而通过HTTP打开文件是可行的 function save(amount, apr, years, zipcode){ if(window.localStorage){ // 只有在浏览器支持的时候才运行这里的代码 localStorage.loan_amount = amount; localStorage.loan_apr = apr; localStorage.loan_years = years; localStorage.loan_zipcode = zipcode; } } // 在文档首次加载时,将会尝试还原输入字段 window.onload = function(){ 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; } }; // 将用户的输入发送至服务器端脚本,(理论上)将返回一个本地放贷人的链接列表, // 在这个例子并没有实现这种查找放贷人的服务 // 但如果该服务存在, 该函数会使用它 function getLenders(amount, apr, years, zipcode){ // 如果浏览器不支持XMLHttpRequest对象, 则退出 if (!window.XMLHttpRequest) return; // 找到要显示放贷人列表的元素 var ad = document.getElementById("lenders"); if (!ad) return; // 如果返回为空, 则退出 // 将用户的输入数据进行URL编码, 并作为查询参数附加在URL里 var url = "getLenders.php" + // 处理数据的URL地址 "?amt=" + encodeURIComponent(amount) + // 使用查询串中的数据 "&apr=" + encodeURIComponent(apr) + "&yrs=" + encodeURIComponent(years) + "&zip=" + encodeURIComponent(zipcode) ; // 通过XMLHttpRequest对象来提取返回数据 var req = new XMLHttpRequest(); // 发起一个新的请求 req.open("GET", url); // 通过URL发起一个HTTP GET请求 req.send(null); //不带任何正文发送这个请求 // 在返回数据之前, 注册了一个事件处理函数, 这个处理函数 // 将会在服务器的响应返回至客户端的时候调用 // 这种异步编程框在客户端JavaScript中是非常常见的 req.onreadystatechange = function(){ if (req.readyState == 4 && req.status == 200){ // 如果代码运行到这里,说明我们得到了一个合法且完整的HTTP响应 var response = req.responseText; // HTTP响应是以字符串的形式呈现的 var lenders = JSON.parse(response); // 将其解析为JS数组 -- page32 // 将数组中的放贷人对象转换为HTML字符串形式 var list = ""; for (var i=0; i<lenders.length; i++){ list += "<li><a href='" + lenders[i].url + "'>" + lenders[i].name + "</a>"; } // 将数据在HTML元素中呈现出来 ad.innerHTML = "<ul>" + list + "</ul>" } } } // 在HTML<cannas>元素中用图表展示月度贷款余额、利息和资产收益 // 如果不传入参数的话,则清空之前的图表数据 function chart(principal, interest, monthly, Payments){ var graph = document.getElementById("graph"); // 得到<canvas>标签 graph.width = graph.width; // 用一种巧妙的方法清除并重置画布 // 如果不传入参数,或者浏览器不支持画布,则直接返回 if (arguments.length == 0 || graph.getContext) return; // 获得画布元素的"context"对象,这个对象定义了一组绘画API var g = graph.getContext("2d"); // 所有的绘画操作都将基于这个对象 var width = graph.width; height = graph.height; // 这里的函数作用是将付款数字和美元转换为像素 function paymentToX(n){ return n * width / payments; } function amountToY(a){ return height - (a * height / (monthly * payments * 1.05)); } // 付款数据是一条从(0,0)到(payments, monthly*payments) 的直线 g.moveTo(paymentToX(0), amountToY(0)); // 从左下方开始 g.lineTo(paymentToX(payments), amountToY(monthly * payments)); // 绘至右上方 g.lineTo(paymentToX(payments), amountToY(0)); // 再至右下方 g.closePath(); // 将结尾连接至开头 g.fillStyle = "#f88"; // 亮红色 g.fill(); g.font = "bold 12px sans-serif"; // 定义一种字体 g.fillText("Total Interest Payments", 20, 20); // 将文字绘制到图例中 // 很多资产数据并不是线性的, 很难将其反映至图表中 var equity = 0; g.beginPath(); // 开始绘制新图形 g.moveTo(paymentToX(0), amountToY(0)); // 从左下方开始 for (var p=1; p<=payments; p++){ // 计算出每一笔赔付的利息 var thisMonthsInterest = (principal - equity) * interest; equity += (monthly - thisMonthsInterest); // 得到资产额 g.lineTo(paymentToX(p), amountToY(equity)); // 将数据绘制画布上 } g.lineTo(paymentToX(payments), amountToY(0)); // 将数据线绘制至X轴 page33 g.closePath(); // 将线条结尾连接至线条开头 g.fillStyle = "green"; // 使用绿色绘制图形 g.fill(); // 曲线之下的部分均填充 g.fillText("Total Equity", 20, 35); // 再次循环, 余额数据显示为黑色粗线条 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); // 得到资产额 g.lineTo(paymentToX(p), amountToY(bal)); // 数据连接至某点 } g.lineWidth = 3; // 将直线宽度加粗 g.stroke(); // 绘制余额的曲线 g.fillStyle = "black"; // 使用黑色字体 g.fillText("Loan Balance", 20, 50); //图例文字 // 将年度数据在X轴做标记 g.textAlign = "center"; // 文字居中对齐 var y = amountToY(0); // Y坐标设为0 for (var year=1; year*12 <= payments; year++){ // 遍历每年 var x = paymentToX(year * 12); // 计算标记位置 g.fillRect(x-0.5, y-3, 1, 3); // 开始绘制标记 if (year==1) { g.fillText("Year", x, y-5); // 在坐标轴标记 } if (year%5 == 0 && year*12 !== payments){ // 每5年的数据 g.fillText(String(year), x, y-5); } } // 将赔付数额标记在右边界 g.textAlign = "right"; // 文字右对齐 g.textBaseline = "middle"; // 文字垂直居中 var ticks = [monthly * payments, principal]; // 我们将要用的两个点 var rightEdge = paymentToX(payments); // 设置X坐标 for (var i=0; i<ticks.length; i++){ // 对每两个点做循环 var y = amountToY(ticks[i]); // 计算每个标记的Y坐标 g.fillRect(rightEdge-3, y-0.5, 3, 1); // 绘制标记 g.fillText(String(ticks[i].toFixed(0)), rightEdge-5, y); // 绘制文本 } } </script> </body> </html>
222222