Tips for eliminating delays in removing CSS transitions

Is there a way to smoothly transition a CSS property, make an immediate change in the CSS property value, and then reattach the transition without any delay? To better understand, consider the following example:

if ($(".marquee").height() < $(".marquee-content").outerHeight(true)) {
  $(".marquee-content").clone().appendTo($(".marquee-wrapper"));
}
$('.marquee-wrapper').css("transition", "transform 3s linear");
$('.marquee-wrapper').css("transform", "translateY(-" + $(".marquee-content").outerHeight(true) + "px)");

setInterval(function() {
  
  $('.marquee-wrapper').css("transition", "none");
  $('.marquee-wrapper').css("transform", "translateY(100px)"); //This should Immediately change translateY to 100px without smooth transition. But this doesn't happen without adding a delay before the below written line
  
  // Its weird why javascript engine executes the below line before executing this line

  $('.marquee-wrapper').css("transition", "transform 3s linear");
  $('.marquee-wrapper').css("transform", "translateY(-" + $(".marquee-content").outerHeight(true) + "px)");

}, 3000);
.marquee {
  margin: auto;
  width: 600px;
  height: 200px;
  overflow: auto;
}

.marquee-wrapper {
  transform: translateY(0);
}

.marquee-content {
  margin: 0;
  padding: 30px 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section class="marquee">
  <div class="marquee-wrapper">
    <div class="marquee-content">
      Updates: Update (8 Mar 2016): Now plugin have new option: startVisible The marquee will be visible in the start if set to true. Thanks to @nuke-ellington 👠Update (24 Jan 2014): Note: people who been asking me how to use this plugin with content being
      loaded with Ajax, please read notes about this update. New methods added, so now after you start the plugin using var $mq = $('.marquee').marquee();, you start the plugin using var $mq = $('.marquee').marquee();, you start the plugin using var $mq
      = $('.marquee').marquee();, then you can pause, resume, togglepause, resume) and desestroy destroy toggle(pause, resume) and destroy toggle(pause, resume) and destroy methods e.g to remove the marquee plugin from your element simply use $mq.marquee('destroy');.
      Similarly you can use pause the marquee any time using $mq.marquee('pause');.
    </div>
  </div>
</section>

Within the setInterval function, the transition is set to none and translateY is set to 100px. Ideally, this should immediately move the div to 100px but the JavaScript engine executes the next line before moving the div to 100px. In the example below, a 100ms delay is added before reassigning the transition property and it works:

if ($(".marquee").height() < $(".marquee-content").outerHeight(true)) {
  $(".marquee-content").clone().appendTo($(".marquee-wrapper"));
}
$('.marquee-wrapper').css("transition", "transform 3s linear");
$('.marquee-wrapper').css("transform", "translateY(-" + $(".marquee-content").outerHeight(true) + "px)");

setInterval(function() {
  
  $('.marquee-wrapper').css("transition", "none");
  $('.marquee-wrapper').css("transform", "translateY(100px)"); //This  Immedeately change translateY to 100px without smooth transition now

  setTimeout(function(){
      $('.marquee-wrapper').css("transition", "transform 3s linear");
      $('.marquee-wrapper').css("transform", "translateY(-" + $(".marquee-content").outerHeight(true) + "px)");
  },100);
}, 3000);
.marquee {
  margin: auto;
  width: 600px;
  height: 200px;
  overflow: auto;
}

.marquee-wrapper {
  transform: translateY(0);
}

