任务十七:零基础JavaScript编码(五)
任务目的
- 在上一任务基础上继续JavaScript的体验
- 接触更加复杂的表单对象
- 实现页面上的一个完整交互功能
- 用DOM实现一个柱状图图表
任务描述
- 参考以下示例代码,原始数据包含几个城市的空气质量指数数据
- 用户可以选择查看不同的时间粒度,以选择要查看的空气质量指数是以天为粒度还是以周或月为粒度
- 天:显示每天的空气质量指数
- 周:以自然周(周一到周日)为粒度,统计一周7天的平均数为这一周的空气质量数值,如果数据中缺少一个自然周的几天,则按剩余天进行计算
- 月:以自然月为粒度,统一一个月所有天的平均数为这一个月的空气质量数值
- 用户可以通过select切换城市
- 通过在"aqi-chart-wrap"里添加DOM,来模拟一个柱状图图表,横轴是时间,纵轴是空气质量指数,参考图(点击打开)。天、周、月的数据只根据用户的选择显示一种。
- 天:每天的数据是一个很细的矩形
- 周:每周的数据是一个矩形
- 月:每周的数据是一个很粗的矩形
- 鼠标移动到柱状图的某个柱子时,用title属性提示这个柱子的具体日期和数据
task.html
<!DOCTYPE>
<html>
<head>
<meta charset="utf-8">
<title>IFE JavaScript Task 01</title>
<script src="task.js"></script>
</head>
<body>
<fieldset id="form-gra-time">
<legend>请选择日期粒度:</legend>
<label>日<input name="gra-time" value="day" type="radio" checked="checked"></label>
<label>周<input name="gra-time" value="week" type="radio"></label>
<label>月<input name="gra-time" value="month" type="radio"></label>
</fieldset> <fieldset>
<legend>请选择查看的城市:</legend>
<select id="city-select">
<option>北京</option>
</select>
</fieldset> <div class="aqi-chart-wrap">
</div>
</body>
</html>
task.js
/* 数据格式演示
var aqiSourceData = {
"北京": {
"2016-01-01": 10,
"2016-01-02": 10,
"2016-01-03": 10,
"2016-01-04": 10
}
};
*/ // 以下两个函数用于随机模拟生成测试数据
function getDateStr(dat) {
var y = dat.getFullYear();
var m = dat.getMonth() + 1;
m = m < 10 ? '0' + m : m;
var d = dat.getDate();
d = d < 10 ? '0' + d : d;
return y + '-' + m + '-' + d;
}
function randomBuildData(seed) {
var returnData = {};
var dat = new Date("2016-01-01");
var datStr = ''
for (var i = 1; i < 92; i++) {
datStr = getDateStr(dat);
returnData[datStr] = Math.ceil(Math.random() * seed);
dat.setDate(dat.getDate() + 1);
}
return returnData;
} var aqiSourceData = {
"北京": randomBuildData(500),
"上海": randomBuildData(300),
"广州": randomBuildData(200),
"深圳": randomBuildData(100),
"成都": randomBuildData(300),
"西安": randomBuildData(500),
"福州": randomBuildData(100),
"厦门": randomBuildData(100),
"沈阳": randomBuildData(500)
}; // 用于渲染图表的数据
var chartData = {}; // 记录当前页面的表单选项
var pageState = {
nowSelectCity: -1,
nowGraTime: "day"
} /**
* 渲染图表
*/
function renderChart() { } /**
* 日、周、月的radio事件点击时的处理函数
*/
function graTimeChange() {
// 确定是否选项发生了变化 // 设置对应数据 // 调用图表渲染函数
} /**
* select发生变化时的处理函数
*/
function citySelectChange() {
// 确定是否选项发生了变化 // 设置对应数据 // 调用图表渲染函数
} /**
* 初始化日、周、月的radio事件,当点击时,调用函数graTimeChange
*/
function initGraTimeForm() { } /**
* 初始化城市Select下拉选择框中的选项
*/
function initCitySelector() {
// 读取aqiSourceData中的城市,然后设置id为city-select的下拉列表中的选项 // 给select设置事件,当选项发生变化时调用函数citySelectChange } /**
* 初始化图表需要的数据格式
*/
function initAqiChartData() {
// 将原始的源数据处理成图表需要的数据格式
// 处理好的数据存到 chartData 中
} /**
* 初始化函数
*/
function init() {
initGraTimeForm()
initCitySelector();
initAqiChartData();
} init();
任务注意事项
- 实现简单功能的同时,请仔细学习JavaScript基本语法、事件、DOM相关的知识
- 请注意代码风格的整齐、优雅
- 代码中含有必要的注释
- 示例图仅为参考,不需要完全一致
- 点击select或者radio选项时,如果没有发生变化,则图表不需要重新渲染
- 建议不使用任何第三方库、框架
- 示例代码仅为示例,可以直接使用,也可以完全自己重写
任务完成与总结:
我看不懂的JS代码:
// 以下两个函数用于随机模拟生成测试数据
function getDateStr(dat) {
var y = dat.getFullYear();
var m = dat.getMonth() + 1;
m = m < 10 ? '0' + m : m;
var d = dat.getDate();
d = d < 10 ? '0' + d : d;
return y + '-' + m + '-' + d;
} function randomBuildData(seed) {
var returnData = {};
var dat = new Date("2016-01-01");
var datStr = '';
for (var i = 1; i < 92; i++) {
datStr = getDateStr(dat);
returnData[datStr] = Math.ceil(Math.random() * seed);
dat.setDate(dat.getDate() + 1);
}
return returnData;
} var aqiSourceData = {
"北京": randomBuildData(500),
"上海": randomBuildData(300),
"广州": randomBuildData(200),
"深圳": randomBuildData(100),
"成都": randomBuildData(300),
"西安": randomBuildData(500),
"福州": randomBuildData(100),
"厦门": randomBuildData(100),
"沈阳": randomBuildData(500)
}; var colors = ['#16324a', '#24385e', '#393f65', '#4e4a67', '#5a4563', '#b38e95',
'#edae9e', '#c1b9c2', '#bec3cb', '#9ea7bb', '#99b4ce', '#d7f0f8']; // 用于渲染图表的数据
var chartData = {}; // 记录当前页面的表单选项
var pageState = {
nowSelectCity: -1,
nowGraTime: "day"
} function getWidth(width, len) {
var posObj = {};
posObj.width = Math.floor(width / (len*2));
posObj.left = Math.floor(width / len);
posObj.offsetLeft = (width - posObj.left * (len - 1) - posObj.width) / 2;
return posObj;
} function getHintLfeft(posObj, i){
if (posObj.left * i + posObj.offsetLeft + posObj.width / 2 - 60 <= 0) {
return 5;
} else if (posObj.left * i + posObj.offsetLeft + posObj.width / 2 + 60 >= 1200) {
return (posObj.left * i + posObj.offsetLeft + posObj.width / 2 - 110);
} else {
return (posObj.left * i + posObj.offsetLeft + posObj.width / 2 - 60);
}
} function getTitle() {
switch (pageState.nowGraTime) {
case "day":
return "每日";
case "week":
return "周平均";
case "month":
return "月平均";
}
} /**
* addEventHandler方法
* 跨浏览器实现事件绑定
*/
function addEventHandler(ele, event, hanlder) {
if (ele.addEventListener) {
ele.addEventListener(event, hanlder, false);
} else if (ele.attachEvent) {
ele.attachEvent("on"+event, hanlder);
} else {
ele["on" + event] = hanlder;
}
} /**
* 渲染图表
*/
function renderChart() {
var innerHTML = "", i = 0;
var wrapper = document.getElementById("aqi-chart-wrap");
var width = wrapper.clientWidth;
var selectedData = chartData[pageState.nowGraTime][pageState.nowSelectCity];
var len = Object.keys(selectedData).length;
var posObj = getWidth(width, len);
innerHTML += "<div class='title'>" + pageState.nowSelectCity + "市01-03月"+ getTitle() +"空气质量报告</div>"
for (var key in selectedData) {
innerHTML += "<div class='aqi-bar " + pageState.nowGraTime + "' style='height:" + selectedData[key] + "px; width: " + posObj.width +"px; left:" + (posObj.left * i + posObj.offsetLeft) + "px; background-color:" + colors[Math.floor(Math.random() * 11)] + "'></div>"
innerHTML += "<div class='aqi-hint' style='bottom: " + (selectedData[key] + 10) + "px; left:" + getHintLfeft(posObj, i++) + "px'>" + key + "<br/> [AQI]: " + selectedData[key] + "</div>"
}
wrapper.innerHTML = innerHTML;
} /**
* 日、周、月的radio事件点击时的处理函数
*/
function graTimeChange(radio) {
// 确定是否选项发生了变化
var value = radio.value;
var item = radio.previousElementSibling;
var items = document.getElementsByTagName('span');
for (var i = 0; i < items.length; i++) {
items[i].className = "";
}
item.className = "selected";
if (value !== pageState.nowGraTime) {
// 设置对应数据
pageState.nowGraTime = value;
// 调用图表渲染函数
renderChart();
}
} /**
* select发生变化时的处理函数
*/
function citySelectChange() {
// 确定是否选项发生了变化
var city = this.value;
if (city !== pageState.nowSelectCity) {
// 设置对应数据
pageState.nowSelectCity = city;
// 调用图表渲染函数
renderChart();
}
} /**
* 初始化日、周、月的radio事件,当点击时,调用函数graTimeChange
*/
function initGraTimeForm() {
var radio = document.getElementsByName('gra-time');
for (var i = 0; i < radio.length; i++) {
(function (m) {
addEventHandler(radio[m], 'click', function () {
graTimeChange(radio[m])
})
})(i);
}
addEventHandler(document, 'mouseover', function(event){
var ele = event.target;
ele.className += " show";
});
addEventHandler(document, 'mouseout', function(event){
var ele = event.target;
ele.className = ele.className.replace(/show/, "");
});
} /**
* 初始化城市Select下拉选择框中的选项
*/
function initCitySelector() {
// 读取aqiSourceData中的城市,然后设置id为city-select的下拉列表中的选项
var select = document.getElementById("city-select");
var cityArr = Object.getOwnPropertyNames(aqiSourceData);
var htmlArr = cityArr.map(function(item) {
return "<option>" + item + "</option>";
});
pageState.nowSelectCity = cityArr[0];
select.innerHTML = htmlArr.join("");
// 给select设置事件,当选项发生变化时调用函数citySelectChange
addEventHandler(select, 'change', citySelectChange); } /**
* 初始化图表需要的数据格式
*/
function initAqiChartData() {
// 将原始的源数据处理成图表需要的数据格式
var week = {}, count = 0, singleWeek = {},
month = {}, mcount = 0, singleMonth = {}; for (var key in aqiSourceData) {
var tempCity = aqiSourceData[key]
var keyArr = Object.getOwnPropertyNames(tempCity);
var tempMonth = keyArr[0].slice(5, 7);
var weekInit = 4, weekCount = 0;
for (var i = 0; i < keyArr.length; i++, weekInit++) {
count += tempCity[keyArr[i]];
mcount += tempCity[keyArr[i]];
weekCount++;
if ((weekInit+1) % 7 == 0 || i == keyArr.length - 1 || keyArr[i+1].slice(5, 7) !== tempMonth) {
var tempKey = keyArr[i].slice(0, 7) + "月第" + (Math.floor(weekInit / 7) + 1) + "周";
singleWeek[tempKey] = Math.floor(count / weekCount); if (i != keyArr.length - 1 && keyArr[i+1].slice(5, 7) !== tempMonth) {
weekInit = weekCount % 7;
}
count = 0;
weekCount = 0; if (i == keyArr.length - 1 || keyArr[i+1].slice(5, 7) !== tempMonth) {
tempMonth = (i == keyArr.length - 1) ? keyArr[i].slice(5, 7) : keyArr[i+1].slice(5, 7);
var tempMKey = keyArr[i].slice(0, 7);
var tempDays = keyArr[i].slice(-2);
singleMonth[tempMKey] = Math.floor(mcount / tempDays);
mcount = 0;
}
}
}
week[key] = singleWeek;
month[key] = singleMonth;
singleWeek = {};
singleMonth = {};
}
// 处理好的数据存到 chartData 中
chartData.day = aqiSourceData;
chartData.week = week;
chartData.month = month;
renderChart();
} /**
* 初始化函数
*/
function init() {
initGraTimeForm();
initCitySelector();
initAqiChartData();
} init()
到了这步,有点小绝望,心里有些打击。看来得继续加油,好好加油!
任务十七:零基础JavaScript编码(五)的更多相关文章
- 零基础JavaScript编码(三)总结
任务目的 在上一任务基础上继续JavaScript的体验 接触一下JavaScript中的高级选择器 学习JavaScript中的数组对象遍历.读写.排序等操作 学习简单的字符串处理操作 任务描述 参 ...
- 零基础JavaScript编码(二)
任务目的 在上一任务基础上继续JavaScript的体验 学习JavaScript中的if判断语法,for循环语法 学习JavaScript中的数组对象 学习如何读取.处理数据,并动态创建.修改DOM ...
- 零基础JavaScript编码(一)
任务目的 JavaScript初体验 初步明白JavaScript的简单基本语法,如变量.函数 初步了解JavaScript的事件是什么 初步了解JavaScript中的DOM是什么 任务描述 参考以 ...
- 任务十五:零基础JavaScript编码(三)
任务目的 在上一任务基础上继续JavaScript的体验 接触一下JavaScript中的高级选择器 学习JavaScript中的数组对象遍历.读写.排序等操作 学习简单的字符串处理操作 任务描述 参 ...
- 任务十四:零基础JavaScript编码(二)
任务目的 在上一任务基础上继续JavaScript的体验 学习JavaScript中的if判断语法,for循环语法 学习JavaScript中的数组对象 学习如何读取.处理数据,并动态创建.修改DOM ...
- 任务十六:零基础JavaScript编码(四)
任务目的 在上一任务基础上继续JavaScript的体验 深入学习JavaScript的事件机制及DOM操作 学习事件代理机制 学习简单的表单验证功能 学习外部加载JavaScript文件 任务描述 ...
- 任务十三:零基础JavaScript编码(一)
任务目的 JavaScript初体验 初步明白JavaScript的简单基本语法,如变量.函数 初步了解JavaScript的事件是什么 初步了解JavaScript中的DOM是什么 任务描述 参考以 ...
- salesforce 零基础学习(五十二)Trigger使用篇(二)
第十七篇的Trigger用法为通过Handler方式实现Trigger的封装,此种好处是一个Handler对应一个sObject,使本该在Trigger中写的代码分到Handler中,代码更加清晰. ...
- 任务一:零基础HTML编码
面向人群: 零基础或初学者 难度: 简单 重要说明 百度前端技术学院的课程任务是由百度前端工程师专为对前端不同掌握程度的同学设计.我们尽力保证课程内容的质量以及学习难度的合理性,但即使如此,真正决定课 ...
随机推荐
- apache的应用(发布目录,黑白名单,虚拟主机,PHP-cgi支持,正向代理,https加密,)
[root@apache1 ~]# yum install httpd -y [root@apache1 ~]# cd /var/www/html/ 进入默认发布目录 [root@apache1 ...
- Http Header之User-Agent
Http Header之User-Agent User-Agent中文名为用户代理,简称 UA,它是一个特殊字符串头.通过这个标识,用户所访问的网站可以显示不同的排版从而为用户提供更好的体验或者进行信 ...
- 文献综述十八:基于SSH框架的进销存管理系统设计与实现
一.基本信息 标题:基于SSH框架的进销存管理系统设计与实现 时间:2017 出版源:内蒙古科技与经济 文件分类:对框架的研究 二.研究背景 进销存管理系统在各企业中广泛应用,使用SSH框架,很大程度 ...
- mysql 必知必会总结
以前 mysql 用的不是很多, 2 天看了一遍 mysql 必知必会又复习了一下基础. 200 页的书,很快就能看完, 大部分知识比较基础, 但还是了解了一些以前不知道的知识点.自己做一个备份,随 ...
- pointer-events属性值详解
其实早知道这个属性,但是一直没有去研究过.今天正好在twitter看到这个词,就去研究了下,正好解决了目前遇到的一个小难题,所以分享下.嗯,其实这是个比较简单的CSS3属性. 在某个项目中,很多元素需 ...
- oracle insert两个关联表
现有一张老师学生表(tb_tea_cou),由于业务需要,需把老师学生表tb_tea_stu拆分成两张表(tb_tea.tb_cou),并把记录insert到这两张子表中(tb_tea.tb_cou为 ...
- Log4j 2.0读取配置文件的方法
log4j中配置日志文件存放的位置不一定在src下面,即根目录下.这个时候我们需要解决如何加载配置文件的问题.在log4j1.x中解决的方法就比较多了.如:PropertyConfigurator.c ...
- 如何优雅的封装一个DOM事件库
1.DOM0级事件和DOM2级事件 DOM 0级事件是元素内的一个私有属性:div.onclick = function () {},对一个私有属性赋值(在该事件上绑定一个方法).由此可知DOM 0级 ...
- imx6. android6.0经常修改或者用到的目录(未完)
系统应用apk存放的文件: out/target/product/sabresd_6dq/system/app 系统配置存放目录:(我自己的,也许不对) out/target/product/sabr ...
- 【转】mvc
又看到有人在问三层架构和MVC的关系,感觉这种问题有点教条化了.因为它们都在逻辑上将应用程序划为三块,凑了一个数字3,就有人非要把它们联系到一起了. 这两个东西我接触有几年了,有一点体会,表达一下: ...