超时后无法删除新添加的 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){
现在我的代码可以工作了,span
s 从 DOM
中消失了
我尝试创建一个 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){
现在我的代码可以工作了,span
s 从 DOM