How to proceed only after all onClick events finished? - javascript

I have a wrapper for the HighCharts lib which autogenerates some code based on their API. In order to autogenerate this code I must export the HTML of the API website with ALL (recursive) the links (from the left side menu) expanded. This must be done recursive as new expanded links may have more not-yet-expanded links.
Right now I must manually proceed this loop from the Browser's Javascript console:
$('div.collapsed').find($('a.plus')).click();
$('div.collapsed').find($('a.plus')).length. If zero, I am done. If none zero, then proceed again with 1).
I tried to automatize this like follows:
while ( $('div.collapsed').find($('a.plus')).length !== 0 ) {
console.log('Doing a pass');
$('div.collapsed').find($('a.plus')).click();
console.log('Pass finished');
}
But it doesn't work as it goes to an endless loop. I guess this is because of onClick fires some async code (maybe an Ajax call?). Any idea how can I make it work?
Thanks in advance,

$('div.collapsed').find($('a.plus')).length is not going to change the value so please use
$('div.collapsed').find('a.plus').each(function(){
//some code
})
for more information regarding each. please check here

I finally fixed it this way:
/* Technically we could simulate the click on the links, like this:
$('div.collapsed').find($('a.plus')).click();
But that won't work as the clicks fire an async AJAX call so we don't know
when to expand their children. To make the recursion to work, I found there are many useful functions
in http://api.highcharts.com/resources/js/api.js
The function toogleExpand() allow us to pass a callback. That way, on callback, we expand again with children
making the recursion to work. */
function expandLinks( items ) {
if (items.find('div[id$="-menu"]').length !== 0) {
items.find('div[id$="-menu"]').each(function(){
var link = $(this);
toggleExpand(link.closest('.collapsed'), function() {
/* note that in this case we are making the recursion but only with the children of items */
expandLinks( items.find('div.menuitem.collapsed') )
});
});
} else {
if( $('div.collapsed').find($('a.plus')).length == 0 ) {
/* If there are no more links to open it means everything is ready so lets download the file */
downloadDetailsDivContent();
}
}
}

Related

assigning anonymous functions to buttons

When i use if statements to determine whether a user has viewed the form before, it breaks my whole code.Originally, my code just removed a child, and set the display of the next DIV to "block".This worked perfectly fine. all my variables saved properly in the end, etc.
However, since I added these checks to the buttons, everything has gone haywire.
I've tried using different mixes of appendChild, removeChild, and style.display methods. I even booted it up in notepad++ to help me visualize.
document.getElementById("newUser").addEventListener("click", function(parent, start, personal){
parent.removeChild(start);
document.getElementById.style.display="block";
});
document.getElementById("toGeneral").addEventListener("click", function(){
if(reUser === 0){
oPersonal;
document.getElementById("general").style.display="block";
} else if(reUser === 1){
oPersonal;
parent.appendChild(oShowInfo);
} else {
window.alert("Whoops this function is in progress");
}
return oPersonal;
});
I want my forms to be editable at the end of the form.
In one test (not this one). I was able to move back and forth between pages, but it would stop the loops that kept my variables up.
This one is using the oldChild = parent.removeChild() method to fix that, which may also be part of the issue.
**Note: variables such as
oPersonal
reUser
parent
are stored just above this code at the beginning of the page loop.
New code:
document.getElementById("newUser").addEventListener("click", function(){
parent.removeChild(start);
document.getElementById.style.display="block";
});
document.getElementById("toGeneral").addEventListener("click", function(){
if(reUser === 0){
oPersonal;
document.getElementById("general").style.display="block";
} else if(reUser === 1){
oPersonal;
parent.appendChild(oShowInfo);
} else {
window.alert("Whoops this function is in progress");
}
return oPersonal;
});
You have many problems in this code. First, your click event's handler can not take 3 args like this. When an event is triggered, only this event is passed to the function. So your code should looks like :
document.getElementById("newUser").addEventListener("click", function(event) {
let element = event.target;
let parent = element.parentNode;
parent.removeChild(element);
//Next line is wrong since getElementById is a function and should take args
//document.getElementById.style.display="block";
});
Then on your second element's click event handler, it seems like you are trying to use parent which is not in the same scope.
Also the return statement is strange, what are you trying to do ?

Why does function only run once?Trying to run function multiple times,after previous invokation complete, using an counter function

