自定义弹出框输入内容时,blur、focus、click按所希望顺序执行【尤其 blur易产生冒泡】

一、应用场景【效果图】

操作:当点击下方  不X受理 按钮时,打开弹出框【居中显示,layerui】(如上左图),获取焦点(focus)时,弹出框居上显示(如上右图),失去焦点(blur)时,弹框居中显示,代码按照默认给某部分绑定相应处理事件【流转意见框 绑定 监听事件(失去焦点或内容改变时,将输入内容同步 另一区域div显示---此区域内容为提交后台数据库保存,并含有前端验证;而流转意见框做为 不进行验证数据保存);惯用语下方 内容绑定 click、focus事件;取消、确定按钮绑定click事件】,此时会出现 bug(操作不友好):1、当 流转意见框 为获取焦点状态时, 点击取消/确定按钮 不能达到 关闭弹框 效果,而需要点击两下才可关闭【第一次 弹框失去焦点居中(触发blur),第二次 弹框关闭】;2、当 流转意见框 为获取焦点状态时,点击惯用语,也出现1问题,会出现弹出框先下再上情况;十分头痛,昨天从4点-7点,尝试各种方法,什么延迟blur事件触发啦(setTimeout),先绑定再解绑啦【折腾focus、blur】,分别封装focus、blur事件【位置显示分别写个函数】,最后还是没解决,今早来,找了一篇  blur和click事件的先后顺序问题https://www.cnblogs.com/xiaoweiy/p/4961450.html),理解后,终于写出了适合自己所需的效果,再此,由衷的感谢博主大大!!!开始上代码【只是部分啦,自己多练习,能从做过的项目中发现很多不合理的地方,慢慢改进代码,真是满满的成就感】

二、代码

1、默认处理:

1)页面显示

<div class="topTip font014">
                <p class="titleBar">请<span id="HdOrXz"></span><span id="activityName"></span>办理人员</p>
                <div class="gnqBtn">
                    <span class="cancelBtn"  οnclick="closeWindow('cancel')">取消</span> 
                    <span class="submitBtn" οnclick="ok()">确定</span>
                </div>
</div>
            <div class="personContent padding18 font014"></div>
            <div class="suggestContent padding18 font014">
                <p>请填写意见</p>
                <div class="wordCount" id="wordCount">
                    <div class="suggestContentTop">
                        <div  id="suggestArea" class="suggestArea" contenteditable="plaintext-only" spellcheck="false" placeholder="请输入意见..."></div>
                        <div class="gyyMobileBtnPic" picture="minus"></div>
                    </div>
                    <div class="gyyCon">
                   
                    </div>
                </div> <!--end wordCount-->
            </div> <!--end suggestContent -->

2)particpant.js【部分】

流转框绑定focus、blur  【页面上取消、确定按钮 click 绑定自定义函数】

$('.partContent .suggestArea').bind('blur', function(){
               $('.layui-layer-iframe',parent.document).css({
                  'top': '50%',
                  'marginTop': -allH/2+'px' //iframe可见高度而不是内容高度
               });
        });

$('.partContent .suggestArea').bind('focus', function(){
              $('.layui-layer-iframe',parent.document).css({
                 'top': '5%',
                 'marginTop': ''
               });
        });