.marquee-content {
  margin: 0;
  padding: 30px 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section class="marquee">
  <div class="marquee-wrapper">
    <div class="marquee-content">
      Updates: Update (8 Mar 2016): Now plugin have new option: startVisible The marquee will be visible in the start if set to true. Thanks to @nuke-ellington 👠Update (24 Jan 2014): Note: people who been asking me how to use this plugin with content being
      loaded with Ajax, please read notes about this update. New methods added, so now after you start the plugin using var $mq = $('.marquee').marquee();, you start the plugin using var $mq = $('.marquee').marquee();, you start the plugin using var $mq
      = $('.marquee').marquee();, then you can pause, resume, togglepause, resume) and desestroy destroy toggle(pause, resume) and destroy toggle(pause, resume) and destroy methods e.g to remove the marquee plugin from your element simply use $mq.marquee('destroy');.
      Similarly you can use pause the marquee any time using $mq.marquee('pause');.
    </div>
  </div>
</section>

My questions regarding this scenario are:

  1. Is there a way to prevent the JavaScript engine from reassigning the transition property before changing the translate property without a delay?
  2. Why does the JavaScript engine execute a forthcoming line (
    $('.marquee-wrapper').css("transition", "transform 3s linear");
    ) before the current line (
    $('.marquee-wrapper').css("transform", "translateY(100px)");
    ) in the script?

Answer №1

Combining the CSS properties transition and transform into a single statement produces the desired outcome without the need for a 100 ms delay:

$('.marquee-wrapper').css({ transition: "transform 3s linear", transform: "translateY(-" + $(".marquee-content").outerHeight(true) + "px)" });
setInterval(function () {
    $('.marquee-wrapper').css({ transition: "none", transform: "translateY(100px)" });
    $('.marquee-wrapper').css({ transition: "transform 3s linear", transform: "translateY(-" + $(".marquee-content").outerHeight(true) + "px)" });
}, 3000);

if ($(".marquee").height() < $(".marquee-content").outerHeight(true)) {
    $(".marquee-content").clone().appendTo($(".marquee-wrapper"));
}

$('.marquee-wrapper').css({ transition: "transform 3s linear", transform: "translateY(-" + $(".marquee-content").outerHeight(true) + "px)" });

setInterval(function () {
    $('.marquee-wrapper').css({ transition: "none", transform: "translateY(100px)" });
    $('.marquee-wrapper').css({ transition: "transform 3s linear", transform: "translateY(-" + $(".marquee-content").outerHeight(true) + "px)" });
}, 3000);
.marquee {
  margin: auto;
  width: 600px;
  height: 200px;
  overflow: auto;
}

.marquee-wrapper {
  transform: translateY(0);
}

.marquee-content {
  margin: 0;
  padding: 30px 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section class="marquee">
  <div class="marquee-wrapper">
    <div class="marquee-content">
      Updates: Update (8 Mar 2016): Now plugin have new option: startVisible The marquee will be visible in the start if set to true. Thanks to @nuke-ellington 👠Update (24 Jan 2014): Note: people who been asking me how to use this plugin with content being
      loaded with Ajax, please read notes about this update. New methods added, so now after you start the plugin using var $mq = $('.marquee').marquee();, you start the plugin using var $mq = $('.marquee').marquee();, you start the plugin using var $mq
      = $('.marquee').marquee();, then you can pause, resume, togglepause, resume) and desestroy destroy toggle(pause, resume) and destroy toggle(pause, resume) and destroy methods e.g to remove the marquee plugin from your element simply use $mq.marquee('destroy');.
      Similarly you can use pause the marquee any time using $mq.marquee('pause');.
    </div>
  </div>
</section>


The reason behind this behavior might stem from setting both CSS properties simultaneously causing an immediate repaint, while setting them separately does not.

Certain Javascript commands are known to trigger a repaint, with retrieving the offsetHeight of an element being a commonly mentioned one (refer to this post for more). In fact, this technique was utilized in this article to address a similar issue with CSS transitions. Testing this method by obtaining the element's height between transitions confirms the desired behavior:

$('.marquee-wrapper').css("transition", "none");
$('.marquee-wrapper').css("transform", "translateY(100px)");
$('.marquee-wrapper').height(); // Trigger a repaint
$('.marquee-wrapper').css("transition", "transform 3s linear");
$('.marquee-wrapper').css("transform", "translateY(-" + $(".marquee-content").outerHeight(true) + "px)");

