Adding transition to delayed dynamic elements? - javascript

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

Related

transitionend method to animate a infinite slider with vanilla Javascript

I'm new to this community and first of all, I take this opportunity to thank all of you for the wonderful work you do every day.
I'm trying to create an infinite manual carousel, in the Netflix style, this is the link to the codepen of everything I have done so far:
https://codepen.io/A12584r/pen/OjvWYp?fref=gc
Here is the relevant javascript:
let prendiContenitoreGalleria = document.querySelector('.contenitore-galleria'),
prendiArticle = Array.prototype.slice.apply(document.querySelectorAll('.contenitore-galleria__article')),
contaArticle = prendiArticle.length,
prendiImmagini = Array.prototype.slice.apply(document.querySelectorAll('.contenitori__img')),
prendiFrecciaSinistra = document.querySelector('.freccia-sinistra'),
prendiFrecciaDestra = document.querySelector('.freccia-destra');
prendiContenitoreGalleria.style.width = 100 * contaArticle + '%';
for (let numeroImmagini = 0; numeroImmagini < prendiImmagini.length; numeroImmagini++) {
prendiImmagini[numeroImmagini].style.width = 100 / contaArticle + '%';
}
prendiContenitoreGalleria.insertBefore(prendiArticle[contaArticle - 1], prendiArticle[0]);
prendiContenitoreGalleria.style.marginLeft = '-' + 100 + '%';
function andareADestra () {
prendiContenitoreGalleria.style.marginLeft = '-' + 200 + '%';
prendiContenitoreGalleria.style.transitionDuration = '.7s';
prendiContenitoreGalleria.addEventListener('transitionend', function(e) {
prendiContenitoreGalleria.appendChild(prendiArticle[0]);
prendiContenitoreGalleria.style.marginLeft = '-' + 100 + '%';
}, false);
}
function andareASinistra () {
prendiContenitoreGalleria.style.marginLeft = 0;
prendiContenitoreGalleria.style.transitionDuration = '.7s';
prendiContenitoreGalleria.addEventListener('transitionend', function(e) {
prendiContenitoreGalleria.insertBefore(prendiArticle[contaArticle - 1], prendiArticle[0]);
prendiContenitoreGalleria.style.marginLeft = '-' + 100 + '%';
}, false);
}
prendiFrecciaSinistra.addEventListener('click', function () {
andareASinistra();
});
prendiFrecciaDestra.addEventListener('click', function () {
andareADestra();
});
I have tried to use the vanilla Javascript transitionend events and what I want to achieve is that when clicking on the right arrow of the carousel the first article is put in place of the third and vice versa, when clicking on the left arrow of the carousel the last article is put in place of the first one.
For this purpose I use marginLeft to move between the articles in my carousel which are 3 and the divs that contains them (these are 3 too) has a width of 300% set via JavaScript.
My problem is that when I click on the carousel arrows, the transition is done but it does a strange effect coming back to its original location immediately.
Any one of you could help me to figure out where I'm wrong and how can I fix it?
In your two functions to move left and right, andareASinistra and andareADestra you are adding an event listener for the transitionend event.
The handler for this is removing the margin-left offset by resetting it to -100% after each move left or right.
You can confirm this is the problem by editing the inline style of the id="contenitore-galleria" element, if you set style="margin-left: 0" then the carousel is moved, then returned to the -100% position.
So the reason it is happening is because that is what you are explicitly telling it to! Delete the lines from both andareASinistra and andareADestra
rendiContenitoreGalleria.style.marginLeft = '-' + 100 + '%';

AJAX - Delay time between displaying AJAX results

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) )

Odin Project Etch a sketch issue with multiple event handlers

I'm working on the js/JQuery project on The Odin Project with a partner, and we've hit a wall with the very last task, which is to increase the opacity of the squares as the mouse reenters them. We got stuck while trying to achieve this via two css classes, and creating mouseenter events for each.
Essentially, we want to start with all of the squares being the grey class, and after entering them for the first time, assigning them the fiftyshades class. When subsequently entering them, we want the event handler for the fiftyshades class to increase the opacity.
However, once the element has both classes, it never enters the mouseenter function for fiftyshades, only for grey. We've already worked around it by putting the opacity logic inside the grey.mouseenter, but according to this answer, I would think that our original approach should work. That says that both handlers should be called, but debugging I can clearly see that fiftyshades.mouseenter is never entered.
This is where we stand after much fiddling with the relevant javascript for this functionality:
$("#fiftyshades").click(function() {
reset();
$(".grey").mouseenter(function(){
if (!$(this).hasClass("fiftyshades"))
{
$(this).addClass("fiftyshades");
console.log("Adding fiftyshades")
if ($(this).hasClass("fiftyshades"))
{
console.log("Add was success");
console.log("Classes: ")
var className = $(this).attr('class');
console.log(" " + className);
}
}
});
$(".fiftyshades").mouseenter(function(){
var shade = parseFloat($(this).css("opacity"));
console.log("Shade: " + shade);
if (shade < 1) {
$(this).css("opacity", shade + 0.1);
}
});
});
Here's a fiddle with the full project. Enter some number to start with, then hit Reset fifty shades to access this functionality.
Much simpler solution is something like this
$(".grey").mouseenter(function(){
var opacity = $(this).data('opacity') || 0;
$(this).css("opacity", opacity = (parseInt(opacity) == 1 ? opacity : opacity + 0.1));
$(this).data('opacity', opacity);
});
But to your actual issue
you are assigning handler before the element actually exists in the DOM, what you should be doing is event delegation
$(".container").on('mouseenter', ".fiftyshades", function (e) {
var shade = parseFloat($(this).css("opacity"));
console.log("Shade: " + shade);
if (shade < 1) {
$(this).css("opacity", shade + 0.1);
}
});
See updated fiddle