惯用语部分:(可只看 标橘色部分

/**
 * 渲染惯用语及相应事件 todo 2018-12-11 
 * @param res [] 
 */
function createGyy(res) { 
        if(res && res.length) {
            var gyyConTitle = '';
            var gyyConVal = '<div class="gyyConVal">';
            for(var i = 0; i < res.length; i++) {
                if(res[i].pId == "-1") {
                    gyyConTitle += '<div class="gyyConTitle"><div class="gyyImg"></div>' + res[i].name + '</div>';
                    continue;
                }
                gyyConVal += '<div class="eqYj">' + res[i].name +'</div>'
            }
            gyyConVal += "</div>";
            $(".gyyCon").append(gyyConTitle);
            $(".gyyCon").append(gyyConVal);
         
            $(".eqYj").click(function(){
                var text=$(this).html();
                //2019-3-1 获取焦点,定位光标在文字最右(若有监听则意见同步)
                var el = $(".partContent .suggestArea").get(0);
                $(el).html($(el).html()+text);
                po_Last_Div(el);
                //定位div(contenteditable = "true")
                function po_Last_Div(el) {
                    if (window.getSelection) {//ie11 10 9 ff safari
                        el.focus(); //解决ff不获取焦点无法定位问题
                        var range = window.getSelection();//创建range
                        range.selectAllChildren(el);//range 选择el下所有子内容
                        range.collapseToEnd();//光标移至最后
                    }
                    else if (document.selection) {//ie10 9 8 7 6 5
                        var range = document.selection.createRange();//创建选择对象
                        //var range = document.body.createTextRange();
                        range.moveToElementText(el);//range定位到el
                        range.collapse(false);//光标移至最后
                        range.select();
                    }
                }
            });

            if(res.length == 1) {
                $(gyyConTitle).find('.gyyImg').addClass('imgRotale');
            } else {
                //2018-12-6 优化移动端动画不兼容问题。
                var $gyyConVal = $(".gyyConVal");
                $(".gyyConTitle").click.(function(){
                    if($(".gyyConVal").is(":hidden")) {
                        $gyyConVal.show();
                        $(this).find('.gyyImg').removeClass('imgRotale');
                    } else {
                        $gyyConVal.hide();
                        $(this).find('.gyyImg').addClass('imgRotale');
                    }
                });

            }
        } else {
            $(".suggestContent").find(".gyyCon").hide();
            $(".suggestContent").find(".gyyMobileBtnPic").hide();
        }
        
        var ifm = $(window.parent.document).find("iframe")[0];//iframe 高度
        var gyyH = $(".gyyCon").outerHeight(true); //惯用语高度
        allH = $(".mainContent").outerHeight(true);//弹出层窗口高度
        if(allH >= $(parent.window).height()) {
            var height = $(parent.window).height();
            allH = height*0.6;
        }
        ifm.style.height = allH + 'px';
        //2018/12/10 解决提交弹层不居中问题
        $(".layui-layer-iframe",parent.document).css({
            'marginTop': -allH/2 + 'px',
            'top': '50%',
            'opacity': 1
        })
        $(".partContent .gyyMobileBtnPic").bind('click',function(){
            var $gyy = $(".gyyCon");
            if($gyy) {
                if($gyy.is(":hidden")) {
                    $gyy.show();
                    $(".gyyMobileBtnPic").attr("picture","minus");
                    ifm.style.height = allH + 'px';
                } else {
                    $gyy.hide();
                    $(".gyyMobileBtnPic").attr("picture","plus");
                    ifm.style.height = (allH - gyyH) + 'px';
                }
            }
        });
}

附加:绑定监听函数

/**
 * 将流转意见  同步给 办理意见  2018-11-12
 * @param elem {HTMLElement}        流转意见 div元素
 * @param currYjObj {nuiObj}        当前 可编辑的 办理意见
 */
function syncSug(elem, currYjObj){
    if(currYjObj.getValue()) {
        $(elem).html(currYjObj.getValue());
    } 
    addCurEvent = function (type, callback) {
        elem.addEventListener ?
                elem.addEventListener(type, callback, false) :
                elem.attachEvent('on' + type, callback);
    }
    //对比父页面与弹窗输入意见框值,如果值不同,则替换旧值
    var change = function(){
        if($(elem).text() != currYjObj.getValue()) {
            currYjObj.setValue($(elem).text());
        }
    }
    addCurEvent('propertychange', change);
    addCurEvent('input', change);
    addCurEvent('blur', change);
}

原因:由于JavaScript为单线程,同一时间只能执行处理一个事件。“blur优先于click执行”。而在本场景中,由于blur处理程序,会将弹出框居中显示,所以导致其后续click事件并不会执行,由此出现上文 需点击两下。

解决方案1:对blur事件进行延迟,让click先执行。【很可惜,虽然解决了 取消、确定按钮的期待效果(仅点击一下),但下方惯用语 处 期待效果(点击惯用语,把此条惯用语显示在流转框显示,并获取焦点---此行为有3个事件blur——》click——》focus)未实现】

修改 流转框绑定

$('.partContent .suggestArea').bind('blur', function(){
        setTimeout(function(){//当blur与  取消(click)事件同时触发时  延迟blur

                $('.layui-layer-iframe',parent.document).css({
                    'top': '50%',
                    'marginTop': -allH/2+'px' //iframe可见高度而不是内容高度
                });

        }, 300);
   });

等等方法不再展示,此解决方案适用于 简单的blur、click,无  惯用语 诸如此类的效果 !

解决方案2:将click事件改为mousedown,让其优先于blur事件执行。【此方案可行,但必须加入阻止冒泡事件

背景知识:

mousedown事件:当鼠标指针移动到元素上方,并按下鼠标按键时,会发生mousedown事件。

mouseup事件:当在元素上放松鼠标按钮时,会发生mouseup事件。 

注意: 

(1)mousedown与click 事件不同,mousedown事件仅需要按键被按下,而不需要松开即可发生。 

(2)mouseup与click事件不同,mouseup事件仅需要放松按钮。当鼠标指针位于元素上方时,放松鼠标按钮就会触发该事件。

其执行顺序为:mousedown >> mouseup >> click

参考网址:http://blog.csdn.net/ligang2585116/article/details/51764828

修改成适用于被场景的代码如下:

//    1、获取焦点时,弹出框移到界面最上方
    $('.partContent .suggestArea').on('focus', function(){
        $('.layui-layer-iframe',parent.document).css({
            'top': '5%',
            'marginTop': ''
        });
    }).on('blur',function(){//    失去焦点时,弹出框恢复在中央(ps:易产生冒泡)
        $('.layui-layer-iframe',parent.document).css({
            'top': '50%',
            'marginTop': -allH/2+'px' //iframe可见高度而不是内容高度
        });
    });
//    2、取消/确定按钮 阻止冒泡后,绑定click
    $('.topTip .cancelBtn').on('mousedown', function(event){
        event.preventDefault();
    }).on('click', function(){
        closeWindow('cancel');
    });
    $('.topTip .submitBtn').on('mousedown', function(event){
        event.preventDefault();
    }).on('click', function(){
        $('.partContent .suggestArea').blur();
        ok();
    });

//   3、 惯用语 (//阻止事件(blur)冒泡,再绑定所希望事件(click))
            $(".eqYj").on('mousedown', function(event){
                event.preventDefault();
            }).on('click', function(){
                var text=$(this).html();
                //2019-3-1 获取焦点,定位光标在文字最右(若有监听则意见同步)
                var el = $(".partContent .suggestArea").get(0);
                $(el).html($(el).html()+text);
                po_Last_Div(el);
                //定位div(contenteditable = "true")
                function po_Last_Div(el) {
                    if (window.getSelection) {//ie11 10 9 ff safari
                        $(el).focus(); //解决ff不获取焦点无法定位问题
                        var range = window.getSelection();//创建range
                        range.selectAllChildren(el);//range 选择el下所有子内容
                        range.collapseToEnd();//光标移至最后
                    }
                    else if (document.selection) {//ie10 9 8 7 6 5
                        var range = document.selection.createRange();//创建选择对象
                        //var range = document.body.createTextRange();
                        range.moveToElementText(el);//range定位到el
                        range.collapse(false);//光标移至最后
                        range.select();
                    }
                }
            });

总结:当遇到blur、click、focus3个事件同时触发时、简单场景可用,延迟 blur事件处理;期待效果较为繁琐时,可先 阻止冒泡事件(preventDefault),再绑定期待事件,稍微注意下  绑定对象---给谁绑,总之当使用blur事件时,尤其注意有没同时触发click、focus等,若有,则需考虑是否阻止冒泡。  方法都是一步步测验出来的,也要善于寻找解决方案,看看别人的做法,理解后整理适用于自己的系统。

备注:最近又看到  阻止冒泡和取消默认事件(默认行为)   stopPropagation也是事件对象(Event)的一个方法,作用是阻止目标元素的冒泡事件,但是会不阻止默认行为;event.preventDefault()可以取消默认事件;在触发事件中返回 return false;  选取这些方式时注意适用w3c标准还是IE,参考文章http://caibaojian.com/javascript-stoppropagation-preventdefault.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值