casperJS How select element(s) by specific starting text with querySelector or querySelectorAll

Marco picture Marco · Aug 13, 2014 · Viewed 17.1k times · Source

I have to use casperJS to upload one file to a customer server, now before uploading I need to emulate a click on two specific links, these links (simple HTML anchors) don't have a name/id/class... (really ugly HTML code) so I have the only option to select it by it's text content.

How can I find it using the querySelector or querySelectorAll methods?

So far, I could come up with the following (unsuccessful attempts) :(

querySelector("a[text()='texttofind']");
querySelector("a[text='texttofind']");
querySelector("a[text=texttofind]");

EDIT AFTER ALL SUGGESTION

TITLE UPDATED to be more specific about my problem that seem related only to casperjs

PLATFORM - Windows 7 - CasperJS version 1.1.0-beta3 - phantomjs version 1.9.7 - Python 2.7

so, probably i'm too dumb :( now i post a complete example that sadly don't work for me :(

HTML main index

<html>
<head>
<title>TEST Main Page</title>
</head>
<frameset cols="100,100" >
    <frame name="menu_a" src="menu_1.html">
    <frame name="menu_b" src="menu_2.html">
</frameset>
</html>

HTML menu_1.html

<html>
<head>
<title>TEST Menu 1</title>
</head>
<body style="background-color:red;">
<h3>Menu 1</h3>
<select onchange="javscript:alert('test')" id="test" size="1" name="systemID">
    <option value="0">---</option>
    <option selected="selected" value="1">TestMenu1            </option>
    <option value="17">TestMenu2                               </option>
</select>
</body>
</html>

HTML menu_2.html

<html>
<head>
<title>TEST Menu 1</title>
</head>
<body style="background-color:orange;">
<h3>Menu 2</h3>
 <a href="javascript:alert('test')"><b>clickhere   </b></a>
 <a href="javascript:alert('noclickhere')"><b>NoClickHere   </b></a>
</body>
</html>

CasperJS script

start equal for all tests:

var casper = require('casper').create();

casper.start(serverName, function(){});

first test - clicklabel as suggested by @Ka0s

casper.then(function(){
    this.withFrame('menu_b', function(){
        this.clickLabel('clickhere', 'a');
    });
});

result:

CasperError: Cannot dispatch mousedown event on nonexistent selector: xpath selector: //a[text()="test"]
  /bin/casperjs/modules/casper.js:1355 in mouseEvent
  /bin/casperjs/modules/casper.js:462 in click
  /bin/casperjs/modules/casper.js:487 in clickLabel
  /test.js:90
  /bin/casperjs/modules/casper.js:1553 in runStep
  /bin/casperjs/modules/casper.js:399 in checkStep

this not work even if i cleanup the string removing blank spaces at the end of the clickhere string on my test code.

second test - xPath as sugggested by @ArtjomB

casper.then(function(){
    this.withFrame('menu_b', function(){
    this.evaluate(function(){
     var element = __utils__.getElementByXPath("//a[starts-with(text(),'clickhere')]");
     console.log(element);
    });
 });
});

result:

remote message caught: undefined

so i suppose that the xPath fail finding the element.

Third test - querySelectorAll with for loop as suggested by @Brunis

This is a strange beaviour, casperJS return the content of href instead the object this not seem an error on below code but a problem on my implementation or something else.

casper.then(function(){
    this.withFrame('menu_b', function(){
        this.evaluate(function(){
        var as = document.querySelectorAll("a");
        var match = "clickhere";
        var elems = [];     
        for (var i=0; i<as.length; i++){
            if (as[i].textContent === match) {
                elems.push(as[i]);
            }
        }
        console.log(elems[0]);
    });
 });
});

Result: remote message caught: javascript:alert('test')

i obtain the href code not the object! if i try this example in a fiddle, i receive the object and i can call onclick() on it.

Answer

Brunis picture Brunis · Aug 13, 2014

Here's a simple loop that matches the text of the link you want and adds them to an array:

the html:

<div>
    <a name="test">not this one</a>
    <a name="test">not this one</a>
    <a name="test">not this one</a>
    <a name="test">not this one</a>
    <a name="test">this one</a>
</div>

and the script:

var as = document.querySelectorAll("a");
var match = "this one";
var elems = [];

for (var i=0; i<as.length; i++){
    if (as[i].textContent === match) {
        elems.push(as[i]);
    }
}

now you have the matched elements in the elems array.

fiddle here: http://jsfiddle.net/0pLd8s9r/