简易表格编辑器

SMMS有个建表工具,尝试使用HTML模仿出一个简单的做为练习.

截图:

 

一.实现SMMS建表工具的操作

  1. 点击单元格,进入编辑状态.
  2. 按TAB切换单元格
  3. 按方向箭切换单元格
  4. 按空格设定取消主键列

二.实现思路:

  1.DOM结构使用div(行)span(列) <div><span></span><span></span>...</div>

  2.每个span上设置tabindex=0,当获得焦点时,在其内生成表单元素.input select textarea等等.失去焦点时,将值取出来写到span内.

  3.给input设定onkeydown事件,方向键切换临近单元格

三.实现过程中:

  在实现TAB时费了不少功夫,按tab时,总是不能按预想的那样移到前一个或者后一个列上.在向span中加入了表单元素后,就更加混乱.后来发现在每个span上tabindex=0这个属性,由于值一样,所以tab就按相临的去找.

  当span得到焦点后,会向其内生成一个表单元素.表单元素上绑定失去焦点事件blue和keydown事件.blue用于将表单元素的值取出,然后写到span中.这里有个细节就是,span得到焦点后,要将tabindex=-1,即按tab时,不能得到焦点.如果不这样做,那么span处于编辑状态后,按tab就移动不了.因为它会移动到span上,而span的得到焦点事件又是在其内生成表单.这样就形成一个死循环.所以当span得到焦点后,需要将tabindex=-1,然后在该 span内的表单元素的失去焦点事件上再tabindex=0.这样就实现了焦点顺序的正确.不会因为新生成了表单元素而乱(表单元素默认能获得焦点).

  按上下方向键盘切换到上下行的同列,这个较简单,取得当前事件span的列索引,然后找到上下行的同列span让它获得焦点.注意是否有上下行.

  按左右方向键切换到同行临近列.如果是输入状态的文本框则判断光标位置,如果在最左并且按左方向,则移动.同理按右方向键也一样.如果是select框,则直接移动到临近.取消默认行为.textarea没做此功能.

四.主要代码 

