超时后无法删除新添加的 HTML 元素

Can't remove new added HTML elements after a timeOut

我尝试创建一个 Google material "ripple" 效果,没有任何库。为此,我使用了 Sitepoint's script for capturing CSS3 Animation 和 "emulate" jQuery 的偏移方法的脚本:

function offset(elt) {
    var rect = elt.getBoundingClientRect(), bodyElt = document.body;
    return {
        top: rect.top + bodyElt.scrollTop,
        left: rect.left + bodyElt.scrollLeft
    }
}

我以Hai Nguyen react touch-ripple file for animation but I don't know even basics of react so I made something like his code mostly for animation. My problem is that for the effect I add a new <span>, after all modification finished and remove the old one after a 1 second delay, for one click it's working well, but if I click several times new <span> elements are still here. I'm far from being a JS guru. So if you see a big mistake, just tell me and I'm gonna improve my code. All code together HTML, SCSS and JS are visible here为例。 我的 HTML:

<button>
    <span class="ripples">
        <span class="ripples-circle"></span>
    </span>Test
</button>
<button>
    <span class="ripples">
        <span class="ripples-circle"></span>
    </span>Test
</button>
<button>
    <span class="ripples">
        <span class="ripples-circle"></span>
  </span>Test
</button>
<button>
    <span class="ripples">
    <span class="ripples-circle"></span>
  </span>Test
</button>
<button class="round">
    <span class="ripples">
        <span class="ripples-circle"></span>
    </span>Test
</button>

我的 SCSS(我使用 Autoprefixer):

body { /*just for remove inline-block lateral margins*/
    font-size: 0;
}

button {
    font-size: 12px;
    height: 4em;
    width: 25%;
    background-color: gray;
    border-style:solid;
    color: #fff;
    cursor: pointer;
    position: relative;
    display: inline-block;
    overflow: hidden;
    padding: 12px 24px;
    margin-top: 0;
    margin-bottom: 0;
    vertical-align: middle;
    transition: all 250ms cubic-bezier(0.23, 1, 0.32, 1) 0ms;

&.round {
    border-radius: 50%;
    height: 100px;
    width: 100px;
}
&:focus {
    outline: 0;
}

&:hover {
    background-color: darken(gray, 5%);
}
}

.ripples {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;

    background-color: transparent;
    opacity: 0.7;
    transition: opacity 1s cubic-bezier(0.23, 1, 0.32, 1) 0ms;

    &.is-active {
        opacity: 1;
        .ripples-circle {
              -webkit-transform: scale(1);
              transform: scale(1);
        }
    }

    .ripples-circle {
        background-color: rgba(255,255,255,.5);
        position: absolute;
        left: 0;
        right: 0;
        height: 100%;
        width: 100%;
        border-radius: 50%;
        -webkit-transform: scale(0);
        transform: scale(0);
        transition: -webkit-transform 1s cubic-bezier(0.23, 1, 0.32, 1) 0ms;
        transition: transform 1s cubic-bezier(0.23, 1, 0.32, 1) 0ms;
    }
}

JavaScript:

