Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
122 views
in Technique[技术] by (71.8m points)

javascript - setTimeout and this binding

I am not very familiar with using jquery other than toggling classes. That said, I will try to explain my problem as good as possible. I have three divs. After clicking one of them, the other two should should flip by 90degs and AFTERWARDS reduce their height to 0

I uploaded a short animation to youTube to show you how the final animation is meant to look like https://youtu.be/4ImmHJ04d0w

So my overly complicated script looks like that at the moment

// Add Temporarily Class
(function($){

    $.fn.extend({ 

        addTemporaryClass: function(className, duration) {
            var elements = this;
            setTimeout(function() {
                elements.removeClass(className);
            }, duration);

            return this.each(function() {
                $(this).addClass(className);
            });
        }
    });

})(jQuery);



$('.review--1').on('click', function() {
$('[class^=review--]').not(this).addClass('review__hidden review__square');
$('.review--2 ,.review--3, .review--4').removeClass('review__hidden');
});

// Animation
$('.review--1').on('click', function() {

$('.review--2 ,.review--3, .review--4').addTemporaryClass('animate-in', 500);
setTimeout(function() {
        $('.review--2 ,.review--3, .review--4').addClass('flip')
    }, 500); 

});


$('.review--2 ,.review--3, .review--4').on('click', function() {
$(this).removeClass('review__square');
$('.review--2 ,.review--3, .review--4').not(this).addTemporaryClass('animate-out', 500);
var that = $(this);
  setTimeout(function() {
        $('.review--2 ,.review--3, .review--4').not(that).removeClass('flip').addClass('review__hidden')
    }, 500); 

        
});
.review--button {
overflow: hidden;
color: #aa7f6f;
width: 100%;
float: left;
height: 50px;
background-color: lightgrey;
}

.review__square {
margin: 6px 3px;
width: 190px;
height: 190px;
text-align: center;
transform: rotateY(90deg);
perspective: 80px;
-webkit-perspective: 80px;
/* transition: height .5s ease, transform .3s ease; */
}
.review__hidden {
height: 0;
margin: 0;
transform: rotateY(90deg);
}
.animate-in {
animation: flip-in .5s forwards;
}



@keyframes flip-in {
from {transform: rotateY(90deg);}
to {transform: rotateY(0);}
}
.animate-out {
animation: flip-out .5s forwards;
}



@keyframes flip-out {
from {transform: rotateY(0);}
to {transform: rotateY(90deg);}
}
.flip {
transform: rotateY(0);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="review--button review--1">
<i>1</i>
</div>

<div class="review--button review__square review--2 review__hidden">
<i>2</i>
</div>

<div class="review--button review__square review--3 review__hidden">
<i>3</i>
</div>

<div class="review--button review__square review--4 review__hidden">
<i>4</i>
</div>

<div class="review--button review__square review--5 review__hidden">
<i>5</i>
</div>

<div class="review--button review__square review--6 review__hidden">
<i>6</i>
</div>

<div class="review--button review__square review--7 review__hidden">
<i>7</i>
</div>
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The this object binding is volatile in JavaScript...that is, it doesn't always point to the same object and its binding can change from one line of code to the very next. How you invoke the code that contains the word this determines what object it will bind to.

Had you cached your this object reference prior to the setTimeout like this:

var self = this;

you could then refer to self in the setTimeout and it would have worked.

Here's a checklist that you can follow to know what this will bind to (your scenario is #3 and you can read more about it here under the "The "this" problem" section)...

If the code that contains this is invoked:

  1. As a method or property of an object instance (through an instance variable):

    var o = new Object(); 
    
    // "this" will be bound to the "o" object instance
    // while "someProperty" and "someMethod" code executes
    o.someProperty = someValue;
    o.someMethod();
    
  2. Via a .call(), .apply(), .bind() or Array.prototype.fn invocation:

    // "this" will be bound to the object suppled as the "thisObjectBinding"
    someFunction.call(thisObjectBinding, arg, arg);
    someFunction.apply(thisObjectBinding, [arg, arg]);
    var newFunc = someFunction.bind(thisObjectBinding, arg, arg);
    

    Note: When a callback function is invoked (i.e. event handler), there is an implicit call to the handler when the event is triggered. In these cases, the object responsible for triggering the event becomes the object bound to this.

    Additionally, several Array.prototype methods allow for a thisObject to be passed which will alter the binding for the duration of the method call:

    Array.prototype.every( callbackfn [ , thisArg ] )
    Array.prototype.some( callbackfn [ , thisArg ] )
    Array.prototype.forEach( callbackfn [ , thisArg ] )
    Array.prototype.map( callbackfn [ , thisArg ] )
    Array.prototype.filter( callbackfn [ , thisArg ] )
    
  3. If none of the other scenarios apply, Default binding occurs.

    3a. With "use strict" in effect: this is undefined

    3b. Without "use strict" in effect: this binds to the Global object

** NOTE: this binding can also be affected by using eval(), but as a general best practice, the use of eval() should be avoided.

Having said all that, I'm not sure why you need a setTimeout at all (if I understood your scenario correctly):

var divs = document.querySelectorAll("div:not(#parent)");

divs.forEach(function(div){
  div.addEventListener("click", function(){
    
    var self = this;
    
    // get other two divs, not this one
    var $otherDivs = $(divs).not(this);
    
    // Fade them out:
    $otherDivs.fadeOut(function(){
       // JQuery animations accept a callback function to run when the animation is complete
       $(self).addClass("clickedDiv");
    });
  });
});
#parent { width:350px; border: 0; background:inherit;}

div {
  width:100px;
  height:100px;
  background:#ff0;
  text-align:center;
  float:left;
  border:1px solid black;
}

.clickedDiv {
  background:#f99;
  width:100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="parent">
   <div>I'm DIV 1</div>
   <div>I'm DIV 2</div>
   <div>I'm DIV 3</div>
</div>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...