.field-table-box {border-top: 1px solid #ccc;border-left: 1px solid #ccc;
}
.field-rows{ height:300px;overflow-y:auto;
}
.field-form-control {border: none;width: 100%;height: 100%;outline:none;/*用谷歌浏览器时:如果元素得到焦点.会默认有个蓝色边框.加这个可以去掉.(EDGE,FF没这问题)*/
}.field-form-control:focus {background-color: #75e0f5;border:none;}.field-col-title, .field-col {display: inline-block;font-size: 14px;height: 24px;line-height: 24px;text-align: center;vertical-align: top;border-right: 1px solid #ccc;border-bottom: 1px solid #ccc;outline:none;
}.field-col {color: #444;
}
.field-col.editinfo {height: 200px;line-height: 200px;
}.field-col.pk {cursor: crosshair;color: orangered;}.field-col-title.pk, .field-col.pk {width: 40px;}.field-col-title.name, .field-col.name {width: 140px;}.field-col-title.type, .field-col.type {width: 140px;}.field-col-title.len, .field-col.len, .field-col-title.len2, .field-col.len2 {width: 60px;}.field-col-title.dval, .field-col.dval {width: 120px;}.field-col-title.info, .field-col.info {width: 180px;}.field-col.info{}
.field-form-control.info {width: 100%;
}.field-col-title.delrow, .field-col-title.moverow, .field-col-title.moverow, .field-col-title.insertrow {width: 40px;
}.field-col.delrow, .field-col.moverow-down, .field-col.moverow-up, .field-col.insertrow {font-size: 0;width: 40px;cursor: pointer;
}.field-col.delrow:hover, .field-col.moverow-down:hover, .field-col.moverow-up:hover, .field-col.insertrow:hover {font-size: 16px; font-weight: 600;}.field-col.delrow {color: orangered;
}
.field-col.delrow:active{color: #fff;background-color: orangered;
}
.field-col.moverow-up, .field-col.moverow-down {color: #999;
}.field-col.moverow-up:active {color: #fff;background-color: blue;}.field-col.moverow-down:active {color: #fff;background-color: green;}.field-col.insertrow {color: #4679ca;
}.field-col.insertrow:active {color: #fff;background-color: #000;}
样式
<div class="field-table-box" id="field_table_box"><div class="field-table-cols"><h5 class="field-col-title pk">PK</h5><h5 class="field-col-title name">列名</h5><h5 class="field-col-title type">数据类型</h5><h5 class="field-col-title len">长度</h5><h5 class="field-col-title len2">精度</h5><h5 class="field-col-title dval">默认值</h5><h5 class="field-col-title info">列说明</h5><h5 class="field-col-title moverow">上移</h5><h5 class="field-col-title moverow">下移</h5><h5 class="field-col-title insertrow">插行</h5><h5 class="field-col-title delrow"></h5></div>
</div><template id="tpl_fieldrow"><div class="fieldrow"><span class="field-col pk noselect" onclick="onClick_SetPk(this)" onkeyup="onkeyup_SetPk(event,this)" tabindex="0"></span><span class="field-col name" onfocus="fieldCol_focus_toEdit(this)" tabindex="0"></span><span class="field-col type" onfocus="fieldColType_toSelect(this)" tabindex="0" val="1">字符串</span><span class="field-col len" onfocus="fieldCol_focus_toEdit(this)" tabindex="0">20</span><span class="field-col len2" onfocus="fieldCol_focus_toEdit(this)" tabindex="0">2</span><span class="field-col dval" onfocus="fieldCol_focus_toEdit(this)" tabindex="0">NULL</span><span class="field-col info" onfocus="fieldCol_focus_info(this)" tabindex="0"></span><span class="field-col moverow-up noselect" onclick="fieldColOp_click_moveRow(this)">&#9650;</span><span class="field-col moverow-down noselect" onclick="fieldColOp_click_moveRow(this)">&#9660;</span><span class="field-col insertrow noselect" onclick="fieldColOp_click_insertRow(this)">&#9768;</span><span class="field-col delrow noselect" onclick="fieldCopOp_click_delRow(this)">&#10005;</span></div>
</template>
<template id="tpl_fieldcol_type_select"><select class="field-form-control" onblur="fieldType_select_blur(this)" onkeydown="fieldType_select_keydown(event,this)"><option value="1">字符串</option><option value="2">整数</option><option value="3">小数</option><option value="4">时间</option></select>
</template>
html
/*
关于列的编辑功能模仿了SMMS工具的建表功能
例如:点击某一个列名,它就会变成可编辑的,实现上是在点击后,套了一个INPUT框
*/
$(function ()
{refreshEditStatus();
})
// 编辑列之后刷新编辑区域状态
function refreshEditStatus()
{// 自动增加新的编辑行
    addRowTpl();// 说明textarea框或处于编辑时的放大状态,此处还原$('.field-col').removeClass('editinfo');
}
// 增加一行到制表工具中:如果没新行加入(列名为空视为有新行),否则不加入
function addRowTpl()
{var hasNewRow = false;$('#field_table_box').find('.field-col.name').each(function (){if (String.IsNullOrWhiteSpace($(this).html())){hasNewRow = true;return false;}})if (hasNewRow == false)$('#field_table_box').append($('#tpl_fieldrow').html());
}
// 主键列: 按空格时,按上下键时
function onkeyup_SetPk(event, thisobj)
{var e = event || window.event;//console.log(e.keyCode);if (e.keyCode == 32){onClick_SetPk(thisobj);return;}// 当前编辑对象在fieldrow中的索引位置var colindex = $(thisobj).index();if (e.keyCode == 38){// 上一行var prevrow = $(thisobj).parent('.fieldrow').prev();if (prevrow.length == 1){prevrow.children().eq(colindex).focus();}} else if (e.keyCode == 39){// 移向右一列 不考虑右侧无列情况
        $(thisobj).next().focus();}else if (e.keyCode == 40){// 下一行var nextrow = $(thisobj).parent('.fieldrow').next();if (nextrow.length == 1){nextrow.children().eq(colindex).focus();}}
}
// 切换设置 鼠标单击时切换
function onClick_SetPk(thisobj)
{if ($(thisobj).html() == ''){$('.field-col.pk').html('');$(thisobj).html('主键');} else{$('.field-col.pk').html('');}return;
}
// 列名称,长度,默认值: 获得焦点后,其内生成input
function fieldCol_focus_toEdit(thisobj)
{if ($(thisobj).find('input').length == 1)return;var html = '<input class="field-form-control" οnblur="field_input_blur(this)" οnkeydοwn="field_input_updownArrow(event,this)" type="text"  />';var val = $(thisobj).html();$(thisobj).html(html).find('input').focus().val(val);$(thisobj).prop('tabindex', '-1');
}
// // 列名称,长度,默认值INPUT框失去焦点事件:框去掉,值写到父级SPAN中
function field_input_blur(thisobj)
{var parent = $(thisobj).parent('.field-col');var val = $(thisobj).val();//console.log(val);parent.html(val).prop('tabindex', '0');//
    refreshEditStatus();// 
}
// // 列名称,长度,默认值INPUT框支持上下方向键盘切换到上下行同一列
function field_input_updownArrow(event, thisobj)
{var e = event || window.event;// 当前编辑对象在其行内的列索引位置var colindex = $(thisobj).parent().index();if (e.keyCode == 38){// 上一行var prevrow = $(thisobj).parent().parent('.fieldrow').prev();if (prevrow.length == 1){prevrow.children().eq(colindex).focus();}} else if (e.keyCode == 40){// 下一行var nextrow = $(thisobj).parent().parent('.fieldrow').next();if (nextrow.length == 1){nextrow.children().eq(colindex).focus();}}// 按左右键时,如果光标处在文本最右,再按右,则移到后列.如果在最左,再按左,则移到前列// 光标焦点位置.如果=文本长度则在最后,为0则在最前//console.log($(thisobj).prop('selectionEnd'));var position = $(thisobj).prop('selectionEnd');if (e.keyCode == 37){// 左移动 未考虑左边没有列的情况,因为第1列是主键设置.有本事件的列,其左边至少有一列if (position == 0){$(thisobj).parent().prev().focus();}} else if (e.keyCode == 39){// 右 未考虑右边没列的情况,有本事件的列不会处于最右边if (position == $(thisobj).val().length){$(thisobj).parent().next().focus();}}
}
// 列的类型:获得焦点后,其内生成SELECT
function fieldColType_toSelect(thisobj)
{if ($(thisobj).find('select').length == 1)return;var selectedval = $(thisobj).attr('val');var select = $('#tpl_fieldcol_type_select').html();$(thisobj).html(select).prop('tabindex', '-1');if (selectedval){$(thisobj).find('select').focus().find('option[value=' + selectedval + ']').prop('selected', 'selected');//console.log($(thisobj).find('select').prop('tabindex'));
    }
}
// // 列类型SELECT框失去焦点后,选择值写到val属性上,标题值写到html
function fieldType_select_blur(thisobj)
{var val = $(thisobj).val();var text = $(thisobj).find('option[value=' + val + ']').html();$(thisobj).parent('.field-col').html(text).attr('val', val).prop('tabindex', '0');//
    refreshEditStatus();
}
// // 列类型SELECT框.按左右方向键时,移动到左右相应列上
function fieldType_select_keydown(event,thisobj)
{// 不考虑左右没有列的情况 ,因为本事件所在列不会处在最左或最右var e = event || window.event;if (e.keyCode == 37){// 左移动 
        $(thisobj).parent().prev().focus();} else if (e.keyCode == 39){//
        $(thisobj).parent().next().focus();}return false;
}
// 列的说明:获得焦点后,其内生成textarea
function fieldCol_focus_info(thisobj)
{if ($(thisobj).find('textarea').length == 1)return;var html = '<textarea class="field-form-control info" οnblur="field_input_blur(this)"></textarea>';var val = $(thisobj).html();$(thisobj).parent('.fieldrow').find('.field-col').addClass('editinfo');$(thisobj).html(html).find('textarea').focus().val(val);$(thisobj).prop('tabindex', '-1');
}
// 在当前行的上面插入一行
function fieldColOp_click_insertRow(thisobj)
{var row = $(thisobj).parent('.fieldrow');var insertrow = row.before($('#tpl_fieldrow').html());
}
// 上移下移操作功能:向上或下移动当前行
function fieldColOp_click_moveRow(thisobj)
{var row = $(thisobj).parent();var colindex = row.index();if ($(thisobj).hasClass('moverow-up')){if (colindex > 1){row.insertBefore(row.prev());}   } else if ($(thisobj).hasClass('moverow-down')){if (row.next().length == 1)row.insertAfter(row.next());}
}
// 删除一个列 // 如果没有填写列名,则不提示确认删除
function fieldCopOp_click_delRow(thisobj)
{var row = $(thisobj).parent('.fieldrow');var fieldname = row.find('.field-col').eq(1).html();if (fieldname.length == 0){row.remove();refreshEditStatus();return;}var msg = '确定要删除字段 ' + fieldname + ' ?不可恢复!';alertbox(msg,function (){row.remove();refreshEditStatus();})
}
js

