1

I tried targeting the below, but can't seem to get it to work. The error I get is

Error: Node is either not clickable or not an HTMLElement.

JavaScript

const btn = await page.$x(//button[contains(., 'My Button')])
await btn[0].click()

HTML/XML

<div>
<button>
<translate original="My Button">My Button</translate>
</button>
</div> 
Prophet
  • 32,350
  • 22
  • 54
  • 79
Jon
  • 453
  • 5
  • 18

2 Answers2

2

Instead of

//button[contains(., 'My Button')]

Try

//translate[contains(., 'My Button')]

Or maybe

//*[local-name()='translate' and contains(., 'My Button')]
Prophet
  • 32,350
  • 22
  • 54
  • 79
  • Good working solution (+1), but better not to [encourage misuse of `contains()`](https://stackoverflow.com/q/39650007/290085) where [better alternatives exist](https://stackoverflow.com/a/70320156/290085) – kjhughes Dec 12 '21 at 01:05
2

If you truly want substring matching (for example, all of "My Button", "Not My Button", and "My Buttonless Shirt"), then use contains() as @Prophet shows.

On the other hand, if you simply want to abstract away whitespace (along with any elements wrapping the targeted text), then use normalize-space():

//button[normalize-space() = 'My Button']

This selects the button element whose space-normalized string value is exactly 'My Button'.

See also

kjhughes
  • 106,133
  • 27
  • 181
  • 240
  • Thanks for your improving comment and a +1, but shouldn't you use `translate` tag name or `local-name()='translate'` as I used since it's not a button containing `My Button` while `translate` element contains that? +1 back ;) – Prophet Dec 12 '21 at 07:32
  • 1
    Taking the *string value* of `button` renders the name or even existence of any elements wrapping `"My Button"` irrelevant, which I believe is preferred. If the presence of `translate` is actually important, then to select the `button` element that contains a `translate` element with a string value of `"My Button"`, use this XPath: `//button[normalize-space(translate) = 'My Button']` – kjhughes Dec 12 '21 at 15:37