一个JavaScript贷款计算器
一向不喜读书的我最近打算重修JavaScript基础,不得已只能拿起死厚死厚的JavaScript权威指南第六版,走向一条不归路~中途总得留下点印记吧,从今天开始为了让自己坚持不懈的读下这本大师之作,打算每读完一节儿写点总结,唠点收获。OK,走起!
代码展示了许多JS语言和新特性,同样展示了重要的客户端JS技术:
-
如何在文档中查找元素,如
var amount=document.getElementById("amount");
-
如何通过表单input元素来获取用户的输入数据,如
document.getElementById("amount").value
-
如何通过文档元素来设置HTML内容,如
document.getElementById("amount").value=localStorage.loan_amount;
-
如何将数据存储在浏览器中
localStorage.loan_amount=amount.value
-
如何使用脚本发起HTTP请求,如
var req=new XMLHttpRequest();
- 如何利用<canvas>元素绘图
-
g.moveTo(paymentToX(0),amountToY(0)); g.lineTo(paymentToX(payments),amountToY(monthly*payments));
粘贴一下我的成果
<!DOCTYPE html>
<meta charset="UTF-8">
<title>JavaScript贷款计算器</title>
<style>/*这是一个CSS样式表:定义了程序输出的样式*/
.output{font-weight: bold;} /*计算结果定义为粗体*/
#payment{text-decoration: underline;}/*定义id="payment"的元素样式*/
#graph{border: solid black 1px;}/*图标有一像素的边框*/
th,td{vertical-align: top;}/*表格单元格对齐方式为顶端对齐*/
</style>
</head>
作者:2538300535@qq.com
时间:2017-03-09
描述:这是一个HTML表格,其中包含<input>元素允许用户输入数据,程序将在<span>元素中显示其计算结果。这些元素有其id名,例如“利益”、“年”,在接下来的JavaScript代码中使用这些id。注意的一些输入元素定义“onchange”或“onclick事件处理程序。当用户输入数据或点击,这些指定的字符串执行的JavaScript代码。
<table>
<th>输入贷款数据:</th>
<td></td>
<th>贷款余额、累计股票和利息支付</th>
<td>贷款的金额 ($):</td>
<input id="amount" onchange="calculate();">
<td rowspan=8>
<canvas id="graph" width="400" height="250"></canvas>
<td>年息 (%):</td>
<input id="apr" onchange="calculate()">
<td>还款期(年):</td>
<input id="years" onchange="calculate();">
<td>邮编 (找到银行):</td>
<input id="zipcode" onchange="calculate();">
<th>近似的支付:</th>
<button onclick="calculate();">计算</button>
<td>月付款数:</td>
<td>$<span class="output" id="payment"></span></td>
<td>付款总额:</td>
<td>$<span class="output" id="total"></span></td>
<td>总利息:</td>
<td>$<span class="output" id="totalinterest"></span></td>
<th>赞助商:</th>
<td colspan=2>
申请贷款,这些优良的银行之一:
<div id="lenders"></div>
</table>
作者:2538300535@qq.com
时间:2017-03-09
描述:1.随后是JavaScript代码,这些代码内嵌在一个<script>标签里
2.通常情况下,这些脚本代码应当放在<head>标签中
3.将JavaScript代码放在HTML代码之后仅仅是为了便于理解
<script>
"user strict"; //如果浏览器支持的话,则开启ECMAScript5的严格模式
* 这里的脚本定义了calculate()函数,在HTML代码中绑定事件处理程序时会调用它
* 这个函数从<input>元素中读取数据,计算贷款赔付信息,并将结果显示在<span>元素中
* 同样,这里还保存了用户数据,展示了放贷人链接并绘制出了图标
function calculate(){
//查找文档中用于输入输出的元素
var amount=document.getElementById("amount");
var apr=document.getElementById("apr");
var year=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(year.value)*12;
//现在计算月度赔付的数据
var x=Math.pow(1+interest,payments);//Math.pow()进行幂次运算
var monthly=(principal*x*interest)/(x-1)
//如果结果没有通过JavaScipt能表示的数字范围,且用户的输入也正确
//这里所展示的结果就是合法的
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对象的属性中
//这些属性在再次访问时还会继续保持在原位置
//如果你在浏览器中按照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 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数组
//将数组中的放贷人对象转换为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<canvas>元素中用图标展示月度贷款余额,利息和资产收益
//如果不传入参数的话,则清空之前的图标数据
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 san-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轴
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)//绘制文本