;(function(window, document, undefined){
    'use strict';
    //http://www.debray-jerome.fr/js-performance-la-fonction-offset-de-jquery-vs-vanilla-javascript-23.html -- french
    function offset(elt) {
        var rect = elt.getBoundingClientRect(), bodyElt = document.body;
        return {
            top: rect.top + bodyElt.scrollTop,
            left: rect.left + bodyElt.scrollLeft
        }
    }

    //http://www.sitepoint.com/css3-animation-javascript-event-handlers/

    var pfx = ["webkit", "moz", "MS", "o", ""];
    function prefixedEvent(element, type, callback) {
        for (var p = 0; p < pfx.length; p++) {
            if (!pfx[p]) type = type.toLowerCase();
            element.addEventListener(pfx[p]+type, callback, false);
    }
}

function calcDiag(a, b) {
    return Math.sqrt((a * a) + (b * b));
}

    var buttons = document.querySelectorAll('button');
    for(var i=0; i < buttons.length;i++ ){
        buttons[i].addEventListener('click', function(e){

        var rippleThis = this;
        var parentOffset = offset(rippleThis);
        var elHeight = rippleThis.offsetHeight;
        var elWidth = rippleThis.offsetWidth;
        var pageX = e.pageX == undefined ? e.nativeEvent.pageX : e.pageX;
        var pageY = e.pageY == undefined ? e.nativeEvent.pageY : e.pageY;
        var x = pageX - parentOffset.left;
        var y = pageY - parentOffset.top;

        var topLeftDiag = calcDiag(x, y);
        var topRightDiag = calcDiag(elWidth - x, y);
        var botRightDiag = calcDiag(elWidth - x, elHeight - y);
        var botLeftDiag = calcDiag(x, elHeight - y);
        var rippleRadius = Math.max(topLeftDiag, topRightDiag, botRightDiag, botLeftDiag);
        var rippleSize = rippleRadius * 2;

        var j;
        var childrenLength = rippleThis.childNodes.length;
        var targetParent;
        var targetChild;

        for(j = 0; j < childrenLength; j++){
          if(rippleThis.childNodes[j].className == 'ripples') {
            targetParent = rippleThis.childNodes[j];
            targetParent.style.top = (y - rippleRadius) + 'px';
            targetParent.style.left = (x -rippleRadius) + 'px';
            targetParent.style.height = rippleSize +'px';
            targetParent.style.width = rippleSize +'px';
            targetParent.className = 'ripples is-active';
          }
        }

       //New span element
       var rippleSpan = document.createElement('span');
       var rippleCircleSpan = document.createElement('span');
       rippleSpan.className = "ripples";
       rippleCircleSpan.className = "ripples-circle";
       //Add new span
       rippleSpan.appendChild(rippleCircleSpan);
       rippleThis.insertBefore(rippleSpan, rippleThis.childNodes[0]);
    }, false);
    buttons[i].addEventListener('mouseup', function(e){
      prefixedEvent(e.currentTarget, "TransitionEnd", function(e){
      var button = e.currentTarget;
        for(var j = 0; j < button.childNodes.length; j++){
          if(button.childNodes[j].className == 'ripples is-active') {
            var targetParent = button.childNodes[j];
            targetParent.style.opacity = 0;
            setTimeout(function(){
              if(targetParent.parentNode){ targetParent.parentNode.removeChild(targetParent);             
              }
            }, 1000);

          }
        }
    });
    }, false);
}
})(window, document);

看到我想要的最终效果here.

据我从您的链接站点可以看出,从视觉上看,这工作得很好,问题是您的剩余跨度。我相信您的问题实际上是 closure 并且与反应无关。我最近遇到了类似的问题(在完全不同的环境中,但基本原理是相同的),我通过删除超时并使用检查超时条件的时间间隔解决了这个问题。这几乎是它的工作原理:

有一个 javascript 变量来保存所有活动元素及其创建时间的数组。每 1 秒循环一次所有元素并查看它们的计时器是否已经过期。如果是这样,请将其删除。

这显然不是最有效的解决方案,因为它需要(几乎)不断地检查元素,但它肯定会起作用。如果这不是在性能密集型环境中实现的,它应该表现得很好。

此外,如果您找到其他解决方案,我很乐意看看我是否也可以实施。

我找到了解决问题的方法。工作脚本可见 here。 我已经将我的前缀事件移到代码的上部,在它是 mouseup 事件的单独 eventListener 之前,更重要的是我在父 span 元素上调用它,然后它在 button 本身,这就是事件不起作用的原因。我在 setTimeOut 条件中又添加了一个条件:

之前

if(targetParent.parentNode){

之后

if(targetParent && targetParent.parentNode){

现在我的代码可以工作了,spans 从 DOM

中消失了