I have a search feature which uses an AJAX request to get data from my web-server.
I want to have a fade in animation which gets applied to each search result, with a slight delay so the last result fades in last (i.e. the first result loads, starts fading in, next one loads, starts fading in, etc.).
I have the code which loads the html into the search results area, but it seems like the results are displaying and running their "fade-in-animation" at the same time - although this could also be due to the fact that my computer is too slow.
Here's me code:
JS
$.ajax({
type: 'GET',
url: '/PersonSearch',
data: {
'search_value': search
},
dataType: 'json',
})
.done(function(json) {
$('#main').html('');
$.each(json, function(key, value) {
var search = $('<div />', {
id: 'search' + key,
'class': 'search-item off',
html:
'<div class="basic-info">' +
'<span class="first-name">' + value.First_name + '</span>' +
'<span> </span>' +
'<span class="last-name">' + value.Last_name + '</span>' +
'</div>' +
'<div class="dropdown">' +
'<span class="phone-number">' + 'PHONE: ' + value.Phone_number + '</span>' +
'<span class="email">' + 'EMAIL: ' + value.Email_address + '</span>' +
'<div class="box edit"><img src="../media/gear.svg"/></div>' +
'</div>'
}).appendTo('#main');
setTimeout(function() {
search.removeClass('off');
});
});
});
CSS
.search-item.off{
opacity: 0;
top: 8px;
}
.search-item{
overflow: hidden;
position: relative;
opacity: 1px;
top: 0;
transition: .75s;
}
HTML
<div id="main">
</div>
Basically what the code does (so you do not need to piece it together yourself) is it adds the search result which has the class of search-item off, and once the <div> is loaded (using setTimeout()) it removes the off class, which then uses the transition CSS attrib to make it fade in over time.
I tried using setTimeout() on the .appendTo('#main') but that did not work.
I need it so that there is a delay in posting each of the search results in the #main element so that there is a delay in running the fade in animation.
Your idea could work, but you need to add a little delay to your call to setTimeout. The delay must be increased for each new result. To be sure that it is working, use a long delay at first (1000, ie. 1 second) then adjust with lover values as desired.
setTimeout(function() { ... }, 1000 * index);
Below is a simple snippet that illustrate the use of setTimeout to delay the successive calls to append
$(function() {
var $container = $('#container');
$.each(['foo', 'bar', 'qux'], function(i, value) {
setTimeout(function() {
$container.append('<div>' + value + '</div>');
}, 1000 * i);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
</div>
try use
setTimeout(function() { ... }, 0);
It will wait that your content is fully loaded.
This happens because setTimeout works asynchronously which means these setTimeout functions will start at 5ms, 10ms, 13ms, etc. and fire off at similar times as well. What you can do is define a timeout variable above ajax call and increase timeout at each call before setTimeout and give this timeout variable as the timeout value to the setTimeout. Following is an example (I know too much timeout):
var timeout = 0;
$('div').each(function() {
var $this = $(this);
timeout += 1000;
setTimeout(function() {
$this.hide('slow');
}, timeout);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
Try using the timeout in the callback of the $.each() function.
$.each(json, setTimeout(function(key, value) {...},1000) )
Related
I'm trying to add a 'slide in to the left' animation to the class .bot-dialog. This class is dynamically generated and my setTimeout function does not seem to be doing justice.
One of the issues I noticed was that it doesn't work ONLY when the typing indicator is active. Here is my code:
Generate my bot dialog
$("#conversation").html(
"<div class='bot-log'>" +
"<span class='bot'>Chatbot: </span>" +
"<span class='current_msg bot-dialog dialog' style='left:-40px; position:relative;'>TEST" +
"</span> </div>"
)
var $to = $(".bot-dialog");
setTimeout(function() {
$to.css("left", 200 + "px");
}, 0);
$(".current_msg").hide();
//Add and remove typing indicator when firing typing function
$(".bot-log:last-child").append(
'<div class="typing-indicator"><span></span><span></span><span></span></div>'
);
$(".typing-indicator").delay(800).fadeOut(function() {
$(".typing-indicator").remove();
});
$(".current_msg").delay(1200).fadeIn(function() {
$(this).removeClass("current_msg");
if (typeof callback == "function") callback();
});
CSS for dialog/transition
.bot-dialog{
transition:5s all ease;
}
Here is a jsfiddle showing the issue.
Thanks for the help!
The thing is, you're hiding it, which sets it to display: none which affects the css transition. Just use opacity instead.
I've changed the transition from all to left so that the test appears instantly, if you want a smooth opacity, change it back to all
https://jsfiddle.net/Lpdx2a0x/4/
Just change the settimeout from 0 value greater than 0 like for example I have used 1 millisecond.
Change:
var $to = $(".bot-dialog");
setTimeout(function() {
$to.css("left", 200 + "px");
}, 0);
To:
var $to = $(".bot-dialog");
setTimeout(function() {
$to.css("left", 200 + "px");
}, 1);
And now it works fine.
EDIT : I am not sure exactly what animation you are looking for but when I remove $(".current_msg").hide(); and increase the delay it works as per you want.
FIDDLE
Here's my function:
function showGeneralWarning(msg) {
if (getIDQuantity("generalWarning") < 1) {
$("#Warnings").append('<p id="generalWarning" class="alert alert-danger" style="display: none;">' + msg + '</p>');
$("#generalWarning").fadeIn('slow').animate({opacity: 1.0}, 100).effect("pulsate", { times: 5 }, 1000);
$("#generalWarning").fadeOut('slow').animate({opacity: 1.0}, 100).effect("pulsate", { times: 5 }, 1000);
}
}
It fades in perfectly, but it stays there forever (It never fades out and go away).
This is kinda simple example, hope its what you looking for
$('.fadein').hide().fadeIn('slow').delay( 800 ).fadeOut('slow');
https://jsfiddle.net/3spc9541/
Some more on chaining
http://www.w3schools.com/jquery/jquery_chaining.asp
You can use the complete function from fadeIn to fadeOut.
as stated on the link
complete
Type: Function()
A function to call once the animation is complete, called once per matched element.
FadeIn api
I'm dynamically updating a few elements after a setTimeout() function. The jQuery function .text() seems to dynamically update with each change of index of an array while processing. But a bootstrap progressbar which is being changed through .css() and .attr() doesnt seem to dynamically update. Here is my page : http://imdbnator.com/process.php?id=f144caf0843490c0d3674113b03da0c5&redirect=false
You can see that the text gets changed but the progress bar only finishes after the whole setTimeout() function finishes. Also, if I set the delay = 1000. It works. But it slows down by application. Therefore, I need delay = 0. But why doesnt the progressbar change?
Here is my snippet
function launch(a) {
var inc = 0;
var maxc = a.length;
var delay = 0; // delay milliseconds
var iID = setInterval(function () {
var index = inc;
var movie = a[inc];
//start processing function
//styling while processing
var markerPer = ((index + 1) / rawListNum) * 100; // marker percentage
$("#procNum").text("(" + (index + 1) + "/" + rawListNum + ")"); //Processing number
$("#procMovie").text(movie); //Processing Name
$("div[role='progressbar']").css("width", markerPer + "%").attr("aria-valuenow", markerPer); // progress bar -> DOES NOT WORK
if (++inc >= maxc) clearInterval(iID);
},
delay);
}
By way of explanation, a lot of answers are going to point out the fact that most browsers run JavaScript on the UI thread, so they cannot update the interface when JavaScript is being executed. Instead, the browser will wait for the Javascript to finish and return control to the UI thread.
While this is important to keep in mind, it doesn't come into play for your code. If you were updating the progress bar doing something like this:
for (i = 0; i < len; i++) {
updateProgressBar(i)
};
Then it would definitely prevent the UI from updating until the end.
But you're already doing the right thing by using setTimeout or setInterval, which have asynchronous callbacks into the code. Meaning that the JavaScript is able to pause for long enough to pipe out any UI messages. As evidenced by the fact that the text is able to update dynamically.
As your question asks, why, if the UI is getting updated, is the progress bar not updated as well?
The answer lies in the fact that Bootstrap applies the following CSS to the progress bar:
.progress-bar {
-webkit-transition: width .6s ease;
-o-transition: width .6s ease;
transition: width .6s ease;
}
When the delay between calls is 0ms, the browser does have time to update the UI, but does not have time to complete the 600ms CSS transition. Consequently, you don't see the animation until the end.
As OhAuth points out, you can prevent the browser from delaying the width update by removing the transition effect with CSS like this:
.progress .progress-bar {
-webkit-transition: none;
-o-transition: none;
transition: none;
}
If you're looping through a lot of items, the incremental updates will provide some sort of animation. If you wanted to leave on the styling for a generic use case, you could toggle the transitions off and on in your code. As of jQuery 1.8 you can update CSS properties without the vendor prefix, so you'd just need to call .css("transition","none").
Of course, depending on how much work you're doing for each movie, the JavaScript may be able to finish before you can visually detect all of the UI changes, in which case extending the delay wouldn't hurt. If you'd like to test out longer running processes, you can use the following sleep function:
function sleep(sleepyTime) {
var start = +new Date;
while (+new Date - start < sleepyTime){}
}
Here's a demo you can play around where each setting is a variable that you can configure to see how it would react, but the best bet is just to conditionally remove transitions.
Demo in Stack Snippets
var delay, numMovies, throttledMovies, sleepTime, removeTransition;
$("#delay").change(function(){ delay = this.value }).change()
$("#movies").change(function(){ numMovies = this.value }).change()
$("#sleep").change(function(){ sleepTime = this.value }).change()
$("#transition").change(function(){ removeTransition = this.checked }).change()
$("#loadMovies").click(loadMovies);
function loadMovies() {
var i = 0;
throttledMovies = Movies.slice(0, numMovies)
if (removeTransition) {
$('#progress-bar-movie').css("transition","none");
}
var interval = setInterval(function () {
loadMovie(i)
if (++i >= numMovies) {
clearInterval(interval);
$('#progress-bar-movie').css("transition","width .6s ease");
}
}, delay);
};
function loadMovie(i) {
var movie = throttledMovies[i];
var percentComplete = ((i + 1) / numMovies) * 100;
sleep(sleepTime);
// update text
$("#procNum").text("(" + (i + 1) + "/" + numMovies + ")");
$("#procMovie").text(movie.Title);
// update progress bar
$('#progress-bar-movie')
.css('width', percentComplete+'%')
.attr('aria-valuenow', percentComplete);
};
function sleep(sleepyTime) {
var start = +new Date;
while (+new Date - start < sleepyTime){}
}
<link href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.2/css/bootstrap.css" rel="stylesheet"/>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.2/js/bootstrap.js"></script>
<script src="http://kylemitofsky.com/libraries/libraries/movies.js"></script>
<!-- params -->
<label for="delay">Delay (ms)</label>
<input type="number" value="0" min=0 max=1000 id="delay"/>
<label for="movies"># Movies </label>
<input type="number" value="250" min=0 max=250 id="movies"/>
<label for="sleep">Sleep time (ms)</label>
<input type="number" value="0" min=0 max=1000 id="sleep"/>
<label for="transition">Remove transition? </label>
<input type="checkbox" checked="true" id="transition"/><br/><br/>
<button class="btn btn-primary btn-lg" id="loadMovies">
Load Movies
</button><br/><br/>
<p>
<b>Current Title</b>: <span id="procMovie"></span><br/>
<b>Progress</b>: <span id="procNum"></span>
</p>
<div class="progress">
<div class="progress-bar" role="progressbar" id="progress-bar-movie"
aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
</div>
It is due to the way bootstrap animates changes in progress bar state. If the timeout interval is smaller than the animation time it will queue the redraw.
Try adding this to your progress bar's CSS:
-webkit-transition: none;
transition: none;
Check my fiddle
Another work around (with reference to my previous answer):
If you must keep the animation, you could use a conservative interval delay of say 100 milliseconds. A delay which shouldn't be noticeable to the user.
If you really need the delay to be zero for the calculations then I would separate the calculations and the setting of the progress bar value so that you are calculating the value in one setInterval, and then run a separate setInterval that updates the bar every 100 ms to this value.
This way your calculations are up to date and your UI has time it needs to update as well.
If you want them both in the same method then I think you need to add a minimum 100 ms delay.
Example: https://jsfiddle.net/dk7g51g4/3/
Javascript:
var _ValueToSet = 0;
$(document).ready(function () {
// SET VALUE EXTREMELY FAST (your current code)
setInterval(function () {
_ValueToSet = Math.round(Math.random() * 100); // get value we are going to set
}
, 0); // run as fast as possible
// RUN UI UPDATE AT 100MS (separate thread for ui updates).
setInterval(function () {
setRandom()
}
, 100); // give ui 100ms to do its thing
});
function setRandom() {
var elem = $('.progress-bar'); // what is our target progress bar
// set both style width and aria-valuenow
elem.css('width', _ValueToSet + '%');
elem.attr('aria-valuenow', _ValueToSet);
}
HTML:
<div class="progress progress-striped active">
<div class="progress-bar"
style="width:0%"
role="progressbar"
aria-valuenow="0"
aria-valuemin="30"
aria-valuemax="100">
</div>
</div>
This is probably a redraw issue. Since the delay is set to 0 the CSS changes aren't drawn until the last update.
The easiest way to force a redraw is to read the elements height:
$("div[role='progressbar']").css("width", markerPer + "%").attr("aria-valuenow", markerPer);
$("div[role='progressbar']").height();
Text and Progress Bar change dynamically, but with delay === 0 it's very fast: (http://jsfiddle.net/sergdenisov/mh4o1wxz/1/):
HTML:
<div class="text">Text</div>
<div class="progress">
<div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" style="width: 0;">
</div>
</div>
Javascript:
var delay = 0;
var percent = 0;
var iID = setInterval(function() {
percent += 10;
$('.text').text('Text ' + percent + '/100');
$('.progress-bar').css('width', percent + '%').attr('aria-valuenow', percent);
if (percent === 100) {
clearInterval(iID);
}
}, delay);
<div id="usu" ></div>
Then I have script that is supposed to add content to that:
<script>
var haelisaanro = 10
function haelisaa() {
$.getJSON("/json/index_oikea.aspx?usu=1", function (data) {
var items = [];
items.push("<div id='rivit" + haelisaanro + "' >");
$.each(data, function (key, val) {
items.push("<div class=\"col-md-12 column nlnyR \"><table><tr><td><div class=\"ot\"><span class=\"glyphicon glyphicon-play\"><\/span><\/div><\/td><td><a href='" + key + "'>" + val + "</a><\/td><\/tr><\/table><\/div>");
});
items.push("</div>");
$("#usu").append(items.join(""));
$('#rivit' + haelisaanro).hide();
$('#rivit' + +haelisaanro).slideDown("slow", function () {
// Animation complete.
});
});
haelisaanro += 10;
}
</script>
I have this on pageload too:
<script>
$(document).ready(function() {
$( "#oikear" ).load( "/index_oikea.aspx", function() {
$( "#oikear2" ).load( "/index_oikea2.aspx", function() {
//alert('lahataanTop of thing hit top of viewport.');
});
});
});
</script>
index_oikea.aspx is loaded to #oikear div. Loaded content has <div id="usu" ></div> in the middle of loded content index_oikea.aspx.
It does fill that are "usu" with data, but there is no slidedown animation on it.
I tryed to add height attribute css to id='rivit'. It seemed like it needs the height to work, but I could not manage to make it work with right height size.
I think the main reason this is not working is that browser can not calculate the height of dynamicly added content somehow...?
If I changethe code... Add this style:
<style>
.dplno{display: none;}
</style>
change to this:
items.push("<div class=\"col-md-12 column nlnyR dplno \"><table><tr><td><div class=\"ot\"><span class=\"glyphicon glyphicon-play\"><\/span><\/div><\/td><td><a href='" + key + "'>" + val + "</a><\/td><\/tr><\/table><\/div>");
and change this:
$( '.dplno' ).slideDown( "slow", function() {
Now every div line is sliding out, but every line is sliding one by one. I needed to slide all those lines at onece.
You are concatenating with + + sign, that may be problem
$('#rivit' + +haelisaanro).slideDown("slow", function () {
// Animation complete.
});
Thus it may be forming wrong id.
Change to this:
$('#rivit'+haelisaanro).slideDown("slow", function () {
// Animation complete.
});
You are hiding the div before applying slideDown. That might be causing your issue.
You should first define animation and then hide the div.
$('#rivit' + +haelisaanro).slideDown("slow", function () {
// Animation complete.
});
$('#rivit' + haelisaanro).hide();
Always thought that javascript was not asynchronous unless Ajax or something else was used. I'm not using Ajax calls or anything, but my JS function doesnt seem to wait for a return value:
I have three lines of code as follows:
var areas = prepareComparer();
console.log('Areas');
console.log(areas);
I need to create an overlay div suing the following function:
function prepareComparer() {
var comparorSelection = d3.select('body').append('div').attr('id', 'comparor').attr('style', 'line-height: 100px;position: fixed;top: 5%; left:5%;background-color: white;border-radius: 5px;text-align: center;z-index: 2;border-style:solid;border-width:1px;box-shadow: 10px 10px 5px #888888')
.transition().duration(500).style('height', '800px').style('width', $(window).width() * .9 + 'px').style('opacity', '1').each('end', function () {
//Waiting for end of transition to append the comparees: Otherwise, causes size adjustment problems since comparee sizes depend on the FULL height.
d3.select('#comparor').append('div').attr('id', 'comparee1').attr('style', 'position:absolute;top:0px;left:0px;width:' + $('#comparor').width() * .45 + 'px;height:' + $('#comparor').height() + 'px')
.append('svg');
d3.select('#comparor').append('div').attr('id', 'comparee2').attr('style', 'position:absolute;top:0px;left:' + $('#comparor').width() * .5 + 'px;width:' + $('#comparor').width() * .45 + 'px;height:' + $('#comparor').height() + 'px')
.append('svg');
d3.select('#comparor').append('div').attr('id', 'destroyComparor').text('Click Here To Go Back').on('click', function () {
d3.select('#comparor').transition().duration(500).style('width', '0px').style('height', '0px').style('opacity', '0').remove();
});
});
setTimeout(function () { console.log("Hello"); return ['#comparor #comparee1', '#comparor #comparee2'] }, 5000);
}
Now, for this, my log shows up as:
Areas
undefined
Hello
Ignore the d3 code, as commenting or uncommenting it doesnt seem to make much of a difference. Just to be sure, Ive introduced a timeout of 5 seconds. So, after 5 seconds I get the message Hello, meaning that return also might be happening after that. Im getting the undefined message almost immediately.
I'm not really sure why this is happening and have been trying to decode this for ages. Can anyone help?
Edit
Wanted to return a data on completion of new div creation(#comparee1 and #comparee2) in the code. But, a delay was introduced due to the d3 transitions which are asynchronous. And since the new divs were created only after transition completion(there were size dependencies), the newly created divs could not be selected by my main function immediately.
Resolved this problem with using JavaScript Promises:
function prepareComparer() {
promise = new Promise(function (resolve, reject) {
var comparorSelection = d3.select('body').append('div').attr('id', 'comparor').attr('style', 'line-height: 100px;position: fixed;top: 5%; left:5%;background-color: white;border-radius: 5px;text-align: center;z-index: 2;border-style:solid;border-width:1px;box-shadow: 10px 10px 5px #888888')
.transition().duration(500).style('height', '800px').style('width', $(window).width() * .9 + 'px').style('opacity', '1').each('end', function () {
//Waiting for end of transition to append the comparees: Otherwise, causes size adjustment problems since comparee sizes depend on the FULL height.
d3.select('#comparor').append('div').attr('id', 'comparee1').attr('style', 'position:absolute;top:0px;left:0px;width:' + $('#comparor').width() * .45 + 'px;height:' + $('#comparor').height() + 'px')
.append('svg');
d3.select('#comparor').append('div').attr('id', 'comparee2').attr('style', 'position:absolute;top:0px;left:' + $('#comparor').width() * .5 + 'px;width:' + $('#comparor').width() * .45 + 'px;height:' + $('#comparor').height() + 'px')
.append('svg');
d3.select('#comparor').append('div').attr('id', 'destroyComparor').text('Click Here To Go Back').on('click', function () {
d3.select('#comparor').transition().duration(500).style('width', '0px').style('height', '0px').style('opacity', '0').remove();
});
resolve(['#comparor #comparee1', '#comparor #comparee1']);
});
});
}
and the function using the data would be called as:
promise.then(function (response) {
window[func](response[0], res, 'label', 'value');
});
Just wanted to share the solution.
Your function prepareComparer() has no return. In JavaScript functions without return return undefined.
The return inside the setTimeout() is irrelevant for prepareComparer().
And here you found an other aspect of asynchrous programming in JavaScript besides Ajax.
put a return before setTimeout cause function will wait set timout finished
look like this
return setTimeout(function () { console.log("Hello"); return ['#comparor #comparee1', '#comparor #comparee2'] }, 5000);
sorry doesn't work but as you have no return areas will always be undifined.
for the async problem traceur.js introduce await keyword that will probably be usefull for you