I have a web page with a text box. When entering in a .js file name into the box and clicking the Execute button, the file given will be loading via AJAX and eval()'d.
Also on this page is an empty <div> for used as output. If the file that is loaded needs to print something to the screen, it adds it to the div's innerHTML.
When adding text to the innerHTML, it usually doesn't render on the screen until the Javascript is completed, which is fine, except for the fact that, sometimes, the Javascript file performs heavy calculations which can take upwards to a minute to complete, and it would be useful to be able to see the output as it was executing.
Is there any way to "refresh" the document?
I would try solving this using setTimeout.
function performInitialHtmlRender() {
/// This sets the innerHtml that you want the user to see
/// While you are performing the calculations
}
function performExpensiveCalculationsAndRender() {
// This is the really expensive function that
// you want to run once the browser has rendered
// the initial HTML
}
performInitialHtmlRender();
setTimeout(performExpensiveCalculationsAndRender(), 0);
You can use setTimeout:
div.innerHTML = html;
setTimeout( function(){
doHeavyCalculations();
}, 35);
http://jsfiddle.net/YKEKK/
Related
I'm trying to clone a button in Google Optimize (or any other javascript/jQuery method) that adds an item into a shopping cart. My problem is that when I run the code or experiment in Optimize, the button doesn't trigger.
Here's my two different attempts. Method 1 via Optimizes' interface.
Select the button to copy, use the 'edit code' to get the html.
Use Insert HTML a the location of the new button, and insert the code.
Version 2 uses jQuery's clone();
$('#SOME-ID > PATH TO THE ELEMENT TO CLONE').clone().attr('id', 'ADD-ID-FOR-GTM-TRACKING').appendTo('#proPriceMobile');
What could the problem be?
Thanks in advance!
It's quite possible, that your experiment fires BEFORE your buttons loads on your page, in your actual browser.
Do you get an error message in console?
My tip would be to wait for your button to load, before applying the DOM changes. If possible, do this all via JavaScript.
For example, here you can wait for an element to load, via using a callback and setTimeout():
// FUNCTION WHICH WAITS FOR YOUR INITAL BUTTON to LOAD
function waitForElement(className, callBack){
window.setTimeout(function(){
// GRAB YOUR ELEMENT
var element = document.querySelectorAll(className)[0];
if(element) {
callBack(className, element);
console.log("Callback successfully fired and code executed...")
} else {
waitForElement(className, callBack);
console.log("This runs every second until your element is loaded on the page..")
}
},1000)
};
// EXECUTE THE CODE IN THIS FUNCTION, WHEN YOUR BUTTON LOADS IN THE DOM
waitForElement(".your-element-classname",function(){
console.log("Button has loaded in DOM...");
// NOW CLONE YOUR BUTTON
});
When I've cloned buttons in the past, I've lost all functionality on the cloned button. So you may need to rebuild that functionality on the cloned button.
I have a simple javascript program that runs onclick of an image.
However, whenever I clicked the image, the page reloaded.
After a lot of debugging I found that the page doesn't reload until right as the script completes.
There are several setTimeouts in the code, but I noticed the page was reloading instantly. I even changed these timeouts to 15000 milliseconds, but it still reloads immediately.
I am using jquery, if it makes any difference.
I also want a different result from the program every time you click it, so that each time you click it a different script runs and a some text changes in a specific order. I did this by changing the onclick attribute of the images in each script to the name of the next script, so that script one would switch onclick to script two, and so on. I set a timeout on these switches so that one click doesn't race through every single script. script two isn't running, so that much works.
my code:
function getSounds() {
console.log("script. initiated");
$("#soundwebGetSoundDirections").html("Now, Wait until the file is done downloading and click below again.");
console.log("new message");
$("#soundwebGetSoundA").attr('href',"");
console.log("href eliminated");
setTimeout($("#soundwebGetSoundImg").attr('onclick','findFile()'),2000);
console.log("onclick to findFile()");
}
function findFile(){
console.log("FINDFILE")
$("#soundwebGetSoundDirections").html("Find the file(it's probably in your downloads), copy the path of the file (usually at the top of the file explorer) and paste in it the box below. Then, make sure there is a '/' at the end of the path and type 'Linkiness.txt' (case sensitive, without quotes) at the end. Once you have all that stuff typed, click the icon again.");
console.log("FIND IT, DARN IT!!");
$("#soundwebGetSoundPathInput").css("opacity",1);
console.log("diving into reader");
setTimeout($("#soundwebGetSoundImg").attr('onclick','readFile()'),1000);
}
function readFile(){
console.log("loading...");
$("#soundwebGetSoundDirections").html("loading...");
if(document.getElementById("soundwebGetSoundPathInput").value.length == 0){
setTimeout($("#soundwebGetSoundDirections").html("Please fill in Path!"),1000);
setTimeout(findFile(),2000);
}
}
and the HTML that's linked to,
<a id = "soundwebGetSoundA" href = "https://docs.google.com/feeds/download/documents/export/Export?id=1ynhHZihlL241FNZEar6ibzEdhHcWJ1qXKaxMUKM-DpE&exportFormat=txt">
<img onclick = "getSounds();" class = "soundwebImgResize" src = "https://cdn3.iconfinder.com/data/icons/glypho-music-and-sound/64/music-note-sound-circle-512.png" id = "soundwebGetSoundImg"/>
</a>
Thanks for any help,
Lucas N.
If you don't want clicking the image to cause the anchor tag to load the href, then move the image tag outside of the anchor tag.
You aren't using setTimeout correctly. You should be passing in a function not a statement. So, for example, instead of
setTimeout($("#soundwebGetSoundDirections").html("Please fill in Path!"),1000);
setTimeout(findFile(),2000);
you should use
setTimeout(function () { $("#soundwebGetSoundDirections").html("Please fill in Path!") },1000);
setTimeout(findFile,2000);
I think the same goes for setting the onclick attribute but I've never tried dynamically changing an onclick attribute like that.
Since you're already using jQuery you could try using .on('click'... and .off('click'... if your current setup isn't working.
I've been looking at how to automate actions on a webpage with PhantomJS, however I'm having issues manipulating the page to do what I want it to.
I'm using this as test site. I've managed to get Phantom to open the webpage and scrape the random sentence from the #result span. But now what I want to do is get another sentence without re-launching the script. I don't want to close and re-open the page as Phantom takes ages to launch the webkit and load the page. So I thought I could get another sentence by getting Phantom to click on the 'Refresh' button below the sentence box. Here's what I have at the moment:
var page = require('webpage').create();
console.log("connecting...");
page.open("http://watchout4snakes.com/wo4snakes/Random/RandomSentence", function(){
console.log('connected');
var content = page.content;
var phrase = page.evaluate(function() {
return document.getElementById("result").innerHTML;
});
console.log(phrase);
page.includeJs("http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function() {
page.evaluate(function() {
$("frmSentence").click();
});
});
var content = page.content;
var phrase = page.evaluate(function() {
return document.getElementById("result").innerHTML;
});
console.log(phrase);
phantom.exit();
});
As you can see I'm trying to click the refresh button by using a .click() function, but this isn't working for me as I still get the same sentence as beforehand. Given the HTML for the button:
<form action="/wo4snakes/Random/NewRandomSentence" id="frmSentence" method="post" novalidate="novalidate">
<p><input type="submit" value="Refresh"></p>
</form>
I'm not sure what I should be referencing in the script to be clicked on? I'm trying the form ID 'frmSentence' but that isn't working. I'm wondering if .click() is the right way to go about this, is there some way for Phantom to submit the form that the button is linked to? Or maybe I can run the associated script on the page that gets the sentence? I'm a bit lost on this one so I don't really know which method I should go with?
You have a problem with your control flow. page.includeJs is an asynchronous function. If you have some other statements page.includeJs, they are likely executed before the script is loaded and the callback is executed. It means in your case that you've read the sentence 2 times before you even trigger a click.
If you want to do this multiple times, I suggest to use recursion since you cannot write this synchronously. Also, since you want this to be fast, you cannot use a static setTimeout with a timeout of 1 second, because sometimes the request may be faster (you lose time) and sometimes slower (your script breaks). You should use waitFor from the examples.
Instead of loading jQuery every time, you can move page.includeJs up and include everything else in its callback. If you only need to click an element or if jQuery click doesn't work (yes, that happens from time to time), you should use PhantomJS; click an element.
web scraping is about sending require information to a web server and get the result. It is not about behaving like a user clicking button or entering search criteria.
All you need to do in this example is send a POST request to http://watchout4snakes.com/wo4snakes/Random/NewRandomSentence. The result is just text in page.content, it does not even need to evaluate. So to get more than one sentence you just need to do a loop of page.open
This is a ColdFusion 8 question.
I have a cfm page that has a button that onClick calls the following Javascript function located in the page header:
function confirm_expiration_letters() {
var resultOfConfirm = confirm("Would you like to send expiration and reminder letters?");
if (resultOfConfirm == true) {
document.body.style.cursor = "wait";
window.location="_expiration_letters.cfm";
}
}
On the page that is called, a series of emails are generated, then a PDF report file is generated and displayed using these two lines:
<cfheader name="Content-disposition" value="attachment;filename=#fileName#">
<cfcontent type="application/pdf" file="#docPath#/#fileName#" deletefile="true">
Notice in the JS function that the cursor is changed to "wait". But the program control appears to get lost after the above cfheader call and so I can't find anywhere that I can reset the cursor back to:
document.body.style.cursor = "default";
Do you have ideas on where I can place this to turn off the cursor animation? I tried multiple places and it doesn't work. Once the cfheader and cfcontent calls happen, control of previous window and cursor are lost it appears.
You might try something like this above the cfheader.
<script>
document.body.style.cursor = "default";
</script>
<cfflush/>
The problem is that doing so might (probably will) screw up the cfheaders since cfflush is designed to flush partial results and will include the headers. But it's the only thing I can think of.
If I understand you correctly, you want to have a "wait" cursor whilst the PDF is prepped, and then return to a standard cursor after that.
Don't web browsers do this automatically when you're waiting for a requested document? IE: as soon as you do your window.location, whilst the document is loading, the cursors automatically changes to a "wait", and then once the doc is served, returns to an "auto".
This is what I see (when running code similar to yours). Is this not what you see?
Instead of changing the cursor, display a loading message using HTML/animated gif. When the PDF loads, it will replace the loading screen.
I would suggest having a hidden div containing your loading message, then use JavaScript to make it appear when needed.
Here's some JavaScript. This is how it would be done with jQuery.
function confirm_expiration_letters() {
var resultOfConfirm = confirm("Would you like to send expiration and reminder letters?");
if (resultOfConfirm == true) {
$('#Loading').fadeIn(); //SHOW THE LOADING INDICATOR
$.post('PDFGenerator.cfm', function(returnData){ // AJAX POST, CALLBACK
//RETURN THE FILENAME OR LOCATION OF THE PDF
var FileName = $.trim(returnData); // TRIM THE RETURNED DATA
window.open("path_to_file/" + FileName,"_blank"); // NEW WINDOW
$('#Loading').fadeOut(); // HIDE THE LOADING INDICATOR
});
}
}
I have a page that on a certain action makes an iframe visible and fills the iframe with some HTML (say for example a multi-select box and an ok button).
The OK button on the iframe has the onClick method defined kinda like this:
onClick="parent.hideIFrame();parent.processMultiSelectBox();"
When User clicks OK on the iframe (presumably after playing with the multi-select box), I'd like the iFrame to disappear immediately and then the selected values in the multi-select box can be processed. But this is not what's happening. The iFrame remains visible during the time the other function runs and disappears only after the second function finishes.
The hideIFrame function is pretty straightforward:
function hideIFrame() {
frmObj = document.all.iFrameID;
if(frmObj) {
frmObj.style.visibility = "hidden";
}
}
I've paraphrased the above function for clarity (removed some indicator variable assignments etc.)
The second function actually loops on all the options in the multi-select object and does stuff with it. This takes about a half a second and only after that is done, does my iFrame disappear. It is a little bothersome to see it linger for half a second when I click ok.
My question is whether there is some way I can make the darn thing disappear faster. Speaking in "classical C" lingo, is there a "flush" for the change in visibility to happen immediately?
I did notice that if I put an "alert" as the first line in my second function, the iframe disappears immediately but now it is the OK on the alert box that lingers for the time it takes the second function to finish.
Thanks.
EDIT: Based on DDaviesBrackett's answer, this is what I ended up doing:
The onclick in the iframe changed to:
onClick="parent.hideAndProcessMultiSelectBox(parm1, parm2);"
The hideAndProcessMultiSelectBox function was defined as:
function hideAndProcessMultiSelectBox( parm1, parm2 ) {
hideIFrame();
setTimeout( function() { processMultiSelectBox( parm1, parm2 ); }, 0 );
}
Voila.. no delay..
You've gotten to the root of your problem already; document reflow doesn't happen until the current JS thread is done (so as not to repaint lots of times during JS execution). You need to return control to the browser before doing your expensive processing.
The simplest way to achieve that, though it doesn't make for obvious code in the slightest, is to call processMultiSelectBox in a setTimeout with a delay of 0:
onClick="parent.hideIFrame();parent.setTimeout(parent.processMultiSelectBox,0);"
If you need to pass parameters to the thing you're setting a timeout on, you have two options: set a timeout on a string that evals to Javascript (bad, bad, very bad, horrible) or define an anonymous function that calls the one you're interested in:
onClick="parent.hideIFrame();parent.setTimeout(function(){parent.processMultiSelectBox(foo, bar, 'baz');},0);"
RSolberg's response may also help, though there's a difference between visibility:hidden and display:none.