Javascript not waiting for return value - javascript

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

Related

how to refresh two functions without reloading the page in html

i need to refresh the blockrandompos and animblock without refreshing the page
var player = document.getElementById("player");
var block = document.getElementById("block");
function goleft(argument) {
player.classList.add("animatel");
setTimeout(function() {
player.classList.remove("animatel");
}, 1);
console.log("left");
}
function goright(argument) {
player.classList.add("animater");
console.log("right");
}
function blockrandompos(argument) {
var block3 = document.getElementById("block");
var posleftblock = Math.floor(Math.random() * 370) + 1; //random numbers from 200 to 300 pos
var info = block3.setAttribute("style", "width:30px; height:30px; position: relative; left:" + posleftblock + "px;"); //block settings
console.log(posleftblock);
}
function animblock(argument) {
block.classList.add("animationblock");
console.log(block.classList);
}
blockrandompos()
animblock()
You are probably looking for an event and subsequently an event-listener that can call a function when the event takes place. Beyond onLoad and onClick, there are a crazy amount of events that you can use.
https://developer.mozilla.org/en-US/docs/Web/Events
So the question is, when do you want to run your functions exactly? Maybe we can help tell you which event to use.

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 + '%';

Function with attr() function not executing all statements when there are multiple elements on screen created dynamically by a function

The function below creates a red dot on the right end of the screen, animate() to move it left and when clicked, its src file should be changed, along with a few other things as you can see below:
function spawnEnemy()
{
$("body").prepend("<img src='red_dot.png' class='enemies' id='enemy' style='left:calc(" + thewidth/2 + "px)'/>");
$("#enemy").attr("id", "enemy"+i);
$("#enemy"+i).css({"left" : thewidth+'px', "top" : (randomInt(10,500))+'px'});
$("#enemy"+i).animate({left :'-400px'}, 20000);
$("#enemy"+i).click(function()
{
var snd = new Audio("laser.mp3");
snd.play(); //function stops here
$("#enemy"+i).stop();
$("#enemy"+i).attr("src","explosion.gif");
$("#enemy"+i).css({"transform" : "scale(0.11,0.11)"});
setTimeout(function()
{
$('#enemy'+i).hide();
}, 350);
});
i+=1;
}
This function is executed every six seconds. If, however, the element is not clicked before a new element is created, then all of the elements when clicked stop executing at the line where I have placed a comment. I checked the console, and I couldn't find anything of value, although I know that the problem isn't with the ids as they all have different ids in the console, and since I can hear the sound from snd.play() and none of the effects of the statements after snd.play() are seen, I'm sure the problem lies there. This problem is only when multiple elements are created, as they all execute fine if I click the element before six seconds, i.e, before the function is executed again. Also, thewidth is just a pre-defined global var, so it's not related.
I'm very new to jquery, so I apologize if there's something obvious I'm missing.
Edit: Okay, now all the statements aren't executing even if there is only one element on the screen. I'm quite lost.
Ok, I think I see what's happening. You need to reference enemy with $(this) inside the callback, because i is in the encapsulating scope, which means it can change and break the internal scope of that callback:
While at it, I cleaned up your jQuery code a little to simplify it.
function spawnEnemy() {
$('<img />', {
id: 'enemy' + i,
src: 'red_dot.png',
className: 'enemies',
style: 'left: ' + thewidth / 2 + 'px; top: ' + randomInt(10,500) + 'px'
})
.prependTo($('body'));
.animate({left :'-400px'}, 20000);
.click(function() {
var snd = new Audio("laser.mp3");
snd.play(); //function stops here
$(this).stop();
$(this).attr("src","explosion.gif");
$(this).css({"transform" : "scale(0.11,0.11)"});
setTimeout(function() {
$(this).hide();
}.bind(this), 350);
});
i+=1;
}

Fetching more shots using Jribbble

So, a question that I've been asking myself is how one should load more shots using the jRibbble plugin. Sadly, I couldn't find anything on this subject that makes sense to me. I've only been using JavaScript for about a week now, so I apologize if this appears clumsy. My code so far is as follows:
$(document).ready(function () {
// this is jRibbble example code if I recall correctly
$.jribbble.getShotsByPlayerId('name', function (playerShots) {
var html = [];
$.each(playerShots.shots, function (i, shot) {
html.push('<li><a href="' + shot.url + '">');
html.push('<img src="' + shot.image_url + '" alt="' + shot.title + '"></a></li>');
});
$('#grid').html(html.join(''));
}, {page: 1, per_page: 6});
$(window).on('load resize', function () {
window.shotHeight = $('#grid li').outerHeight(true);
});
});
$(window).scroll(function () {
if ($(window).scrollTop() + shotHeight > $(document).height() - $(window).height()) {
// so that there's a time buffer between the time of request and when the user reaches the bottom of the page (and thus expects new posts)
}
});
I don't think that it's necessary to mention how thankful I'd be if someone could at least provide some pointers.
Note: as I've already received a downvote I'd like to explain that I'd like to get some pointers, and not that I'm too lazy to code myself.
So, I've found the answer to my question. All I've to do is rerun the getShotsByPlayer, increasing the 'per_page' every time I do so. Then, just merge the two arrays. I should've known this.

Not sure why this isn't working - Javascript basic increment function

I am writing code to increment the most basic progress bar ever...... it just isn't working.
These are the variable used:
map.progress_bar = // the progress div that grows inside a container div that is the width of the screen (100%);
progress = 0;
state.window_width = // the width of the window or otherwise $(window).width();
setTimeout(function incProgress(){
if ( map.progress_bar.width() < state.window_width ) {
progress += 10;
map.progress_bar.css({
width: String(progress + '%')
});
console.log('progress: ', map.progress_bar.width());
console.log('window: ', state.window_width);
setTimeout(incProgress(), 300);
}
}, 300);
Please do not ask me to do setInterval. Please explain to me why on earth this does not work, I feel very unhappy.
setTimeout(incProgress(), 300);
You are calling the function and passing its return value (undefined) to setTimeout.
You need to pass a function to setTimeout.
Remove the ().

Categories

Resources