• preventDefault, stopPropagation, return false -JS事件处理中的坑


    我们以一个文件上传ui重设计为例子来探讨这几个函数的区别:

     其中的html代码如下:

    <div class="file-upload">
        <input type="file" name="upload-file" class="file-upload__input" style="display: none;" />
        
        <div class="file-upload__drop-zone">
            <span class="file-upload__drop-zone-text">Drop files here</span>
            <a href="#" class="file-upload__btn--upload">Upload files</a>
        </div>
    </div>

    HTML包含有三部分内容:

    1. 一个inpyout空间来处理文件上传的对话.在这里,我们设置为hidden,因为我们并不想使用浏览器默认给出的控件;

    2. 一个class为file-upload__dropzone的div元素,它作为主要的"drop zone"拖拽区,如果有代码支持的话,可以直接拖拽文件到这个区域。

    3. 一个a元素,它具有file-upload__btn--upload作为其css类,它作为实际的"upload files"按钮,当我们点击它时,就能够打开文件选择对话框

    Javascript部分:

    function fileUpload() {
      document.querySelector('.file-upload__input').click();
    }
    
    const dropzone = document.querySelector('.file-upload__drop-zone');
    const button = document.querySelector('.file-upload__btn--upload');
    
    dropzone.addEventListener('click', fileUpload);
    button.addEventListener('click', fileUpload);

    js也有三部分组成:

    1. 一个fileUpload函数用于触发input元素的click事件

    2.div和a元素获取并赋予变量

    3.给这几个元素添加事件侦听,当click时就调用fileUpload函数以模拟触发file input的点击

    如果我们直接实验,我们会发现很多诡异的行为:当第一个对话框打开并选择文件后,又有第二个会打开

    event.preventDefault()

    该函数用于阻止浏览器的默认行为(比如当我们点击a标签的时候,自然就打开a中的链接,这个行为就是默认的行为),但是并不会阻止事件的bubbling,也就是说其父亲元素依然会接受到对应该元素的事件

    在我们的例子中,当我们点击upload files按钮时,就会调用fileUpload函数,这和我们的预期时一致的。

    而作为a标签,其默认行为是引导浏览器nav到href所指定的url中,在这里,我们设置为#,大多数浏览器解释为跳到页面的top处。

    而跳转到页面的top处可能并不是我们希望的行为,因此,我们可以通过使用preventDefault方法来阻止这个行为。

    通过修改我们的js代码,我们可以阻止a标签的默认行为,但是依然会调用fileUpload函数:

    dropzone.addEventListener('click', fileUpload);
    
    button.addEventListener('click', (event) => {
      event.preventDefault();
      fileUpload();
    });

    这时,虽然点击a标签不再跳转到页面top处,但是文件对话框依然会弹出两次。。。

    需要注意的是:

    1.preventDefault有继承性,如果在父亲元素的事件处理函数中调用了event.preventDefault,那么子元素对应的event默认行为也将丢失

    2.如果希望在子组件中要覆盖这个行为,我们可以通过在子组件的event handler中最后执行 return true; 来恢复!

    调试evenyout.preventDefault

    有的时候,浏览器的行为非常奇怪,往往就是因为在某些组件上调用过preventDefault函数,而我们又无法知晓是哪个元素身上调用的,这时需要调试。

    一个比较好的聪敏的办法是使用类似proxy代理wrapper将event的该函数重新包装:

    var oldEPD = Event.prototype.preventDefault;
    Event.prototype.preventDefault = function() {
        debugger;
        oldEPD.call(this);
    };

    这样的话就会在有该调用时代码停止供我们查找

    event.stopPropagation()

    这个函数可以阻止发生在子元素上的事件继续向上冒泡,但是不会阻止浏览器的默认行为.

    上面的例子中,由于我们点击a元素后,a元素的click handler中会触发对话框,而随后a的click事件继续bubble到上级元素,也就是dropzone,而该dropzone元素也有监听click事件因此就又会被调用fileUpload,这就是为什么文件对话框弹出两次的原因。我们可以通过stopPropagation来阻止事件继续网上冒泡。

    我们来看终级解决方案:

    dropzone.addEventListener('click', fileUpload);
    
    button.addEventListener('click', (event) => {
      event.preventDefault();
      event.stopPropagation();
      fileUpload();
    });

    return false

    通常return false这个代码仅仅在jQuery的事件处理函数中有效,对于原生的js代码并无任何影响。

    而在jquery的代码中return false又有两个效果:

    1. preventDefault

    2. stopPropagation

    const dropzone = $('.file-upload__drop-zone');
    const button = $('.file-upload__btn--upload');
    
    $(dropzone).on('click', fileUpload);
    
    $(button).on('click', (event) => {
      fileUpload();
      return false;
    });

  • 相关阅读:
    web 移动端 适配
    meta
    meta设置
    时间
    CentOS下配置nginx conf/koi-win为同一文件的各类错误
    CentOS7 配置LAMP
    centos 进度条卡死
    LeetCode02:两数相加
    LeetCode01:两数之和
    单链表类,链表逆置
  • 原文地址:https://www.cnblogs.com/kidsitcn/p/11642442.html
Copyright © 2020-2023  润新知