83

How do I click an element in PhantomJS?

page.evaluate(function() {
    document.getElementById('idButtonSpan').click();  
});

This gives me an error "undefined is not a function..."

If I instead

 return document.getElementById('idButtonSpan');

and then print it,

then it prints [object object], so the element does exist.

The element acts as a button, but it's actually just a span element, not a submit input.

I was able to get this button click to work with Casper, but Casper had other limitations so I'm back to PhantomJS.

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
user984003
  • 28,050
  • 64
  • 189
  • 285

11 Answers11

68

.click() is not standard. You need to create an event and dispatch it:

function click(el){
    var ev = document.createEvent("MouseEvent");
    ev.initMouseEvent(
        "click",
        true /* bubble */, true /* cancelable */,
        window, null,
        0, 0, 0, 0, /* coordinates */
        false, false, false, false, /* modifier keys */
        0 /*left*/, null
    );
    el.dispatchEvent(ev);
}
  • 9
    Wrapping `el` with jQuery gives you this defn of click. It'll work in your browser and in Phantom. `$(el).click()` – Bluu Jun 05 '13 at 02:02
  • 3
    omg tears of joy, after almost 12 hours of continuous struggle with this! sniff sniff! – user1323136 Jan 14 '15 at 16:11
  • 1
    Worked fine for me too, but one must remember not to exit PhantomJS too soon, before the processing is finished. I needed a short script to just click one button and exit. This would not work for me until I called phantom.exit() from within page.onLoadFinished = function() {...}; – gregko Oct 30 '15 at 20:27
  • document.querySelector(element).click() works just fine for me. – GracefulCode Jan 15 '16 at 15:40
  • To work with custom controls, I had to make more complicated solution with 3 events: function triggerMouseEvent(elm, eventType) { var ev = document.createEvent("MouseEvent"); ev.initMouseEvent( eventType, true /* bubble */, true /* cancelable */, window, null, 0, 0, 0, 0, /* coordinates */ false, false, false, false, /* modifier keys */ 0 /*left*/, null ); elm.dispatchEvent(ev); } function click(elm) { triggerMouseEvent(elm, 'mousedown'); triggerMouseEvent(elm, 'mouseup'); triggerMouseEvent(elm, 'click'); } – a-bobkov Jan 19 '16 at 13:43
  • This does not work for me. Could someone take a look at http://stackoverflow.com/questions/41560375/why-does-the-button-click-not-happen-phantomjs and point out if I missed something? – Red Cricket Jan 10 '17 at 04:21
34

Alternatively to @torazaburo's response, you could stub HTMLElement.prototype.click when running in PhantomJS. For example, we use PhantomJS + QUnit to run our tests and in our qunit-config.js we have something like this:

if (window._phantom) {
  // Patch since PhantomJS does not implement click() on HTMLElement. In some 
  // cases we need to execute the native click on an element. However, jQuery's 
  // $.fn.click() does not dispatch to the native function on <a> elements, so we
  // can't use it in our implementations: $el[0].click() to correctly dispatch.
  if (!HTMLElement.prototype.click) {
    HTMLElement.prototype.click = function() {
      var ev = document.createEvent('MouseEvent');
      ev.initMouseEvent(
          'click',
          /*bubble*/true, /*cancelable*/true,
          window, null,
          0, 0, 0, 0, /*coordinates*/
          false, false, false, false, /*modifier keys*/
          0/*button=left*/, null
      );
      this.dispatchEvent(ev);
    };
  }
}
TheCloudlessSky
  • 18,608
  • 15
  • 75
  • 116
  • 1
    A heads up that I'm running Karma 0.12.17 with PhantomJs 1.9.7-14. There is no `window._phantom` object so I just removed that condition and it worked as expected. – BradGreens Jul 22 '14 at 16:51
  • I wonder if the function should accept parameters in order to set different coordinates for the MouseEvent – ffflabs Feb 02 '17 at 15:45