I am currently working on a book with page turn effect in jQuery (no plugin). The page turn effect works fine so far, as long as you click through the pages one by one. But now I want to include a dropdown selection (i.e. a select element) so the user can directly jump to the selected content. I tried to make this work with loops and with the .each() method, so that the turnRightPage/ turnLeftPage function is called repeatedly, until the page with the selected content is shown. But after quite a bit of trial and error and a lot of research, I think loops iterate too fast for my turnRightPage /turnLeftPage()-function (which are the transform functions that turn the respective page), in that the loop is done, before the function has completed. I think, what I need to do, is find a way to pause the loop until the function has finished executing and then resume with the next iteration. I think the most promising approach would be using a function with an iteration counter, like it was suggested here:
Javascript: wait for function in loop to finish executing before next iteration (Thanks to jfriend00 at this point) I have also read
Invoking a jQuery function after .each() has completed and
wait for each jQuery
among others, where similar solutions were suggested.
Below is how I tried to implement jfriend00's callback. I added a return statement to break out of that "callback loop", once the number of page turns is completed.
//determine whether to flip pages forward or back - first forward
if(currentPagePos < foundPagePos){ // => turn right page
//determine how many times need to turn page
if (pageDifference > 1 && pageDifference % 2 !=0) {
var numPageTurns = (pageDifference-1)/2;
pageForward (numPageTurns);
} //else if ... rest omitted for brevity
}
function pageForward (numPageTurns){
var i = 0;
function next(){
i++;
if (i <= numPageTurns){
turnRightPage ();
} else {
return;
}
}
next();
};
The full code can be seen here: http://jsfiddle.net/snshjyxr/1/
It DOES turn the page, but only once! What am I missing?
I am still very new to javascript / jQuery so my apologies, if the problem seems all too obvious. Any pointers appreciated. Thx!
The thing is all the page turns are fired, but all at once. You have to wait until each transition is finished to start the next one.
Use a callback function in your turnRightPage and turnLeftPage functions. Example for turnRightPage :
function turnRightPage(callback) {
[...]
//change class AFTER transition (frm. treehouse-site)
$page.on('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend', function () {
//need to double-set z-index or else secondtime turning page open setting z-index does not work (tried in Chrome 38.0.2125.111 m)
$page.css("z-index", turnedZindex + 1);
$(".turned").removeClass("turned");
$page.addClass("turned");
if(typeof callback == "function") {
callback();
}
});
};
And in your pageForward function, use turnRightPage recursively:
function pageForward(numPageTurns) {
console.log("number of FORWARD page turns: " + numPageTurns);
if(numPageTurns > 0) {
turnRightPage(function(){
pageForward(numPageTurns - 1);
});
}
};
Here is your updated jsfiddle. As you can see, there's a remaining bug when you make several page changes which is caused by the fact that you're adding listeners on the transition end every time a page is turned, and never removing them. So they're all executing every time.
EDIT: jsfiddle updated again without the annoying last bug. As you can see, all it took was to unbind the event listener as soon as it's fired.

Javascript try...catch - 2 identical calls, only one works

I'm in the process of redesigning a menu/submenu - just moving the submenu from somewhere in the bottom of the page to under its corresponding menu item. Right now, the same submenu (actions) is found twice in page, only that one of the times it's hidden and displayed with jQuery on mouse over. In both menus I have an option pointing to a function with a try...catch block.
Here's the problem, when using the jQuery-displayed actions, Javascript crashes in the try block ("something" is undefined - classical), just like the word 'try' wasn't even there. I've used the firebug debugger, and the same error occurs when using the other submenu, but that was just skipped and the code from inside catch was executed.
I just don't understand how this is possible, given that both options call THE SAME function, not 2 "identical" ones...
Any thoughts? Thanks :)
EDIT I:
function export_pdf() {
try {
ReportType=document.forms["down"].elements["ReportType"].value;
}
catch (e) {
ReportType='TDR_Report';
}
//other code...
}
And the error is "document.forms.down.elements.ReportType is undefined"...
Thanks!
EDIT II:
Calling the function:
onclick="export_pdf();"
In this case, I would simply try to avoid the try .. catch and test the ReportType. If the ReportType is undefined, set the value manually.
function export_pdf() {
if (document.forms["down"].elements["ReportType"] === undefined){
ReportType = 'TDR_Report';
} else {
ReportType = document.forms["down"].elements["ReportType"].value;
}
//other code...
}

if/else statement in a function: using onclick as a switch

