javaScript是一种解释型语言,它的执行是自上而下,但是各个浏览器对于至上而下的理解是有细微差别的,而代码的上下游也就是程序流又对于程序正确至关重要。
首先得了解有几种方法能把javaScript加入到页面中? 常见下述的前2种,其实还有更多。
1.页面中直接引入外部js文件:<script src="my.js"></script>
2.页面中直接写入 js片段: <script>alert(1)</script>
3.在js中引入js文件(比较少用): document.write("<scr"+"ipt src='my.js'></scr"+"ipt>");
注意:这时候"..</script>"必须拆成"</scr"+"ipt>",否则浏览器可能会把父js片段关闭掉,出错;
4.同样在js中引用其他js片段,document.write("<scr"+"ipt>alert(1)</scr"+"ipt>");
你可能觉得这个并没有必要,既然已经在script中了还套一层干嘛?呵呵,怎么说也是一种写法,而且它具有其特殊的行为,稍后我们讨论到。
5.使用Ajax中的xmlHttpRequest结合eval()来引入js,我最早在Dojo的代码见到,写的详细些:
var ajaxRequest=getXmlHttpRequest()//省去各个浏览器得到xmlHttpRequest的部门
ajaxRequest.open("GET","my.js",false);//使用xmlHttpRequest对象Get方法的同步调用
ajaxRequest.send(null);
sJsFragment=ajax.responseText;//得到字符串为js片段
eval(sJsFragment);//执行js片段
注意:这里要求my.js即后来的sJsFragment内容得是非常规范的js,且没有//开头的注释,怎样检查js是否规范呢?去http://jslint.com/
6.无所不能的Dom方法,非常好用:
var oScript=document.createElement("script");//创建一个Script元素
oScript.src="my.js";//制定src属性
document.getElementsByTagName("head")[0].appendChild(oScript);
说明:my.js的内容会在oScript加入到文档中之后获得并执行。仔细看下这段容易发现这个调用是异步的,可以在文档载入之后通过事件触发,我用它变通了一下,作为了xmlHttpRequest的Get方法在跨域取数时的替代,获得了很完美的效果,以后有机会专门写篇文。
六种不少吧,可能还会有吧,而且这几种之间还可能相互嵌套,变化无常。
其中1、2、4、6种方式引入的javaScript的执行顺序是非常自然的,随着页面的载入以及后续的事件触发,它们遵守先来后到、而其内部自上而下。
现代web开发中,表单是用户与网站互动的重要方式之一。HTML5为表单提交提供了强大的功能和丰富的输入类型,让收集和验证用户输入数据变得更加容易和安全。本文将详细介绍HTML5表单的各个方面,包括基本结构、输入类型、验证方法和提交过程。
HTML表单由<form>标签定义,它可以包含输入字段、标签、按钮等元素。一个基本的表单结构如下所示:
<form action="/submit_form" method="post">
<label for="name">姓名:</label>
<input type="text" id="name" name="name" required>
<label for="email">电子邮箱:</label>
<input type="email" id="email" name="email" required>
<input type="submit" value="提交">
</form>
在这个例子中,表单有两个输入字段:姓名和电子邮箱。每个输入字段都有一个<label>标签,这不仅有助于用户理解输入的内容,也有助于屏幕阅读器等辅助技术。<form>标签的action属性定义了数据提交到服务器的URL,method属性定义了提交数据的HTTP方法(通常是post或get)。
HTML5提供了多种输入类型,以支持不同的数据格式和设备。
<!-- 单行文本 -->
<input type="text" name="username" placeholder="请输入用户名" required>
<!-- 密码 -->
<input type="password" name="password" required minlength="8">
<!-- 邮箱 -->
<input type="email" name="email" required placeholder="example@domain.com">
<!-- 搜索框 -->
<input type="search" name="search" placeholder="搜索...">
<!-- 数值 -->
<input type="number" name="age" min="18" max="100" step="1" required>
<!-- 滑动条 -->
<input type="range" name="volume" min="0" max="100" step="1">
<!-- 电话号码 -->
<input type="tel" name="phone" pattern="^\+?\d{0,13}" placeholder="+8613800000000">
<!-- 日期 -->
<input type="date" name="birthdate" required>
<!-- 时间 -->
<input type="time" name="appointmenttime">
<!-- 日期和时间 -->
<input type="datetime-local" name="appointmentdatetime">
<!-- 复选框 -->
<label><input type="checkbox" name="interest" value="coding"> 编程</label>
<label><input type="checkbox" name="interest" value="music"> 音乐</label>
<!-- 单选按钮 -->
<label><input type="radio" name="gender" value="male" required> 男性</label>
<label><input type="radio" name="gender" value="female"> 女性</label>
<!-- 下拉选择 -->
<select name="country" required>
<option value="china">中国</option>
<option value="usa">美国</option>
</select>
<!-- 颜色选择器 -->
<input type="color" name="favcolor" value="#ff0000">
<!-- 文件上传 -->
<input type="file" name="resume" accept=".pdf,.docx" multiple>
HTML5表单提供了内置的验证功能,可以在数据提交到服务器之前进行检查。
<input type="text" name="username" required>
<input type="text" name="zipcode" pattern="\d{5}(-\d{4})?" title="请输入5位数的邮政编码">
<input type="number" name="age" min="18" max="99">
<input type="text" name="username" minlength="4" maxlength="8">
当用户填写完表单并点击提交按钮时,浏览器会自动检查所有输入字段的有效性。如果所有字段都满足要求,表单数据将被发送到服务器。否则,浏览器会显示错误信息,并阻止表单提交。
<input type="submit" value="提交">
可以使用JavaScript来自定义验证或处理提交事件:
document.querySelector('form').addEventListener('submit', function(event) {
// 检查表单数据
if (!this.checkValidity()) {
event.preventDefault(); // 阻止表单提交
// 自定义错误处理
}
// 可以在这里添加额外的逻辑,比如发送数据到服务器的Ajax请求
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>表单提交并显示JSON</title>
</head>
<body>
<!-- 表单定义 -->
<form id="myForm">
<label for="name">姓名:</label>
<input type="text" id="name" name="name">
<br>
<label for="email">电子邮件:</label>
<input type="email" id="email" name="email">
<br>
<input type="button" value="提交" onclick="submitForm()">
</form>
<script>
// JavaScript函数,处理表单提交
function submitForm() {
// 获取表单元素
var form=document.getElementById('myForm');
// 创建一个FormData对象
var formData=new FormData(form);
// 创建一个空对象来存储表单数据
var formObject={};
// 将FormData转换为普通对象
formData.forEach(function(value, key){
formObject[key]=value;
});
// 将对象转换为JSON字符串
var jsonString=JSON.stringify(formObject);
// 弹出包含JSON字符串的对话框
alert(jsonString);
// 阻止表单的默认提交行为
return false;
}
</script>
</body>
</html>
在这个例子中:
注意,这个例子中我们使用了type="button"而不是type="submit",因为我们不希望表单有默认的提交行为。我们的JavaScript函数submitForm会处理所有的逻辑,并且通过返回false来阻止默认的表单提交。如果你想要使用type="submit",你需要在<form>标签上添加一个onsubmit="return submitForm()"属性来代替按钮上的onclick事件。
HTML5的表单功能为开发者提供了强大的工具,以便创建功能丰富、用户友好且安全的网站。通过使用HTML5的输入类型和验证方法,可以确保用户输入的数据是有效的,同时提高用户体验。随着技术的不断进步,HTML5表单和相关API将继续发展,为前端工程师提供更多的可能性。
最近笔者终于把H5-Dooring的后台管理系统初步搭建完成, 有了初步的数据采集和数据分析能力, 接下来我们就复盘一下其中涉及的几个知识点,并一一阐述其在Dooring H5可视化编辑器中的解决方案. 笔者将分成3篇文章来复盘, 主要解决场景如下: 如何使用JavaScript实现前端导入和导出excel文件(H5编辑器实战复盘) 前端如何基于table中的数据一键生成多维度数据可视化分析报表 * 如何实现会员管理系统下的权限路由和权限菜单
以上场景也是前端工程师在开发后台管理系统中经常遇到的或者即将遇到的问题, 本文是上述介绍中的第一篇文章, 你将收获: 使用JavaScript实现前端导入excel文件并自动生成可编辑的Table组件 使用JavaScript实现前端基于Table数据一键导出excel文件 * XLSX和js-export-excel基本使用
本文接下来的内容素材都是基于H5可视化编辑器(H5-Dooring)项目的截图, 如果想实际体验, 可以访问H5-Dooring网站实际体验. 接下来我们直接开始我们的方案实现.
在开始实现之前, 我们先来看看实现效果.
导入excel文件并通过antd的table组件渲染table:
编辑table组件:
保存table数据后实时渲染可视化图表:
以上就是我们实现导入excel文件后, 编辑table, 最后动态生成图表的完整流程.
导入excel文件的功能我们可以用javascript原生的方式实现解析, 比如可以用fileReader这些原生api,但考虑到开发效率和后期的维护, 笔者这里采用antd的Upload组件和XLSX来实现上传文件并解析的功能. 由于我们采用antd的table组件来渲染数据, 所以我们需要手动将解析出来的数据转换成table支持的数据格式.大致流程如下:
所以我们需要做的就是将Upload得到的文件数据传给xlsx, 由xlsx生成解析对象, 最后我们利用javascript算法将xlsx的对象处理成ant-table支持的数据格式即可. 这里我们用到了FileReader对象, 目的是将文件转化为BinaryString, 然后我们就可以用xlsx的binary模式来读取excel数据了, 代码如下:
// 解析并提取excel数据
let reader=new FileReader();
reader.onload=function(e) {
let data=e.target.result;
let workbook=XLSX.read(data, {type: 'binary'});
let sheetNames=workbook.SheetNames; // 工作表名称集合
let draftObj={}
sheetNames.forEach(name=> {
// 通过工作表名称来获取指定工作表
let worksheet=workbook.Sheets[name];
for(let key in worksheet) {
// v是读取单元格的原始值
if(key[0] !=='!') {
if(draftObj[key[0]]) {
draftObj[key[0]].push(worksheet[key].v)
}else {
draftObj[key[0]]=[worksheet[key].v]
}
}
}
});
// 生成ant-table支持的数据格式
let sourceData=Object.values(draftObj).map((item,i)=> ({ key: i + '', name: item[0], value: item[1]}))
经过以上处理, 我们得到的sourceData即是ant-table可用的数据结构, 至此我们就实现了表格导入的功能.
table表格的编辑功能实现其实也很简单, 我们只需要按照antd的table组件提供的自定义行和单元格的实现方式即可. antd官网上也有实现可编辑表格的实现方案, 如下:
大家感兴趣的可以研究一下. 当然自己实现可编辑的表格也很简单, 而且有很多方式, 比如用column的render函数来动态切换表格的编辑状态, 或者使用弹窗编辑等都是可以的.
根据table数据动态生成图表这块需要有一定的约定, 我们需要符合图表库的数据规范, 不过我们有了table数据, 处理数据规范当然是很简单的事情了, 笔者的可视化库采用antv的f2实现, 所以需要做一层适配来使得f2能消费我们的数据.
还有一点就是为了能使用多张图表, 我们需要对f2的图表进行统一封装, 使其成为符合我们应用场景的可视化组件库.
我们先看看f2的使用的数据格式:
const data=[
{ genre: 'Sports', sold: 275 },
{ genre: 'Strategy', sold: 115 },
{ genre: 'Action', sold: 120 },
{ genre: 'Shooter', sold: 350 },
{ genre: 'Other', sold: 150 }
];
此数据格式会渲染成如下的图表:
所以说我们总结下来其主要有2个纬度的指标, 包括它们的面积图, 饼图, 折线图, 格式都基本一致, 所以我们可以基于这一点封装成组件的可视化组件, 如下:
import { Chart } from '@antv/f2';
import React, { memo, useEffect, useRef } from 'react';
import ChartImg from '@/assets/chart.png';
import styles from './index.less';
import { IChartConfig } from './schema';
interface XChartProps extends IChartConfig {
isTpl: boolean;
}
const XChart=(props: XChartProps)=> {
const { isTpl, data, color, size, paddingTop, title }=props;
const chartRef=useRef(null);
useEffect(()=> {
if (!isTpl) {
const chart=new Chart({
el: chartRef.current || undefined,
pixelRatio: window.devicePixelRatio, // 指定分辨率
});
// step 2: 处理数据
const dataX=data.map(item=> ({ ...item, value: Number(item.value) }));
// Step 2: 载入数据源
chart.source(dataX);
// Step 3:创建图形语法,绘制柱状图,由 genre 和 sold 两个属性决定图形位置,genre 映射至 x 轴,sold 映射至 y 轴
chart
.interval()
.position('name*value')
.color('name');
// Step 4: 渲染图表
chart.render();
}
}, [data, isTpl]);
return (
<div className={styles.chartWrap}>
<div className={styles.chartTitle} style={{ color, fontSize: size, paddingTop }}>
{title}
</div>
{isTpl ? <img src={ChartImg} alt="dooring chart" /> : <canvas ref={chartRef}></canvas>}
</div>
);
};
export default memo(XChart);
当然其他的可视化组件也可以用相同的模式封装,这里就不一一举例了. 以上的组件封装使用react的hooks组件, vue的也类似, 基本原理都一致.
同样的, 我们实现将table数据一键导出为excel也是类似, 不过方案有所不同, 我们先来看看在Dooring中的实现效果.
以上就是用户基于后台采集到的数据, 一键导出excel文件的流程, 最后一张图是生成的excel文件在office软件中的呈现.
一键导出功能主要用在H5-Dooring的后台管理页面中, 为用户提供方便的导出数据能力. 我们这里导出功能也依然能使用xlsx来实现, 但是综合对比了一下笔者发现有更简单的方案, 接下来笔者会详细介绍, 首先我们还是来看一下流程:
很明显我们的导出流程比导入流程简单很多, 我们只需要将table的数据格式反编译成插件支持的数据即可. 这里笔者使用了js-export-excel来做文件导出, 使用它非常灵活,我们可以自定义: 自定义导出的excel文件名 自定义excel的过滤字段 * 自定义excel文件中每列的表头名称
由于js-export-excel支持的数据结构是数组对象, 所以我们需要花点功夫把table的数据转换成数组对象, 其中需要注意的是ant的table数据结构中键对应的值可以是数组, 但是js-export-excel键对应的值是字符串, 所以我们要把数组转换成字符串,如[a,b,c]变成'a,b,c', 所以我们需要对数据格式进行转换, 具体实现如下:
const generateExcel=()=> {
let option={}; //option代表的就是excel文件
let dataTable=[]; //excel文件中的数据内容
let len=list.length;
if (len) {
for(let i=0; i<len; i++) {
let row=list[i];
let obj:any={};
for(let key in row) {
if(typeof row[key]==='object') {
let arr=row[key];
obj[key]=arr.map((item:any)=> (typeof item==='object' ? item.label : item)).join(',')
}else {
obj[key]=row[key]
}
}
dataTable.push(obj); //设置excel中每列所获取的数据源
}
}
let tableKeys=Object.keys(dataTable[0]);
option.fileName=tableName; //excel文件名称
option.datas=[
{
sheetData: dataTable, //excel文件中的数据源
sheetName: tableName, //excel文件中sheet页名称
sheetFilter: tableKeys, //excel文件中需显示的列数据
sheetHeader: tableKeys, //excel文件中每列的表头名称
}
]
let toExcel=new ExportJsonExcel(option); //生成excel文件
toExcel.saveExcel(); //下载excel文件
}
注意, 以上笔者实现的方案对任何table组件都使用, 可直接使用以上代码在大多数场景下使用. 至此, 我们就实现了使用JavaScript实现前端导入和导出excel文件的功能.
所以, 今天你又博学了吗?
以上教程笔者已经集成到H5-Dooring中,对于一些更复杂的交互功能,通过合理的设计也是可以实现的,大家可以自行探索研究。
地址:H5-Dooring | 一款强大的H5编辑器
如果想学习更多H5游戏, webpack,node,gulp,css3,javascript,nodeJS,canvas数据可视化等前端知识和实战,欢迎在《趣谈前端》一起学习讨论,共同探索前端的边界。
*请认真填写需求信息,我们会在24小时内与您取得联系。