zoukankan      html  css  js  c++  java
  • 简易表格编辑器

    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" onblur="field_input_blur(this)" onkeydown="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" onblur="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
  • 相关阅读:
    PAT (Advanced Level) Practice 1054 The Dominant Color (20 分)
    PAT (Advanced Level) Practice 1005 Spell It Right (20 分) (switch)
    PAT (Advanced Level) Practice 1006 Sign In and Sign Out (25 分) (排序)
    hdu 5114 Collision
    hdu4365 Palindrome graph
    单链表查找最大值、两个递增的链表合并并且去重
    蓝桥杯-最短路 (SPFA算法学习)
    蓝桥杯-最大最小公倍数
    Codeforces-470 div2 C题
    蓝桥杯-地宫取宝
  • 原文地址:https://www.cnblogs.com/mirrortom/p/7660577.html
Copyright © 2011-2022 走看看