实现效果
- 添加表格条目
- 编辑表格某一条数据
- 删除表格某一条数据 (添加数据和修改数据都使用模态框完成数据交互)
实现细节
(直接想要完整代码的朋友请滑到页面最下方)
· 遮罩层
模拟框应该伴随着遮罩层出现,使弹窗出现的效果不会太突兀
**css代码**
/*遮罩层样式*/
.mask {
position: absolute;
left: 0;
top: 0;
background: rgba(0, 0, 0, 0.4);
width: 100%; /*宽度和高度设置为100%,这样才能使隐藏背景层覆盖原页面*/
height: 100%;
opacity: 0.6;
display: none;
z-Index: 2;
}
· 新建数据的弹窗验证
新建数据的弹窗是一个需要用户填写的表单,表单的话当然少不了要对用户输入进行验证。
验证一:失去焦点 逐个验证
- 首先我们需要获取到不同类别的输入元素,如我们这里使用的input和select
- 对这些元素绑定失去焦点事件,当用户点击该元素输入后触发事件执行对应的验证函数
- 然后验证的是用户输入是否为空
- 再验证填写是否符合格式要求(如手机号码应该为13、14、15、17、18开头的11位手机号等),这里我们只对手机号码进行了正则表达式格式验证,其他输入数据原理也是一样的,大家可以举一反三
**js代码**
//表单验证
// 获取新建数据弹窗的所有input框
var inputs = newDialogForm.getElementsByTagName('input');
// 为每个input框添加失去焦点事件
for (var i = 0; i < inputs.length; i++) {
inputs[i].onblur = verifyInput;
}
// 获取新建数据弹窗的所有select框
var selects = newDialogForm.getElementsByTagName('select');
//为每个select框添加失去焦点事件
for (var j = 0; j < selects.length; j++) {
selects[j].onblur = verifySelect;
}
//单个验证:该函数主要用于获取相应input元素的用户输入的内容进行检验,之后,把检验的结果显示在HTML页面中
function verifyInput() {
// 获取输入框的id值、value值
var id = this.id;
var val = this.value;
//去掉空格
val = val.trim();
//判断内容是否为空,显示提示信息
if (!val) {
this.parentNode.children[2].textContent = '输入不能为空!'
return
} else {
//当用户添加内容后清除提示
this.parentNode.children[2].textContent = ''
}
//手机号码:13、14、15、17、18开头的11位手机号。
if (id === 'newPhone') {
var reg = /^1[3456789]\d{9}$/;
if (!reg.test(val)) {
this.parentNode.children[2].textContent = '不符合格式!'
} else {
//当用户修改内容符合格式后清除提示
this.parentNode.children[2].textContent = ''
}
}
}
//单个验证:该函数主要用于获取相应select元素的用户输入的内容进行检验,之后,把检验的结果显示在HTML页面中
function verifySelect() {
// 获取输入框的value值
var val = this.value;
//去掉空格
val = val.trim();
//判断select选择框内容是否为空
if (val === 'none') {
this.parentNode.children[2].textContent = '输入不能为空!'
} else {
//当用户选择内容后清除提示
this.parentNode.children[2].textContent = ''
}
}
验证二:保存按钮 集体验证
验证一存在一个漏洞:若用户没有点击过该元素,则不会触发元素的失去焦点事件,所以验证函数就不会被执行。因此在用户点击提交表单数据后再次对所有输入元素进行验证,避免有元素因为没有获取焦点而遗漏验证。
点击保存按钮后,遍历所有的input和select元素执行验证函数,判断是否存在输入不正确,存在则弹窗警告不做任何处理,等待用户更改数据;若所有输入都正确则往表格的末尾追加一条数据,数据所有内容从表单中一一对应获取
**js代码**
//在提交表单数据前再次进行验证,避免有元素因为没有获取焦点而遗漏验证
for (var i = 0; i < inputs.length; i++) {
// 通过apply()方法可以设置 this 的值
verifyInput.apply(inputs[i])
}
for (var j = 0; j < selects.length; j++) {
verifySelect.apply(selects[j])
}
//判断验证结果是否全都正确
var verifyList = document.querySelectorAll(".verify")
for (var k = 0; k < verifyList.length; k++) {
if (verifyList[k].textContent !== '') {
alert("存在输入为空或者不正确的输入!")
return
}
}
· 表格数据的编辑和删除
删除:注意每次删除表格数据后都要重新排序剩下表格数据的编号
// 删除动作
btnDeletes[idx].addEventListener('click', function () {
num--
recordTable.removeChild(this.parentNode.parentNode)
var numList = document.querySelectorAll(".recordForm>tbody>tr>td:nth-child(1)") //表格数据编号单元格列表
//删除节点后编号重新排序
for (var i = 0; i < numList.length; i++) {
numList[i].textContent = i + 1
}
})
编辑
// 编辑动作
btnEdits[idx].addEventListener('click', function () {
editObj = this.parentNode.parentNode
showEditDialog()
})
· 新加数据的编辑和删除
上面提到的表格数据的编辑和删除只是针对当前已有的编辑和删除按钮,当用户添加了新的数据后会增加新的编辑和删除按钮(未绑定编辑和删除事件),因此需要对新增记录的按钮绑定事件
//给新增加的记录的按钮绑定点击事件
//绑定删除按键事件
recordTable.lastElementChild.querySelector('.delete').addEventListener('click', function () {
num--
recordTable.removeChild(this.parentNode.parentNode)
var numList = document.querySelectorAll(".recordForm>tbody>tr>td:nth-child(1)") //表格数据编号单元格列表
//删除节点后编号重新排序
for (var i = 0; i < numList.length; i++) {
numList[i].textContent = i + 1
}
})
//绑定编辑按键事件
recordTable.lastElementChild.querySelector('.edit').addEventListener('click', function () {
editObj = this.parentNode.parentNode
showEditDialog()
})
以上就是一些比较重要的实现细节,当然还有很多css和js设计细节笔者没有进行论述,感兴趣的朋友可以看看具体代码,发现问题也期待指正!
完整代码
HTML
<button class="new">新建数据</button>
<!--表格-->
<table class="recordForm">
<!-- 表格头部-->
<thead>
<tr>
<th>编号</th>
<th>合同编号</th>
<th>公司名称</th>
<th>联系人</th>
<th>联系电话</th>
<th>合同类型</th>
<th>合同状态</th>
<th>负责业务员</th>
<th>设备数量</th>
<th>操作</th>
</tr>
</thead>
<!-- 表格主体-->
<tbody>
<tr>
<td>1</td>
<td>ZL-2017230489032849</td>
<td>背景南玻智能设备有限公司</td>
<td>xxxx</td>
<td>xxxxxxxx</td>
<td>
<button class="standard">标准</button>
</td>
<td>未生效</td>
<td>李三</td>
<td>1</td>
<td>
<button class="edit">编辑</button>
<button class="delete">删除</button>
</td>
</tr>
<tr>
<td>2</td>
<td>ZL-2017071892766</td>
<td>北京南博智能设备有限公司</td>
<td>xxxx</td>
<td>xxxxxxxx</td>
<td>
<button class="special">特殊</button>
</td>
<td>已生效</td>
<td>王五</td>
<td>2</td>
<td>
<button class="edit">编辑</button>
<button class="delete">删除</button>
</td>
</tr>
<tr>
<td>3</td>
<td>ZL-2017230489032849</td>
<td>北京起邦事业有事有限公司</td>
<td>xxxx</td>
<td>xxxxxxxx</td>
<td>
<button class="standard">标准</button>
</td>
<td>已生效</td>
<td>张青</td>
<td>4</td>
<td>
<button class="edit">编辑</button>
<button class="delete">删除</button>
</td>
</tr>
</tbody>
</table>
<!--遮罩层-->
<section class="mask"></section>
<!--新建数据弹窗表单-->
<section class="newDialogForm">
<header>
<span>新建数据</span>
<strong>X</strong>
</header>
<form action="" method="POST" autocomplete="off">
<div>
<label for="newContractNum">合同编号</label>
<input type="text" id="newContractNum">
<span class="verify"></span>
</div>
<div>
<label for="newCompany">客户公司名称</label>
<input type="text" id="newCompany">
<span class="verify"></span>
</div>
<div>
<label for="newName">联系人名字</label>
<input type="text" id="newName">
<span class="verify"></span>
</div>
<div>
<label for="newPhone">联系人手机号</label>
<input type="text" id="newPhone">
<span class="verify"></span>
</div>
<div>
<label for="newCompact">合同类型</label>
<select id="newCompact">
<option value="none" selected disabled hidden>请选择</option>
<option value="标准">标准</option>
<option value="特殊">特殊</option>
</select>
<span class="verify"></span>
</div>
<div>
<label for="newAddress">公司详细地址</label>
<input type="text" id="newAddress">
<span class="verify"></span>
</div>
<div>
<label for="newTip" id="tip">备注</label>
<textarea id="newTip" cols="30" rows="5"></textarea>
<span class="verify"></span>
</div>
<div>
<label for="newState">合同状态</label>
<select id="newState">
<option value="none" selected disabled hidden>请选择</option>
<option value="未生效">未生效</option>
<option value="已生效">已生效</option>
</select>
<span class="verify"></span>
</div>
<div>
<label for="newClerk">业务员</label>
<input type="text" id="newClerk">
<span class="verify"></span>
</div>
<div>
<label for="newEquipment">设备数量</label>
<input type="text" id="newEquipment">
<span class="verify"></span>
</div>
</form>
<footer>
<button class="newClose">关闭</button>
<button class="newSave">保存</button>
</footer>
</section>
<!--编辑数据弹窗表单-->
<section class="editDialogForm">
<header>
<span>编辑数据</span>
<strong>X</strong>
</header>
<form action="" method="POST" autocomplete="off">
<div>
<label for="editContractNum">合同编号</label>
<input type="text" id="editContractNum">
</div>
<div>
<label for="editCompany">公司名称</label>
<input type="text" id="editCompany">
</div>
<div>
<label for="editName">联系人</label>
<input type="text" id="editName">
</div>
<div>
<label for="editPhone">联系电话</label>
<input type="text" id="editPhone">
</div>
<div>
<label for="editCompact">合同类型</label>
<select id="editCompact">
<option value="none" selected disabled hidden>请选择</option>
<option value="标准">标准</option>
<option value="特殊">特殊</option>
</select>
</div>
<div>
<label for="editState">合同状态</label>
<select id="editState">
<option value="none" selected disabled hidden>请选择</option>
<option value="未生效">未生效</option>
<option value="已生效">已生效</option>
</select>
</div>
<div>
<label for="editClerk">负责业务员</label>
<input type="text" id="editClerk">
</div>
<div>
<label for="editEquipment">设备数量</label>
<input type="text" id="editEquipment">
</div>
</form>
<footer>
<button class="editClose">关闭</button>
<button class="editSave">保存</button>
</footer>
</section>
CSS
<style>
* {
margin: 0;
padding: 0;
}
body,
html {
width: 100%;
height: 100%;
font-family: 'Microsoft Yahei', '微软雅黑', 'Simsun', '宋体', 'Arial', sans-serif;
font-size: 14px;
box-sizing: border-box;
}
/*去除ul,li,input等标签的默认样式*/
ul {
list-style: none;
}
li {
display: inline-block;
}
input, button, select, textarea {
background: none;
outline: none;
border: 0;
}
a {
text-decoration: none;
color: #000;
}
/*“新建数据”按钮的样式*/
.new {
background: #289FFE;
margin: 10px 0 10px 20px;
padding: 7px 18px;
border-radius: 3px;
color: #fff;
cursor: pointer;
}
/*表格样式*/
.recordForm {
border-collapse: collapse;
border: 1px solid #000;
margin-left: 20px;
text-align: center;
}
/*表格th和td样式*/
th, td {
white-space: nowrap;
border: 1px solid #000;
border-collapse: collapse;
padding: 10px;
}
/*表格合同编号列设置字体颜色*/
tbody > tr > td:nth-child(2) {
color: #FAB241;
}
/*合同类型标准样式*/
.standard {
background: #B3E0FC;
border: 1px solid #70BFFE;
padding: 5px 15px;
border-radius: 5px;
color: #2CA4FD;
}
/*合同类型特殊样式*/
.special {
background: #EEEEEE;
border: 1px solid #A7A7A7;
padding: 5px 15px;
border-radius: 5px;
color: #ACACAC;
}
/*编辑按钮样式*/
.edit {
background: #F99800;
border: 1px solid #FAAE35;
padding: 5px 15px;
border-radius: 5px;
color: #fff;
cursor: pointer;
}
/*删除按钮样式*/
.delete {
background: #E93B0E;
border: 1px solid #E94014;
padding: 5px 15px;
border-radius: 5px;
color: #fff;
cursor: pointer;
}
/*遮罩层样式*/
.mask {
position: absolute;
left: 0;
top: 0;
background: rgba(0, 0, 0, 0.4);
width: 100%; /*宽度和高度设置为100%,这样才能使隐藏背景层覆盖原页面*/
height: 100%;
opacity: 0.6;
display: none;
z-Index: 2;
}
/*新建数据表单和编辑数据表单*/
.newDialogForm, .editDialogForm {
position: absolute;
width: 600px;
height: 500px;
top: 50%;
left: 50%;
margin-left: -300px;
margin-top: -250px;
display: none;
z-Index: 3;
border: 1px solid #BABABA;
box-shadow: 0 2px 4px 2px rgba(213, 213, 213, 0.50);
}
.newDialogForm > header, .editDialogForm > header {
background: #289FFE;
padding: 10px 0 10px 15px;
color: #fff;
font-size: 18px;
line-height: 20px;
}
.newDialogForm > header > strong, .editDialogForm > header > strong {
margin-left: 470px;
cursor: pointer;
}
.newDialogForm > form, .editDialogForm > form {
background: #fff;
height: 410px;
padding: 20px 40px 0 40px;
}
.newDialogForm > form > div, .editDialogForm > form > div {
display: flex;
margin-bottom: 10px;
padding: 0;
}
.newDialogForm > form > div > label, .editDialogForm > form > div > label {
flex: 0.2;
text-align: right;
width: 100px;
color: #4B4B4B;
margin-right: 15px;
}
.newDialogForm > form > div > label::before {
content: '*';
color: #F63101;
}
/*备注不需要’*‘,去除伪类*/
.newDialogForm > form > div > #tip::before {
content: none;
}
/*单行输入框和选择框的共同样式*/
.newDialogForm > form > div > input, .newDialogForm > form > div > select, .editDialogForm > form > div > input, .editDialogForm > form > div > select {
height: 20px;
border: 1px solid #CFCFCF;
border-radius: 2px;
}
.newDialogForm > form > div > input, .newDialogForm > form > div > textarea, .editDialogForm > form > div > input {
flex: 0.6;
right: 0;
}
.newDialogForm > form > div > select, .editDialogForm > form > div > select {
flex: 0.5;
right: 85px;
}
/*验证内容:判断输入是否合格*/
.verify {
flex: 0.2;
margin-left: 5px;
color: red;
font-size: 12px;
}
.newDialogForm > form > div > textarea {
border: 1px solid #CFCFCF;
border-radius: 2px;
}
.newDialogForm > footer, .editDialogForm > footer {
width: 100%;
height: 50px;
background: #EEEEEE;
}
/*关闭和保存按钮共同样式*/
.newClose, .newSave, .editClose, .editSave {
position: absolute;
width: 100px;
height: 30px;
border-radius: 3px;
margin: 10px 0;
cursor: pointer;
}
.newClose, .editClose {
right: 120px;
border: 1px solid #888888;
background: #fff;
color: #525252;
}
.newSave, .editSave {
right: 10px;
border: 1px solid #BEDFE7;
background: #289FFE;
color: #FEFEFE;
}
</style>
JS
<script>
var newBtn = document.querySelector(".new") //新建按钮
var mask = document.querySelector(".mask") //遮罩
var newDialogForm = document.querySelector(".newDialogForm") //新建记录对话模拟框
var editDialogForm = document.querySelector(".editDialogForm") //编辑记录对话模拟框
var xNewClose = document.querySelector(".newDialogForm>header>strong") //新建窗口右上角的×
var btnNewClose = document.querySelector(".newClose") //新建窗口关闭按钮
var btnNewSave = document.querySelector(".newSave") //新建窗口保存按钮
var xEditClose = document.querySelector(".editDialogForm>header>strong") //编辑窗口右上角的×
var btnEditClose = document.querySelector(".editClose") //编辑窗口关闭按钮
var btnEditSave = document.querySelector(".editSave") //编辑窗口保存按钮
var btnDeletes = document.querySelectorAll(".delete") //删除按钮
var btnEdits = document.querySelectorAll(".edit") //编辑按钮
var recordTable = document.querySelector(".recordForm>tbody") //表格数据主体
var num = 3 //当前表格的记录数量
var editObj //当前修改的记录
//隐藏新建记录对话模拟框
function closeNewDialog() {
mask.style.display = 'none'
newDialogForm.style.display = 'none'
}
//展示新建记录对话模拟框
function showNewDialog() {
mask.style.display = 'block'
newDialogForm.style.display = 'block'
}
//隐藏编辑记录对话模拟框
function closeEditDialog() {
mask.style.display = 'none'
editDialogForm.style.display = 'none'
}
//展示编辑记录对话模拟框
function showEditDialog() {
mask.style.display = 'block'
editDialogForm.style.display = 'block'
}
//给所有删除按钮和编辑按钮绑定点击事件
for (let idx = 0; idx < btnDeletes.length; idx++) {
// 删除动作
btnDeletes[idx].addEventListener('click', function () {
num--
recordTable.removeChild(this.parentNode.parentNode)
var numList = document.querySelectorAll(".recordForm>tbody>tr>td:nth-child(1)") //表格数据编号单元格列表
//删除节点后编号重新排序
for (var i = 0; i < numList.length; i++) {
numList[i].textContent = i + 1
}
})
// 编辑动作
btnEdits[idx].addEventListener('click', function () {
editObj = this.parentNode.parentNode
showEditDialog()
})
}
// 新建数据弹窗动作
//新建按钮
newBtn.addEventListener('click', showNewDialog)
//关闭按钮
btnNewClose.addEventListener('click', closeNewDialog)
//右上角叉掉
xNewClose.addEventListener('click', closeNewDialog)
//表单验证
// 获取新建数据弹窗的所有input框
var inputs = newDialogForm.getElementsByTagName('input');
// 为每个input框添加失去焦点事件
for (var i = 0; i < inputs.length; i++) {
inputs[i].onblur = verifyInput;
}
// 获取新建数据弹窗的所有select框
var selects = newDialogForm.getElementsByTagName('select');
//为每个select框添加失去焦点事件
for (var j = 0; j < selects.length; j++) {
selects[j].onblur = verifySelect;
}
//单个验证:该函数主要用于获取相应input元素的用户输入的内容进行检验,之后,把检验的结果显示在HTML页面中
function verifyInput() {
// 获取输入框的id值、value值
var id = this.id;
var val = this.value;
//去掉空格
val = val.trim();
//判断内容是否为空,显示提示信息
if (!val) {
this.parentNode.children[2].textContent = '输入不能为空!'
return
} else {
//当用户添加内容后清除提示
this.parentNode.children[2].textContent = ''
}
//手机号码:13、14、15、17、18开头的11位手机号。
if (id === 'newPhone') {
var reg = /^1[3456789]\d{9}$/;
if (!reg.test(val)) {
this.parentNode.children[2].textContent = '不符合格式!'
} else {
//当用户修改内容符合格式后清除提示
this.parentNode.children[2].textContent = ''
}
}
}
//单个验证:该函数主要用于获取相应select元素的用户输入的内容进行检验,之后,把检验的结果显示在HTML页面中
function verifySelect() {
// 获取输入框的value值
var val = this.value;
//去掉空格
val = val.trim();
//判断select选择框内容是否为空
if (val === 'none') {
this.parentNode.children[2].textContent = '输入不能为空!'
} else {
//当用户选择内容后清除提示
this.parentNode.children[2].textContent = ''
}
}
//保存按钮
btnNewSave.addEventListener('click', function () {
//在提交表单数据前再次进行验证,避免有元素因为没有获取焦点而遗漏验证
for (var i = 0; i < inputs.length; i++) {
// 通过apply()方法可以设置 this 的值
verifyInput.apply(inputs[i])
}
for (var j = 0; j < selects.length; j++) {
verifySelect.apply(selects[j])
}
//判断验证结果是否全都正确
var verifyList = document.querySelectorAll(".verify")
for (var k = 0; k < verifyList.length; k++) {
if (verifyList[k].textContent !== '') {
alert("存在输入为空或者不正确的输入!")
return
}
}
num++
//用户选择的合同类型为标准
if (document.getElementById("newCompact").value === "标准") {
recordTable.insertAdjacentHTML('beforeend', `<tr>
<td>${num}</td>
<td>${document.getElementById("newContractNum").value}</td>
<td>${document.getElementById("newCompany").value}</td>
<td>${document.getElementById("newName").value}</td>
<td>${document.getElementById("newPhone").value}</td>
<td>
<button class="standard">标准</button>
</td>
<td>${document.getElementById("newState").value}</td>
<td>${document.getElementById("newClerk").value}</td>
<td>${document.getElementById("newEquipment").value}</td>
<td>
<button class="edit">编辑</button>
<button class="delete">删除</button>
</td>
</tr>`)
}
//用户选择的合同类型为特殊
else {
recordTable.insertAdjacentHTML('beforeend', `<tr>
<td>${num}</td>
<td>${document.getElementById("newContractNum").value}</td>
<td>${document.getElementById("newCompany").value}</td>
<td>${document.getElementById("newName").value}</td>
<td>${document.getElementById("newPhone").value}</td>
<td>
<button class="special">特殊</button>
</td>
<td>${document.getElementById("newState").value}</td>
<td>${document.getElementById("newClerk").value}</td>
<td>${document.getElementById("newEquipment").value}</td>
<td>
<button class="edit">编辑</button>
<button class="delete">删除</button>
</td>
</tr>`)
}
//关闭新建数据弹窗
closeNewDialog()
//给新增加的记录的按钮绑定点击事件
//绑定删除按键事件
recordTable.lastElementChild.querySelector('.delete').addEventListener('click', function () {
num--
recordTable.removeChild(this.parentNode.parentNode)
var numList = document.querySelectorAll(".recordForm>tbody>tr>td:nth-child(1)") //表格数据编号单元格列表
//删除节点后编号重新排序
for (var i = 0; i < numList.length; i++) {
numList[i].textContent = i + 1
}
})
//绑定编辑按键事件
recordTable.lastElementChild.querySelector('.edit').addEventListener('click', function () {
editObj = this.parentNode.parentNode
showEditDialog()
})
})
// 编辑数据弹窗动作
//关闭按钮
btnEditClose.addEventListener('click', closeEditDialog)
//右上角叉掉
xEditClose.addEventListener('click', closeEditDialog)
//保存按钮
btnEditSave.addEventListener('click', function () {
//修改合同编号
if (document.getElementById("editContractNum").value !== '') {
editObj.cells[1].innerText = document.getElementById("editContractNum").value
}
//修改公司名称
if (document.getElementById("editCompany").value !== '') {
editObj.cells[2].innerText = document.getElementById("editCompany").value
}
//修改联系人
if (document.getElementById("editName").value !== '') {
editObj.cells[3].innerText = document.getElementById("editName").value
}
//修改联系电话
if (document.getElementById("editPhone").value !== '') {
editObj.cells[4].innerText = document.getElementById("editPhone").value
}
//修改合同类型
if (document.getElementById("editCompact").value === '标准') {
editObj.cells[5].innerHTML = "<button class='standard'>标准</button>"
} else {
editObj.cells[5].innerHTML = "<button class='special'>特殊</button>"
}
//修改合同状态
if (document.getElementById("editState").value !== '') {
editObj.cells[6].innerText = document.getElementById("editState").value
}
//修改负责业务员
if (document.getElementById("editClerk").value !== '') {
editObj.cells[7].innerText = document.getElementById("editClerk").value
}
//修改设备数量
if (document.getElementById("editEquipment").value !== '') {
editObj.cells[8].innerText = document.getElementById("editEquipment").value
}
closeEditDialog()
})
</script>