I have looked for solutions to this on google for what seems like an eternity, but I can't seem to formulate my search correctly, or nobody has posted the code I'm looking for earlier.
I am currently trying to make a function that will modify one or several margins of a div element. I want to use an if/else statement within the function, so that the onclick event will switch between the two conditions. This is what I have been working on so far;
function facebookToggle()
{
if($('#facebooktab').style.margin-left == "-250px";)
{
document.getElementById("facebooktab").style.marginLeft="0px";
}
else
{
document.getElementById("facebooktab").style.marginLeft="-250px";
}
}
I have tried twisting it around a little, like switching between "marginLeft" and "margin-left", to see if I was just using the wrong terms.. I'm starting to wonder if it might not be possible to combine jQuery and regular javascript? I don't know.. It's all just guesses on my part at this point.
Anyway, I have a div, which is now positioned (fixed) so almost all of it is hidden outside the borders of the browser. I want the margin to change onclick so that it will be fully shown on the page. And when it is shown, I want to be able to hide it again by clicking it.
I might be approaching this in the wrong way, but I really hope someone can help me out, or even tell me another way to get the same results. Thank you for any help you can give me.
You can see it in action at: http://www.torucon.no/test/
(EDIT: By the way, I am a complete javascript novice, I have no experience with javascript prior to this experiment. Please don't be too harsh, as I am aware I probably made some really stupid mistakes in this short code.)
Fixed problem:
function facebookToggle() {
var fb = $('#facebooktab'); // save reference to element
if( fb.css('margin-left') === '-250px' ) {
fb.css('margin-left', '0px');
} else {
fb.css('margin-left', '-250px');
}
}
A jQuery object doesn't have a property called style, so
if($('#facebooktab').style.margin-left == "-250px";)
// also remove this semi-colon! ^
is going to throw an error. Some options for accessing CSS properties are (1)
document.getElementById("facebooktab").style.marginLeft;
which you have correctly used, or (2)
$('#facebooktab').css('margin-left');
Consider being consistent and using the same approach for all three cases. You can assign css properties with jQuery like
$('#facebooktab').css('margin-left', '-250px');
With these things in mind, here's a suggested rewrite:
function facebookToggle() {
var fb = $('#facebooktab'); // save reference to element
if( fb.css('margin-left') === '-250px' ) {
fb.css('margin-left', '0px');
} else {
fb.css('margin-left', '-250px');
}
}
and here's another that uses a predefined CSS class:
#facebooktab {
margin-left: -250px; /** default state */
}
.no-left-margin {
margin-left: 0px;
}
function facebookToggle() {
$('#facebooktab').toggleClass('no-left-margin');
}
toggleClass
jQuery is just a JavaScript library. It is written in JavaScript and its API is in JavaScript. Your event handler could be rewritten as follows:
function facebookToggle() {
var el = document.getElementById('facebooktab');
if (el)
el.style.marginLeft = (el.style.marginLeft == '250px' ? 0 : -250) + 'px';
}
Since you are mixing jQuery with javascript, you got mixed up. Apart from what paislee's advice. you are do this too.
if($('#facebooktab')[0].style.margin-left == "-250px";){
document.getElementById("facebooktab").style.marginLeft="0px";
}
else {
var fb = document.getElementById("facebooktab");
fb.style.marginLeft="-250px";
}

JavaScript architecture - mediators, when to use them?

This is more of a general question about the structure of my JavaScript code and if I'm going in the right direction towards well structured code.
The current code I've got:
(function (myNamespace, $, undefined) {
myNamespace.className = {
init:function { } // do stuff
}
} (window.myNamespace= window.myNamespace|| {}, jQuery)));
(function (myNamespace, $, undefined) {
myNamespace.className2 = {
init:function { } // do stuff
}
} (window.myNamespace= window.myNamespace|| {}, jQuery)));
Obviously with the above code, I can use the same Namespace (as per page/site section) and call them via myNamespace.className.init() etc. I can also combine these if I want to, but I'm encapsulating classes for readability.
Now, I've been reading http://addyosmani.com/largescalejavascript/ about the concept of mediators. My secondary question is when (and if) I should be using these? From className2 obviously I can do:
myNamespace.className2 = {
init:function { myNamespace.className.init() } // do stuff
}
So why would this ever subscribe to className like mediator.subscribe("classNameInit") and publish that event in className?
I'm highly open to suggestions about the structure of my code as this is something I need to get right whilst I'm changing the way I write my JavaScript.
You would use it when you have multiple pieces which will work together in unlimited combinations where you don't know all combinations ahead of time or where it's more efficient to assume all combinations.
Let's say you were building a social media app and you wrote a class to encapsulate a list of users. On some screens, clicking on a user in the list opens their profile, on another screen perhaps clicking a user searches for every comment they left, and on a third screen something else happens.
If you were to write this not using mediator/pubsub, what you'd end up with is a bunch of if statements in the onclick event...
UserList.prototype.onUserClick = function(user) {
// Check if we're supposed to open a popup
if (this.mode === 'profile')
// Check for something else
else if (this.mode === 'something else')
// Check for another case
else if (this.mode === 'foo')
}
Mediator is a solution to this problem because it doesn't require that UserList have knowledge of every single situation it might end up in. Instead, the above code in UserList could simply be refined to broadcast when a user is clicked on...
UserList.prototype.onUserClick = function(user) {
this.publish('user-click', user);
}
Then each of your other screens or UI pieces can simply listen for the user-click message...
// On pages where there needs to be a popup profile
Mediator.onMessage('user-click', function(data) {
showProfilePopup(data);
});
// Or perhaps on a search page
SearchBox.onMessage('user-click', function(data) {
this.searchByUser(data);
});
Furthermore, where mediator begins to shine is because these other UI components, like SearchBox are not interested in specifically when UserList fires a user-click, they're interested only when a user-click is published, other UI controls on the page can fire user-click as well and these pieces can react to it.
On a side note, className = { } isn't creating a class. What you probably want is className = function() { }.

Categories

Resources