if ($(".marquee").height() < $(".marquee-content").outerHeight(true)) {
    $(".marquee-content").clone().appendTo($(".marquee-wrapper"));
}
$('.marquee-wrapper').css("transition", "transform 3s linear");
$('.marquee-wrapper').css("transform", "translateY(-" + $(".marquee-content").outerHeight(true) + "px)");

setInterval(function () {
    $('.marquee-wrapper').css("transition", "none");
    $('.marquee-wrapper').css("transform", "translateY(100px)");
    $('.marquee-wrapper').height(); // Trigger a repaint
    $('.marquee-wrapper').css("transition", "transform 3s linear");
    $('.marquee-wrapper').css("transform", "translateY(-" + $(".marquee-content").outerHeight(true) + "px)");
}, 3000);
.marquee {
  margin: auto;
  width: 600px;
  height: 200px;
  overflow: auto;
}

.marquee-wrapper {
  transform: translateY(0);
}

.marquee-content {
  margin: 0;
  padding: 30px 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section class="marquee">
  <div class="marquee-wrapper">
    <div class="marquee-content">
      Updates: Update (8 Mar 2016): Now plugin have new option: startVisible The marquee will be visible in the start if set to true. Thanks to @nuke-ellington 👠Update (24 Jan 2014): Note: people who been asking me how to use this plugin with content being
      loaded with Ajax, please read notes about this update. New methods added, so now after you start the plugin using var $mq = $('.marquee').marquee();, you start the plugin using var $mq = $('.marquee').marquee();, you start the plugin using var $mq
      = $('.marquee').marquee();, then you can pause, resume, togglepause, resume) and desestroy destroy toggle(pause, resume) and destroy toggle(pause, resume) and destroy methods e.g to remove the marquee plugin from your element simply use $mq.marquee('destroy');.
      Similarly you can use pause the marquee any time using $mq.marquee('pause');.
    </div>
  </div>
</section>

Answer №2

Let me shed some light on why this phenomenon occurs.
Lately, I've been working with javascript animations, exploring various javascript libraries (focused on mathematical operations) that consider operational delays (based on I/O availability policies) and timeouts.

Within your initial code snippet, you have the following operations:

$('.marquee-wrapper').css("transform", "translateY(100px)");
$('.marquee-wrapper').css("transition", "transform 3s linear");

Transform

The Css Transform method involves matrix-based operations that come with a significant computational cost. Some css-animation frameworks leverage the graphics processor unit (which uses matrix operators) to ensure faster and smoother real-time graphic operations.

Transition

Css Transition is another graphical operation, but it does not alter the css entity with a standard [matrix op matrix] transformation. Instead, it employs a right one-dimensional operator, where your css matrix is adjusted with an [matrix op array].

By selecting the linear mode, it applies linear interpolation (potentially utilizing just a few integration operators) on the element's position. This approach boasts a modest computational cost, making the entire transition operation relatively swift to compute.

The execution process follows a timeline-like schema:

transform         calculation-process
exec -----------------------------------------> applied
 |         |                            |        |
 |         |                            |        |
 |         |                            |        |
transition |     calculation-process    |        |
--------- exec ---------------------> applied --------- 

Due to jQuery's non-blocking code execution (especially for I/O dependent functions, unless specifically coded synchronously) atop javascript, which is fundamental to javascript's asynchronous policy, subsequent operations can commence before prior ones conclude.

Introducing a timeout function within your code guarantees that the transform operation is finalized before triggering the following code. However, one drawback is that this approach only works effectively for clients with comparable computational speeds to the current client's processor. (For instance, code developed on a pc may not function optimally on a smartphone)

Another strategy I often employ in my code involves utilizing jquery callbacks. Check out the jquery animate() documentation, which illustrates:

.animate( properties [, duration ] [, easing ] [, complete ] )

For your case, it could look something like this:

