ChE Junkie ChE Junkie - 28 days ago 22
Javascript Question

How to: Select Radio Option Button in iFrame Using CasperJS?

I am new to JavaScript, new to CasperJS, etc. However, I have watched a couple of YouTube videos and worked through some examples using CasperJS. From this vantage I thought I had a fairly good grasp of the basics and was ready for my first project, which I am currently now stuck on. How to click a radio option button?

I do not know if it is something unique to the site I am working with or some additional holes in my understanding, but nothing I try seems to work. I would greatly appreciate any input; I have been stumbiling in the dark for far to long...

Since I am new to this, I will give details incase there are some things I have done wrong, or should consider doing etc. Thanks!

The Problem



When I open the site URL: http://www.ddbst.com/unifacga.html I am greeted by four options:
enter image description here
So I right-click on the control and use DevTools to get information on the elements:

<input name="selection" value="Upload" id="ID1" type="radio" onclick="this.form.submit()">
<input name="selection" value="MolinspirationJME" id="ID2" type="radio" onclick="this.form.submit()">
<input name="selection" value="Insert" id="ID3" type="radio" onclick="this.form.submit()">
enter code here
<input name="selection" value="DDBSearch" id="ID4" type="radio" checked="checked" onclick="this.form.submit()">


Here I am interested in DDBSearch (option no. 4). I have yet to do an example with radio buttons, and I originally thought that I could just do somehting like e.g. value=true, but then I see that value is actually a text string, so I click on the button to see what its active state looks like:

<input name="selection" value="DDBSearch" id="ID4" type="radio" checked="checked" onclick="this.form.submit()">


So I know that I need to somehow activate the property checked, but I have been unable to do so by XPath, ID, Name, etc. Not knowing what the problem was I tried to report more error information using other examples from the web.

Casper Constructor Options



I setup my casper object as follows:

var casper = require("casper").create({
logLevel:"debug",
verbose:true, // log messages will be printed out to the console
pageSettings:{ // the WebPage instance used by Casper will use these settings.
loadImages: false,
loadPlugins: false,
webSecurityEnabled: false
},
onDie: function(){
console.log("Script complete.");
},
// Display message every time any page successfully initializes:
onPageInitialized: function(){
console.log("\rPage initialized.\r");
},
waitTimeout: 10000,
stepTimeout: 10000,
onWaitTimeout: function(){ // invoked when you are waiting for an element to be visible, e.g. after clicking a button, and waitTimeout has been exceeded.
this.echo("** Wait-TimeOut **");
},
onStepTimeout: function(){ // NOT REALLY SURE WHAT THIS IS USED FOR ???
this.echo("** Step-TimeOut **");
}
});


Trying to catch additional error information by using code like e.g.

casper.on('remote.message', log);
casper.on('error', logError);
casper.on('complete.error', logError);
casper.on('page.error', logError);


Requiring additional functions:

function log(msg){
console.log("\r");
console.log('Remote msg>: ', msg);
console.log("----------------------------------------------------","\r");
}

function logError(msg,traceback){
console.log("\r");
console.log(msg);
console.log(traceback);
console.log("----------------------------------------------------","\r");
}


Not really sure where I got this from, or how it really works, but it looked like it should report additional error information, so I went for broke.

Verify Page Structure



I read somewhere that you should verify the structure of the page first, so I based it on the form opeform, which the radio options are contained within. At this point I also noticed that the additional input fields that I am really tring to get to (after option no. 4 is selected) are just hidden on the page--not sure if this is important or not?

Seems to me that it would be easier to work with the hidden elements of the form; thereby, bypassing the radio option issue that I am currently having? (Just a thought burning in the background for now..)

<form name="opeform" method="post" action="http://ddbonline.ddbst.com/OnlinePropertyEstimation/OnlineUNIFACGroupAssignmentSA.exe">
<hr>
<table border="0" style="white-space: nowrap; font-size:10pt;width: 560px; text-align: left;" cellpadding="2" cellspacing="2">
<tbody><tr><td>
<h3>Input Method</h3><input name="selection" value="Upload" id="ID1" type="radio" onclick="this.form.submit()"><b>Upload</b> a Mol- or CTC-file directly from your hard drive<br>
<input name="selection" value="MolinspirationJME" id="ID2" type="radio" onclick="this.form.submit()"><b>Molinspiration JME</b> allows to draw and edit molecules (Java required)<br>
<input name="selection" value="Insert" id="ID3" type="radio" onclick="this.form.submit()"><b>Insert</b> the content of a Molfile in a textfield<br>
<input name="selection" value="DDBSearch" id="ID4" type="radio" onclick="this.form.submit()"><b>DDB Search</b> (retrieves a structure from the chemical structure database ChemDB)</td>
</tr>
</tbody>
</table>
<input type="hidden" id="ID9" name="molstruct" value="">
</form>


