Search code examples
javascriptiframeradio-buttondom-eventscasperjs

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 stumbling in the dark for far to long...

Since I am new to this, I will give details in case 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.


Solution

  • 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");