转载于:https://www.cnblogs.com/mirrortom/p/7660577.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/415585.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android 向右滑返回,退出当前activity

demo链接&#xff1a;https://download.csdn.net/download/meixi_android/10826597 实现效果: 纯原生类实现方法&#xff1a; 1、自定义侧滑基类SwipeBackActivity public class SwipeBackActivity extends FragmentActivity implements SwipeBackActivityBase {private Swi…

4 微信公众号开发 被动回复消息 回复没有反应怎么办

接收到用户的消息&#xff0c;我们需要做出反馈。 文档地址&#xff1a;https://mp.weixin.qq.com/wiki?tresource/res_main&idmp1421140543 官方返回的数据格式是xml&#xff0c;我们需要转化为json。获取数据&#xff0c;构造xml在返回就OK了。 核心代码&#xff1a; …

Linux系统的远程登录

Linux大多应用于服务器&#xff0c;而服务器不可能像PC一样放在办公室&#xff0c;它们是放在IDC机房的&#xff0c;所以我平时登录linux系统都是通过远程登录的。Linux系统中是通过ssh服务实现的远程登录功能。默认ssh服务开启了22端口&#xff0c;而且当我们安装完系统时&…

更新——Canvas画布动画效果之实现倒计时

Hello&#xff0c;大家好&#xff01; 小W复活啦&#xff01;继续欢乐的给大家更博&#xff0c;输送新知识~~ 不开玩笑啦&#xff01;秒进正题~~~ 上次更博&#xff0c;小W给大家介绍了Canvas画布的基础部分&#xff0c;以及实现了一个由7*10点阵图显示的倒计时的基本架构。 上…