$('.marquee-wrapper').animate({"transform": "translateY(100px)"}, function(){
    // this code runs after transform ends...
    $('.marquee-wrapper').css("transition", "transform 3s linear");
    $('.marquee-wrapper').css("transform", "translateY(-" + $(".marquee-content").outerHeight(true) + "px)");
});

I've come across numerous handy libraries that elevate the animation experience to a new level. Here are some of the libraries I frequently utilize:

d3.js
bounce.js
secuence.js
paper.js

I trust this information proves valuable.

Update
For further insights into animate and css transitions, do check out this insightful answer on Stack Overflow here.

Answer №3

There are some insightful responses here explaining why it works. For a more widely supported animation across browsers, consider utilizing jQuery animations.

$wrap = $(".marquee-wrapper")
$con = $(".marquee-content");
cHeight = $con.outerHeight(true)

if ($(".marquee").height() < cHeight) {
  $con.clone().appendTo( $wrap );
}

function animate() {
  $wrap.animate({
    top: "-=" + cHeight
  }, 3000, "linear", function() { 
    $(this).css("top", "0");
    animate();
  });
}

animate();

//Cache values
$wrap = $(".marquee-wrapper")
$con = $(".marquee-content");
cHeight = $con.outerHeight(true)

if ($(".marquee").height() < cHeight) {
  $con.clone().appendTo( $wrap );
}


function animate() {
  $wrap.animate({
    top: "-=" + cHeight //minus height from the value of top
  },
  3000, // milisecs of animations length
  "linear", // type of animations
  function() { //function to run after animation is complete
    $(this).css("top", "0");
    animate();
  });
}

animate(); //Run function in the beginning
.marquee {
  margin: auto;
  width: 600px;
  height: 200px;
  overflow: auto;
  position: relative;
}

.marquee-wrapper {  position: absolute;  }

.marquee-content {
  margin: 0;
  padding: 30px 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section class="marquee">
  <div class="marquee-wrapper">
    <div class="marquee-content">
      Updates: Update (8 Mar 2016): Now plugin have new option: startVisible The marquee will be visible in the start if set to true. Thanks to @nuke-ellington 👠Update (24 Jan 2014): Note: people who been asking me how to use this plugin with content being
      loaded with Ajax, please read notes about this update. New methods added, so now after you start the plugin using var $mq = $('.marquee').marquee();, you start the plugin using var $mq = $('.marquee').marquee();, you start the plugin using var $mq
      = $('.marquee').marquee();, then you can pause, resume, togglepause, resume) and desestroy destroy toggle(pause, resume) and destroy toggle(pause, resume) and destroy methods e.g to remove the marquee plugin from your element simply use $mq.marquee('destroy');.
      Similarly you can use pause the marquee any time using $mq.marquee('pause');.
    </div>
  </div>
</section>

JSfiddle

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Countdown Timer Integration for Online Auction PHP/jQuery

Currently on the lookout for a countdown timer for my auction site that remains accurate even if a user decides to tamper with their local computer clock. Experimented with Keith Woods and the server sync functionality, yet still searching for an option t ...

Choose an element based on its position in the index (multiple elements with the same class)

Can you use Javascript or jQuery to select an element by its index? For example: <div class="item"></div> <div class="item"></div> <div class="item"></div> <div class="item"></div> If I have 4 elements with ...

What is the recommended approach for embedding scripts within Angular templates?

I am working on an Angular SPA that consists of multiple tabs, each served by templates. Depending on the tab, different scripts (both local and CDN) are required. Therefore, I am looking for a way to load these scripts only when they are needed, which mea ...

Searching for a table element and clicking it based on its text with Protractor

<tr id="item" ng-repeat="item in itemList> <td id="code" ng-repeat="column in columns">Different Text</td> </tr> I have encountered some similar issues before, but I am still struggling to find a solution. Here is what I have at ...

Validating a string using regular expressions in JavaScript

