Jquery click handler from setTimeout - javascript

I have <a> element which clicked runs handler like:
function onCummReportClick(e) {
if ($(e.currentTarget).attr('href').indexOf('csv') !== -1) {
{
return true;
}
//Here some files loaded asynchronousely
$.ajax().success(...).always(...);
var reports = [];
var downloadedCount = 0;
var reportsCount = 9;
var oneDownloaded = function() {
if (downloadedCount === reportsCount) {
//Prepare cummulative reports CSV
$(e.currentTarget).attr('href', 'data:application/csv;charset=utf-8,'+ encodeURI(reports.join('\n\n')))
//HERE!
$(e.currentTarget).trigger('click');
} else {
setTimeout(oneDownloaded, 1000);
}
};
setTimeout(oneDownloaded, 1000);
}
I.e. it download reports, join its content to one CSV and set it as base64-encoded to original <a>'s href. After that I want it downloaded automatically without user have to click again.
And return true does not generates "normal" click flow, looks like if just skipped. i mean that I running into this handler second time, but download did not started.
If I set up href to generated value statically and in handler just returning true if works as expected - file automatically downloaded.
I know that I could download reports synchronously and make entire method sync, but I dislike this approach.
UPD: I created jsfiddle which demonstrates what I trying to do: https://jsfiddle.net/u1rs17w1/

I do not know why code from JSFiddle does not works, as I understand all assumption about "lost context" is wrong (at least half wrong) because original hyperlink is updated correctly (I mean href attribute uptdated).
But I found another solution. Instead of clicking on original link I do following:
var link = document.createElement('a');
link.download = _messages.CUMMULATIVE_REPORT_NAME;
link.href='data:application/csv;charset=utf-8,'+ encodeURI(csvFile.join('\n\n'));
//These 2 lines are required for Firefox
link.style = "display: none";
document.body.appendChild(link);
link.click();
And it is works even from setTimeout handler. I suspect that clicks by jquery-style, i.e. with return true (I cannot call it somehow clearly) works only from main thread. Each time I works with setTimeout execution continued in new thread. But creating element in new thread and clicking on it works like a charm!
Happy coding!
UPD: This solution does not works in Firefox. Nothing works in Firefox unless synchronous AJAX (ffffffuuuuuuuu!) and return true.
UPD1: Added solution for Firefox.

Related

How to store a newly created element to localStorage or Cookie with Javascript?

I am making an idle clicker game for fun, everything was going fine until I encountered a problem.
What I basically want to happen is when the image is clicked and the clickCounter element is over one, the new image element is created. No problem here, the main problem is saving the image. If the user refreshes the page, I want the created element to still be there. I have tried using outerHTML and following some other Stack Overflow forum questions but I could never get a proper solution to this certain problem. I have also tried localStorage and cookies but I believe I am using them wrong.
My code is below, my sololearn will be linked below, consisting of the full code to my project.
function oneHundThou() {
var countvar = document.getElementById("clickCounter")
if(document.getElementById("clickCounter").innerHTML > 1) {
alert("Achievement! 1 pat!")
var achievement1k = document.createElement("img");
// create a cookie so when the user refreshes the page, the achievement is shown again
document.cookie = "achievement1k=true";
achievement1k.src = "https://c.pxhere.com/images/f2/ec/a3fcfba7993c96fe881024fe21e7-1460589.jpg!d";
achievement1k.style.height = "1000px"
achievement1k.style.width = "1000px"
achievement1k.style.backgroundColor = "red"
document.body.appendChild(achievement1k);
oneHundThou = function(){}; // emptying my function after it is run once instead of using a standard switch statement
}
else {
return
}
}
oneHundThou();
I am aware that there is another post that is similar to this, however, my answer could not be answered on that post.
Full code is here: https://code.sololearn.com/Wwdw59oenFqB
Please help! Thank you. (:
Instead of storing the image, try storing the innerHTML, then create the image on document load:
function oneHundThou() {
countvar.innerHTML = localStorage.getItem('clickCount') ? localStorage.getItem('clickCount') : 0; //before if
//original code
localStorage.setItem('clickCount', countvar.innerHTML); //instead of doc.cookie
}
window.addEventListener('DOMContentLoaded', (event) => {
oneHundThou();
});
or, if you don't care that clickCounter may initialize to null, you can remove the ? : and just put
countvar.innerHTML = localStorage.getItem('clickCount');
Edit: shortened code with the countvar variable

How can I deal with asynchronous requests involving modal popups in Casperjs?

Trying to iterate through a list of links that open modal popups, I'm running into an issue with the asynchronous nature of Javascript. I can loop through the links, and I can get Casperjs to click on all of the links. The popup opens up well (and I need to save the content of that popup). However, my code leads to Casperjs skipping every few links -- I suspect that's because of the delay. I need to be sure that every link is clicked and every popup saved. Any hint is highly appreciated!
I'm aware of Casperjs wait and waitForSelector functions, but no matter where I put them -- it still skips some popups. I suppose the reason for this behaviour is the delay, but increasing/decreasing the wait values and places where I tell casperjs to wait don't help.
this.then(function(){
x = 0;
this.each(links,function(self,link){
// I only need links that contain a certain string
if(link.indexOf('jugyoKmkName')>=0) {
var coursetitle = linktexts[x];
this.clickLabel(linktexts[x], 'a');
this.wait(2000, function() {
var coursetitleSplit = coursetitle.split(' ');
var courseid = coursetitleSplit[0];
//this logs the title and id in a file. Works perfectly
var line = courseid+' '+coursetitle+' \\n';
fs.write('/myappdirectory/alldata.txt', line, 'a');
//this logs the popup contents -- but it's completely out of sync
var courseinfo = this.getElementInfo('.rx-dialog-large').html
fs.write('/myappdirectory/'+courseid+'.html', courseinfo, 'w');
});
}
x++;
});
});
I'm logging two things here -- the link text (and some more information) in a running log file. That's working fine -- it catches every link correctly. The link text contains a unique id, which I'm using as a file name to save the popup contents. That's only working on every nth popup -- and the popup contents and the id are out of sync.
To be precise: The first 10 ids in the list are:
20000 -- saved with this id, but contains data of popup 20215
20160 -- saved with this id, but contains data of popup 20307
20211 -- saved with this id, but contains data of popup 20312
20214 ...etc (saved, but with popup from an ID way further down the list)
20215
20225
20235
20236
20307
20308
Obviously, I need the file 2000.html to save the contents of the popup with the ID 20000, 20160 with the contents of 20160 etc.
Presumably this.each(links,...) will run the callback synchronously rather than waiting for each this.wait() call to complete. Instead you'll want to wait until you've written your data to the filesystem before processing the next link. Consider this code instead:
this.then(function() {
function processNthLink(i) {
var self = this;
var link = links[i];
if (link.indexOf('jugyoKmkName')>=0) {
var coursetitle = linktexts[i];
self.clickLabel(linktexts[i], 'a');
self.wait(2000, function() {
var coursetitleSplit = coursetitle.split(' ');
var courseid = coursetitleSplit[0];
var line = courseid+' '+coursetitle+' \\n';
fs.write('/myappdirectory/alldata.txt', line, 'a');
var courseinfo = self.getElementInfo('.rx-dialog-large').html
fs.write('/myappdirectory/'+courseid+'.html', courseinfo, 'w');
if (i < links.length) {
processNthLink(i+1);
}
});
} else if (i < links.length) {
processNthLink(i+1);
}
}
processNthLink(0);
});
In this case the the next link will only be processed after the timeout and write to FS has been completed. In the case that the link doesn't contain the expected string, the next link is processed immediately.

Script not working on page load, but working from console

I wrote a script that's running from ebay listing iframe. It's working fine, it runs on $(document).ready(), sends an AJAX request to a remote server, gets some info, manipulate the DOM on 'success' callback, everything working perfect...
However, I added a piece of code, which should get the document.referrer, and extract some keywords from it, if they exist. Specifically, if a user searches ebay for a product, and clicks on my product from the results, the function extracts the keywords he entered.
Now, the problem is, that function is not running on page load at all. It seems like it blocks the script when it comes to that part. This is the function:
function getKeywords(){
var index = window.parent.document.referrer.indexOf('_nkw');
if (index >= 0){
var currentIndex = index + 5;
var keywords = '';
while (window.parent.document.referrer[currentIndex] !== '&'){
keywords += window.parent.document.referrer[currentIndex++];
}
keywords = keywords.split('+');
return keywords;
}
}
And I tried calling two logs right after it:
console.log('referrer: ' + window.parent.document.referrer);
console.log(getKeywords());
None of them is working. It's like when it comes to that 'window.parent.document.referrer' part, it stops completely.
But, when I put this all in a console, and run it, it works perfectly. It logs the right referrer, and the right keywords.
Does anybody know what might be the issue here?
The reason it is working on the console is because your window object is the outer window reference and not your iframe. Besides that, on the console:
window.parent === window
// ==> true
So, on in fact you are running window.document.referrer and not your frame's window.parent.document.referrer.
If you want to access your frame's window object you should something like
var myFrame = document.getElementsByClassName('my-frame-class')[0];
myFrame.contentWindow === window
// ==> false
myFrame.contentWindow.parent.window === window
// ==> true
This might help you debug your problem, but I guess the browser is just preventing an inner iframe from accessing the parent's window object.

Chrome JavaScript location object

I am trying to start 3 applications from a browser by use of custom protocol names associated with these applications. This might look familiar to other threads started on stackoverflow, I believe that they do not help in resolving this issue so please dont close this thread just yet, it needs a different approach than those suggested in other threads.
example:
ts3server://a.b.c?property1=value1&property2=value2
...
...
to start these applications I would do
location.href = ts3server://a.b.c?property1=value1&property2=value2
location.href = ...
location.href = ...
which would work in FF but not in Chrome
I figured that it might by optimizing the number of writes when there will be effectively only the last change present.
So i did this:
function a ()
{
var apps = ['ts3server://...', 'anotherapp://...', '...'];
b(apps);
}
function b (apps)
{
if (apps.length == 0) return;
location.href = apps[0]; alert(apps[0]);
setTimeout(function (rest) {return function () {b(rest);};} (apps.slice(1)), 1);
}
But it didn't solve my problem (actually only the first location.href assignment is taken into account and even though the other calls happen long enough after the first one (thanks to changing the timeout delay to lets say 10000) the applications do not get started (the alerts are displayed).
If I try accessing each of the URIs separately the apps get started (first I call location.href = uri1 by clicking on one button, then I call location.href = uri2 by clicking again on another button).
Replacing:
location.href = ...
with:
var form = document.createElement('form');
form.action = ...
document.body.appendChild(form);
form.submit();
does not help either, nor does:
var frame = document.createElement('iframe');
frame.src = ...
document.body.appendChild(frame);
Is it possible to do what I am trying to do? How would it be done?
EDIT:
a reworded summary
i want to start MULTIPLE applications after one click on a link or a button like element. I want to achieve that with starting applications associated to custom protocols ... i would hold a list of links (in each link there is one protocol used) and i would try to do "location.src = link" for all items of the list. Which when used with 'for' does optimize to assigning only once (the last value) so i make the function something like recursive function with delay (which eliminates the optimization and really forces 3 distinct calls of location.src = list[head] when the list gets sliced before each call so that all the links are taken into account and they are assigned to the location.src. This all works just fine in Mozilla Firefox, but in google, after the first assignment the rest of the assignments lose effect (they are probably performed but dont trigger the associated application launch))
Are you having trouble looping through the elements? if so try the for..in statement here
Or are you having trouble navigating? if so try window.location.assign(new_location);
[edit]
You can also use window.location = "...";
[edit]
Ok so I did some work, and here is what I got. in the example I open a random ace of spades link. which is a custom protocol. click here and then click on the "click me". The comments show where the JSFiddle debugger found errors.

Re-render a webpage

I was wondering if there was a way to render a webpage over again, thus calling all the onload events over again, and redrawing the css?
For example, say you changed the pathway of a javascript file that is linked to an onload event and the edited page needed to reload without a refresh in order for the change to take affect.
tested, now is working:
function fireEvent(element,event){
if (document.createEventObject){
// dispatch for IE
var evt = document.createEventObject();
return element.fireEvent('on'+event,evt)
}
else{
// dispatch for firefox + others
var evt = document.createEvent("HTMLEvents");
evt.initEvent(event, true, true ); // event type,bubbling,cancelable
return !element.dispatchEvent(evt);
}
}
setTimeout(function(){
var links = document.getElementsByTagName("link");
var st = [];
for(var x=0;x<links.length;x++)
if(links[x].getAttribute("rel") == "stylesheet")
{
st.push(links[x]);
links[x].wasAtt = links[x].getAttribute("href");
links[x].setAttribute("href", "");
}
setTimeout(function()
{
for(var x =0;x<st.length;x++)
st[x].setAttribute("href", st[x].wasAtt);
setTimeout(function(){
fireEvent(window, "load");
},1000);
},1000);
},5000); // test reload after five seconds
"reload without a refresh" sounds a bit confusing to me.
However I do not know if this is what you are looking for, but window.location.reload() triggers a page reload and thus will load your linked javascript files.
But changing linked files and reload the page is not a good thing to do. It would be better if your dynamic files are loaded in a dynamic way like using Ajax. So you do not need to reload the whole page. Frameworks like JQuery, ExtJS or others provide methods to do this easily.

Categories

Resources