So I use the CasperJS assert system to make sure that the form opeformis in place before continuing:

var selector = "#unifacga";
casper.then(function(){
console.log("\r>>> Check if the element exists.");
console.log(">>> If the element does not exist, the test (i.e. our script) will fail, otherwise it will simply continue.\r");
this.waitForSelector(selector,
function pass () {
this.capture("open.png");
console.log("\r",">>> Element found");
console.log("1: ",this.evaluate(function(sel){return !!document.querySelector(sel)},selector));
console.log("2: ",this.evaluate(function(sel){return document.querySelector(sel).textContent;},selector));
console.log(">>> Continue.","\r");
},
function fail () {
this.capture("fail.png");
this.die(">>> Did not load element... something is wrong T_T","\r");
}
);
});


Up to this point everything is roses an sunshine...

Try No.1 - Radio Option Select by XPATH



I first tried using XPath, because this is what a lot of examples use for clicking buttons:

var x = require("casper").selectXPath;
// //*[@id="ID4"] is the XPath statement.
// input type that has the attribute id as radio.
casper.thenClick(x('//*[@id="ID4"]'));
casper.wait(5000).then(function(){
casper.capture("option_sel4.png");
});
casper.run();


For which I get an error like:
enter image description here
It says nonexistant selector, but the syntax looks right (as far as I can tell), and the ID is most definately "ID4" (as shown above from DevTools).

Try No.2 - Radio Option Select by ID (and Name)



So I next tried to work with the object by ID (and name):

casper.evaluate(function(){
document.getElementById("ID4").checked=true;
});
casper.run();


Now I get a different error:
enter image description here
So now I get a type error. Looks like the getElementByID method as I have used it may not return the object or something? Any other variation like e.g. getElementByName results in similar errors...

Try No.3 - Casper.Click



I also tried CasperJS.click:

casper.then(function() {
this.click('input[id="ID4"][checked="checked"]');
});
casper.run();


Which results in another error like XPath:
enter image description here

In Conclusion



I am not sure how to resolve the issue. Seems like everything I try to select radio option 4 results in some type of error; however, I do not know enough about JavaScript and CasperJS to troubleshoot any furhter than I already have. Other solutions that I have tried from online, so far, have not worked for me. Any help would be greately appretiated.

Answer

Use my function click2iframe, and don't forget --web-security=no option, e.g. ./casperjs --web-security=no snap.js

function click2iframe(sel_of_the_iframe,sel_in_the_iframe){
var event=document.createEvent('MouseEvents'),element=document.querySelector(sel_of_the_iframe).contentDocument.querySelector(sel_in_the_iframe);event.initMouseEvent('click',1,1,window,1,0,0,0,0,0,0,0,0,0,null); element.dispatchEvent(event);
}
var casper = require('casper').create();
casper.start('http://www.ddbst.com/unifacga.html').wait(5000).then(function(){
this.capture('test0.png');
this.evaluate(function(click2iframe){
click2iframe('iframe[name="ircframe"]','input[id="ID4"]');
},click2iframe);
});
casper.wait(5000).then(function(){this.capture('test.png');}).wait(2000);
casper.run();

Bonus example: click3_iframe function, if you need to click on an element which is located in the 3rd iframe:

<iframe id="1"><iframe id="2"><iframe id="3">
<a id="some_id" href="">some text</a>
</iframe></iframe></iframe>

function click3_iframe(sel_of_the_iframe,sel_of_the_iframe2,sel_of_the_iframe3,sel_in_the_iframe){
var event=document.createEvent('MouseEvents'),
element=document.querySelector(sel_of_the_iframe).contentDocument.querySelector(sel_of_the_iframe2).contentDocument.querySelector(sel_of_the_iframe3).contentDocument.querySelector(sel_in_the_iframe);
event.initMouseEvent('click',1,1,window,1,0,0,0,0,0,0,0,0,0,null); element.dispatchEvent(event);
}
click3_iframe("iframe#1","iframe#2","iframe#3","a#some_id");