5 微信公众号开发 获取 access_token

在使用微信公众号接口中&#xff0c;需要access_token。access_token是公众号的全局唯一凭证。可以理解为我们服务器的身份证。 总结以上说明&#xff0c;access_token需要做到以下两点&#xff1a; 1.因为access_token有2个小时的时效性&#xff0c;要有一个机制保证最长2个…

Android TextView 设置文字背景色或文字颜色,字体阴影,字体样式

//第一个字符变色String copiesStr_notic tv_notic.getText().toString().trim();SpannableString spannableString2 new SpannableString(copiesStr_notic);//0 第一行缩进像素 , SizeUtils.dp2px(15)非第一行缩进像素Paint mPaintnew Paint();float wmPaint.measureText(cop…

微信公众号 和 微信小程序 用户数据互通 通过微信开放平台的UnionID机制

很多时候&#xff0c;需要将多个公众号和小程序的用户数据打通&#xff0c;我们需要做的&#xff0c;就是将这些公众号和小程序都绑定到同一个微信开发平台上&#xff0c;那么我们就可以获取到UnionID了。其实数据就已经打通了。 UnionID机制说明&#xff1a; 如果开发者拥有…

LR 安装与破解

LoadRunner 11 安装及破解 注意事项&#xff1a; 安装前&#xff0c;把所有的杀毒软件和防火墙关闭。 若以前安装过LoadRunner,则将其卸载。 安装路径不要带中文字符。 如果系统为WIN7&#xff0c;旗舰版才能安装。 安装完毕&#xff0c;需破解。 一&#xff0e; 下载 在HP的官…

从零开始学习springBoot2

简单修改一下: 创建一个包com.kfit.test.web&#xff0c;新建类HelloController 修改 运行访问http://127.0.0.1:8080/得到相同的结果 转载于:https://www.cnblogs.com/zhengzhouyang/p/7680482.html

如何调整压力测试工具

如何调整压力测试工具 如何调整压力测试工具 您是否曾经不得不对应用程序进行压力测试&#xff0c;而最后却发现不明白结果表明什么意义?也许问题不是出在应用程序上。也许问题出在配置压力测试工具的方式上。如果您曾经经历过这种情况&#xff0c;或者正要进行压力测试&#…

未获取root手机抓包方法

没有root的android不能使用类似shark的APP抓包&#xff0c;以下两个不root抓包的方法供参考 未获取root的Android手机抓包方法1&#xff1a;连接PC开启的WIFI PC开启WIFI热点共享&#xff0c;然后手机连接到此虚拟WIFI&#xff0c;pc上使用wireshark选择对应网卡抓包。 win7以上…

栈的应用--迷宫问题

问题描述&#xff1a;给定一个迷宫&#xff0c;给定入口和出口&#xff0c;找到从入口到出口的一条路径(任何一条路径都可以)&#xff0c;迷宫为0表示可走&#xff0c;为1表示墙。用1将迷宫围起来避免边界问题。 实现思路&#xff1a;1.DFS搜索&#xff08;递归&#xff09; 2.…