Input needed: A string that specifies the range of ZIP codes the user intends to use. Examples: If the user wants to use all zip codes from 1000 to 1200: They should enter 1000:1200 If they want to use only ZIP codes 1000 and 1200: They should enter ...

Is it possible to line up Ajax request in Javascript?

I am seeking a way to schedule an Ajax request to occur every second. The code I currently have in place functions flawlessly. window.onload = function () { setTimeout(doStuff, 1000); // Wait before continuing } function doStuff() { ...

I am consistently encountering the error message: "Error: Unable to locate module './framer'"

I've been running into the same issue repeatedly. I'm in the process of creating a website for a barbershop and I'm attempting to integrate events into a Google calendar using the Google API. I've installed googleapis and framer, but I ...

Is there a method to obtain over 10 markers on the map?

Trying to find nearby places from my current location. The issue I'm currently facing is that I'm only able to see 10 markers on the map, and no more locations are being displayed. Is there a way to bypass or resolve this problem? I've come ...

html form-- assistance required in making new input area appear upon clicking radio button

Currently, I am in the process of creating a basic customer database using mysql and php. My goal is to have extra input fields appear for mailing address when users click on the radio button labeled "different mailing address". However, I'm unsure ab ...

Incorporating Materialize CSS and MaterialUI within a single React Application

Presently, I have a straightforward non-React application built with HTML, Materialize, and JavaScript that I am considering converting to React while also making the switch from MaterializeCss to MaterialUI. However, I am not looking to make this transiti ...

Utilizing JQuery to extract data from a <select> dropdown menu

Is there a way to retrieve the current value of a SELECT tag using JavaScript or jQuery? I have tried using $('select').val(), but it only returns the default value and does not update when changed. Any suggestions on how to solve this issue? $( ...

StyledTab element unable to receive To or component attributes

Is there a way to use tabs as links within a styled tab component? I've tried using the Tab component syntax with links, but it doesn't seem to work in this scenario: <Tab value="..." component={Link} to="/"> If I have ...

Interact with a webpage element using Selenium

My goal is to extract information from the following page: I want to interact with each blue stats icon on the page (one for every match of the tournament). Below is the code I am using: from selenium import webdriver from selenium.webdriver.common.by im ...

Is it possible to develop a function that can verify various input values and modify their appearance as needed?

I am currently working on creating a form and faced with the challenge of simplifying my code using a function. Specifically, I want to write JavaScript code that will check all input fields in the form to ensure they are filled. If any field is left emp ...

Sending dynamic information to bootstrap's modal using props in VueJS

I'm currently working on a personal project and encountering an issue with the bootstrap modal. My project involves a list of projects, each one contained within a card element. The problem arises when I attempt to display details for each project by ...

What is the best way to add a border to the <map coordinates>?

Is there a way to add a border around the active link section of an image defined by coordinates? I have been able to show an outline when the user clicks using outline-color and href, but now I need a default border around the specified coordinates. I am ...

Utilizing MongoDB and Express to access collections within a controller

Is there a way to access the collection in the controller using mongodb and express? I came across this code snippet in the mongodb documentation db.getCollection("countries");, but how do you import the database name: db into a controller? serv ...

Is it feasible to trigger asp.net validation using jQuery?

I'm facing an issue with a form that has multiple text boxes. I want to only accept floats as input, but users might accidentally enter a dollar sign along with the number. To address this, I have implemented the following code: Using jQuery: $("#&l ...

Utilizing Template Styling for Iterating through Lists in Vue.js

I would like the output format to display one player value followed by one monster value, repeating this pattern for each pair of values. new Vue({ el: app, data: { output: { player: [1, 5, 61, 98, 15, 315, 154, 65], monster: [2,14, ...

following the history.back() function call, the subsequent codes are executed

<?php $ok_register = 0; if($ok_register != 1) { ?> <javascript type="text/javascript"> alert("1"); history.back(); </javascript> <?php } ?> <javascript type="text/javas ...