Disable 'Previous' button on 1st slide? Nivoslider

I'm using Nivoslider for a web project I am working on.
As you can see from the above link, the "prev" button displays even when on the 1st slide.
Is it possible I can make NivoSlider "linear" and only display the previous button if the slide number is greater than 0?
It seems NivoSlider is set up as 'infinite' or 'circular' by default, but I'm sure there must be a way to alter this.
Many thanks for any help with this :-)
You could use a custom afterChange function when you initialize the slider.
var sliderSelector = '#slider'; // Replace with your slider selector
$(sliderSelector).nivoSlider({
// .. Other config here
controlNav: true, // This solution relies on this to work
afterChange: function() {
// Hide prev button if on first slide
if ($(sliderSelector + ' .nivo-controlNav .nivo-control:first').hasClass('active'))
$(sliderSelector + ' .nivo-prevNav').hide();
else
$(sliderSelector + ' .nivo-prevNav').show();
// Hide next button if on last slide
if ($(sliderSelector + ' .nivo-controlNav .nivo-control:last').hasClass('active'))
$(sliderSelector + ' .nivo-nextNav').hide();
else
$(sliderSelector + ' .nivo-nextNav').show();
},
afterLoad: function() {
// If you have randomStart set to true, then you might check with what slide it started and act accordingly.
// If you use the default, then this should be all you need
$(sliderSelector + ' .nivo-prevNav').hide();
}
});

Image Rotation using pure Javascript