16

It's not pretty, but I've been using this to allow me to use jQuery for the selection:

var rect = page.evaluate(function() {
    return $('a.whatever')[0].getBoundingClientRect();
});
page.sendEvent('click', rect.left + rect.width / 2, rect.top + rect.height / 2);

but you can always replace $(s)[0] with document.querySelector(s) if not using jQuery.

(It does rely on the element being in view mind, i.e. your viewportSize.height is big enough).

stovroz
  • 6,835
  • 2
  • 48
  • 59
8

Hope the following method will be useful. It worked for me in version 1.9

page.evaluate(function(){
    var a = document.getElementById("spr-sign-in-btn-standard");
    var e = document.createEvent('MouseEvents');
    e.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
    a.dispatchEvent(e);
    waitforload = true;
});

This worked for me. Hope this will be useful for others also

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
Jobins John
  • 1,265
  • 23
  • 45
6

With 1.9.2 this worked for me, click handlers were triggered:

var a = page.evaluate(function() {
    return document.querySelector('a.open');
});

page.sendEvent('click', a.offsetLeft, a.offsetTop);
sshaw
  • 994
  • 6
  • 10
6

use simple JavaScript with evaluate, something like this:

page.evaluate(function() {
    document.getElementById('yourId').click();
});
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
HappyCoder888
  • 736
  • 1
  • 8
  • 16
3

I never was able to directly click the element. Instead, I looked at the html to find what function was called with onclick, and then called that function.

user984003
  • 28,050
  • 64
  • 189
  • 285
  • how could you find the function and how you call it if it has been attached through addEventListener? – Suo6613 Jul 16 '15 at 20:40
2

Document.querySelector(element).click() works when using Phantomjs 2.0

click: function (selector, options, callback) {
    var self = this;
    var deferred = Q.defer();
    options = options || {timeout:1000}; 
    setTimeout(function () { 
        self.page.evaluate(function(targetSelector) {
            $(document).ready(function() {
                document.querySelector(targetSelector).click();
            }) ;
        }, function () {
                deferred.resolve();
        }, selector);
    }, options.timeout);
    return deferred.promise.nodeify(callback);
},
GracefulCode
  • 141
  • 7
2

Double clicks are also possible with PhantomJS.

Recommended

This is adapted from the answer of stovroz and triggers a native dblclick including the mousedown, mouseup and click events (two of each).

var rect = page.evaluate(function(selector){
    return document.querySelector(selector).getBoundingClientRect();
}, selector);
page.sendEvent('doubleclick', rect.left + rect.width / 2, rect.top + rect.height / 2);

Other ways

The following two ways only trigger the dblclick event, but not the other events that should precede it.

Adapted from this answer of torazaburo:

page.evaluate(function(selector){
    var el = document.querySelector(sel);
    var ev = document.createEvent("MouseEvent");
    ev.initMouseEvent(
        'dblclick',
        true /* bubble */, true /* cancelable */,
        window, null,
        0, 0, 0, 0, /* coordinates */
        false, false, false, false, /* modifier keys */
        0 /*left*/, null
    );
    el.dispatchEvent(ev);
}, selector);

Adapted from this answer of Jobins John:

page.evaluate(function(selector){
    var el = document.querySelector(sel);
    var e = document.createEvent('MouseEvents');
    e.initMouseEvent('dblclick', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
    el.dispatchEvent(e);
}, selector);

Full test script

Community
  • 1
  • 1
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
2

The easiest way is using jQuery.

page.evaluate(function() {
  page.includeJs("your_jquery_file.js", function() {
    page.evaluate(function() {
      $('button[data-control-name="see_more"]').click();
    });
  });
});
0

For those using JQuery, the JQuery UI created a utility to simulate these: jquery-simulate. I use this in PhantomJS and Chrome

$ele..simulate( "click" );
wbdarby
  • 1,129
  • 12
  • 12