PLEASE DO NOT RECOMMEND JQUERY - I AM DOING THIS EXERCISE FOR LEARNING PURPOSES.
I have implemented a JavaScript, which rotates images (_elementSlideChange) on a timer, using a set interval of 10 seconds. Also I have added a slide functionality to this, which is 7 milliseconds (_slideImage).
The image rotates automatically every 10 seconds on page load, and I have also provided next and previous buttons, which allow the user to change the images manually.
_elementSlideChange: function () {
var myString;
var myText;
for (var i = 0; i < this._imgArray.length; i++) {
var imageArr = "url(" + this._imgArray[i].src + ")";
var imageBg = this._imageHolder.style.background + "";
if (imageArr == imageBg) {
if (i == (this._imgArray.length - 1)) {
myString = "url(" + this._imgArray[0].src + ")";
myText = this._infoArray[0];
} else {
myString = "url(" + this._imgArray[(i + 1)].src + ")";
myText = this._infoArray[i + 1];
}
}
}
this._imageNextSlide.style.background = myString;
this._imageNextSlide.style.background);
this._infoElement.innerHTML = myText;
this._myTimer = setInterval(MyProject.Utils.createDelegate(this._slideImage, this), 7);
},
_slideImage: function () {
if (parseInt(this._imageHolder.style.width) >= 0 && parseInt(this._imageNextSlide.style.width) <= 450) {
this._imageHolder.style.backgroundPosition = "right";
this._imageHolder.style.width = (parseInt(this._imageHolder.style.width) - 1) + 'px';
console.log(this._imageNextSlide.style.background);
this._imageNextSlide.style.width = (parseInt(this._imageNextSlide.style.width) + 1) + 'px';
} else {
console.log("reached 0px");
if (parseInt(this._imageHolder.style.width) == 0) {
this._imageHolder.style.background = this._imageNextSlide.style.background;
this._imageHolder.style.width = 450 + 'px';
this._imageHolder === this._imageNextSlide;
this._imageHolder.className = "orginalImage";
this._imageNextSlide.style.width = 0 + "px";
this._imageNextSlide = this._dummyImageNextSlide;
this._imagesElement.appendChild(this._imageHolder);
this._imagesElement.appendChild(this._imageNextSlide);
clearInterval(this._myTimer);
}
clearInterval(this._myTimer);
clearInterval(this._elementSlideChange);
}
}
So when the user clicks on the Next arrow button, the event listener for "click" is triggered. This creates a div for the current image on display, and creates a new div, which will contain the next image. The image slide and rotation works correctly (whether it's onLoad or onClick). The issue I have is if I click the Next button, while the new div image is sliding into position, it causes it to run into an infinite loop, so the same div with the image to be displayed keeps sliding in, and the more you click the Next button, the faster the image starts to rotate.
I have tried putting a clear interval for the image rotation and slider, but I do understand my code is wrong, which causes the infinite loop of the sliding image. And I know I am close to finishing the functionality.
Can anyone please advise where I could be going wrong? Or should I try to implement the sliding DIV in another way?
Once again please don't recommend jQuery.
And thank you for your help in advance.
Kush
To solve the issue, I did re-write the entire code, where I had a next and previous button event listener.
myProject.Utils.addHandler(this._nextImageElement, "click", myProject.Utils.createDelegate(this._changeImage, this));
Both the buttons will call the same function :
_changeImage: function (e)
In this function I check to see if the function is Transition (changing images),
I declare a boolean var forward = e.target == this._nextImageElement;
Then check to see the current index if forward ? Add 1 else minus 1
this._currentImageIndex += forward ? 1 : -1;
If its at the end of the Array and forward is true, assign the this._currentImageIndex to reset to 0 or Array.length – 1 if it’s in reverse
Then call another function which gives the ‘div’ a sliding effect. In this case call it this._transitionImage(forward);
In this function, set the this._inTranstion to true. (Because the div’s are sliding in this case).
The following code solved the issue i was having.
this._slideImageElement.style.backgroundImage = "url(\"" + this._imgArray[this._currentImageIndex].src + "\")";
this._slideImageElement.style.backgroundPosition = forward ? "left" : "right";
this._slideImageElement.style.left = forward ? "auto" : "0px";
this._slideImageElement.style.right = forward ? "0px" : "auto";
The above code is very important as the object is to place the “sliding in div” Left or Right of the current Visible “div” to the user, and this is mainly dependent on if the forward variable is true or false.
var i = 0;
Then start the transition by
setInterval( function() {
this._currentImageElement.style.backgroundPosition = (forward ? -1 : 1) * (i + 1) + "px";
this._slideImageElement.style.width = (i + 1) + "px";
Notice the forward will determine if the bgPosition will go to the left if its forward as we multiple by -1 or +1,
So for example
If the user clicks NEXT BUTTON,
Forward = true
So the first thing we do is set the
this._slideImageElement.style.backgroundPosition = "left"
Then
this._slideImageElement.style.left = "auto"
this._slideImageElement.style.right = "0px"
This means when the sliding image moves in its background position is LEFT but the div is placed on the RIGHT to 0px;
then this._currentImageElement.style.backgroundPosition = -1 * (i + 1)
Which moves the position of the currentImageElement to the left by 1px,
Increase the width of the slideImage which in this case is right of the current div,
and as the current div moves to the left the sliding image starts to appear from the right. (By default set the width of slideImageElement to 0px so the div exists but isn’t visible to the user). This gives it the slide effect of moving forward new image coming from the right.
this._slideImageElement.style.width = (i + 1) + "px";
then declare it to stop when it it’s the image width. In this case it will be 500px.
if ((i = i + 2) == 500) {
In this if statement reset the currentImageElement background and the background position “right” or “left” don’t really matter as long it has been reset.
Clear the interval
Set the transition to false again
Then call a setTimeout for the function changeImage, which will continue until the slide is completed.
The following shows the reset code as this is very important to prevent repeating the same image (This solved my entire issue)
// set the current image to the "new" current image and reset it's background position
this._currentImageElement.style.backgroundImage = "url(\"" + this._imgArray[this._currentImageIndex].src + "\")";
this._currentImageElement.style.backgroundPosition = "right";
// reset the slide image width
this._slideImageElement.style.width = "0px";
// clear the transition interval and mark as not in transition
clearInterval(this._transitionInterval);
this._inTransition = false;
// setup the next image timer
this._nextImageTimeout = setTimeout(myProject.Utils.createDelegate(this._changeImage, this), 2500);
}
I have provided a thorough detail because then it easier to understand the logic of the problem, and even if your not having the same issue, this may help you fingure out any problem.
I couldn't provide a JSfiddle, as i have created my CSS using Javascript, there are different ways of doing this, but i wanted to understand the logic behind the forward and reverse, and having a timer which continuously goes forward.
It seems like you want to cancel the animation on the slide (perhaps have it fade out while the next slide animates in, cancel its animation abruptly or let it finish and ignore the button click)
What I usually do, personally, is check for the animated state (yes, I use jquery, but you should be able to test the CSS or positioning values you are using to animate in the same way) you could even add an "active" class or data type during animation to make testing easier. Global flags work, too. If there is animation, ignore the button. (For my work... Depends on your intention)
Like I said, the problem may be with button behaviour not with the animation routine. It would be useful to see how you are calling this from the button click, and what your intended results are going to be.
How about CSS3 transitions?
transition: all 1s ease 0.5s;
Simple example on JS Fiddle.
This takes care of the animation, so you just need to set the intended destination using JavaScript, i.e.
this.style.left = '100px';
Or
this.style.top = '30px';
And CSS3 transitions will smoothly slide the element.
Cross Browser Note!
The transition property may need a vendor prefix for some browsers, I am using the latest production Firefox and you don't need -moz for that. Same goes for Opera, no '-o' required. Internet Exporer 10 needs no prefix. You may need to use -webkit for Safari / Chrome, but test without first.

Categories

Resources