user10409695
user10409695

Reputation:

Selenium: input text in field that hasn't an input tag - how to find which one is it?

I need to input text into a field in a table that doesn't have the input tag. This XPath points out the field uniquely

//div[@id="contentBody"]//div[@row="0"]/div[contains(@class, "l4") and contains(@class, "r4")]

(one can add a trailing /div to this XPath, it makes no difference)

I can use this XPath to read this field but when I try to insert some text in the same field I get an error message:

InvalidElementStateException: Message: invalid element state: Element must be user-editable in order to clear it.

This is the HTML for the cell

<div class="slick-cell l4 r4  alignRight uppercase highint editable-cell active selected" aria-describedby="inforDataGrid731693C5" tabindex="-1" role="gridcell">
    <div class="edit-cell">2,0</div></div>

I found this question (which, BTW, does seem to be erroneously marked as a duplicate)

Element must be user-editable in order to clear it exception in selenium

The comments there seem to make sense for my situation: my XPath points to a div, not an input field (but as you can see, the div's class is "edit-cell" so...) but I don't think I have the option to point an input field. How do I work around this - how to find which element to target, so I can set its text successfully, without taht exception?

Upvotes: 3

Views: 2284

Answers (2)

Todor Minakov
Todor Minakov

Reputation: 20067

The conclusion you came to is the corrected one - you cannot send input to a <div> tag, that's not how browsers usually work (an exception to the rule is given in "edit 2"). The code exception states it clearly - "Element must be user-editable in order to clear it". Regardless that the div has a class value of "edit-cell" - that just a name chosen by a developer, for illustrative proposed; it does not imply the div will be able to take input.

Most probably the UI used by the application uses this <div> for stylized presentation of the values set by the user; there is a hidden <input> element that takes the actual keyboard entries, and a javascript code updates the <div> with them.
You'll have to find that input tag, and target it for your changes.

edit1: Finding input tags by their values with JS.

Here's how to find the input tag - in the browser, (by hand) set the value to a string that would be unique for the page - for instance, "myMegaSpecialString". Then run the following js in the console:

[...document.getElementsByTagName("input")].forEach(function(el){ if (el.value == "myMegaSpecialString"){console.log(el);} })

It will print the <input> element(s) that has that "special" value.

edit2: Finding any tags by their values with JS.

According to the discussion in the comments, no <input> element was found to have this value. As I was rightfully corrected by @Andersson, under special circumstances other elements but input can receive, khm, input - they should have the contenteditable attribute, and it's inherited from parent elements - e.g. if set on the <body> tag, you can type over any element in the page.

So - a modified version of the javascript to locate "who holds our value"; thankfully, getElementsByTagName() supports wildcard for argument - that'll match any element:

[...document.getElementsByTagName("*")].forEach(function(el){ if (el.value == "myMegaSpecialString"){console.log(el);} })

It might be a bit slower - but it must print the element(s) with value property equal to "myMegaSpecialString".

edit3: Finding any tags by their text content, with JS.

The quest continues - the text you are entering does not turn up in any element's value property? No problem, let's look for it in the elements textual content.
This might be the case when you are actually editing the content of a <span>, <div> or similar tags (fingers crossed... :)).

This is a variation of the previous version - manually set the text to a value that is unique across the page (to limit the output), and run this in the console:

[...document.getElementsByTagName("*")].forEach(function(el){ if (el.textContent.indexOf("myMegaSpecialString") !== -1){console.log(el);} })

, which will (should? might? I'm not certain in anything anymore :D) print in the console all DOM elements which have that string as part of their text.

Breakdown what is what in the javascript, for future changes:

  • document.getElementsByTagName("*")] returns a all elements with that tag name as an array; the argument asterisk (*) - any tag name.
  • forEach - a loop over the collection elements, passing each one to the anonymous function in it.
  • el.textContent - returns the text content of the node (and its child nodes).
  • indexOf() - returns the position of the argument in the string, -1 if it is not present (used indexOf() vs contains() for compatibility, the later comes in ES6).
  • if the condition returns true, console.log(el) will print it in the console.

Upvotes: 1

undetected Selenium
undetected Selenium

Reputation: 193108

This error message...

InvalidElementStateException: Message: invalid element state: Element must be user-editable in order to clear it.

...implies that a WebElement is in a state in which actions cannot be performed on it. Examples would include an element being obscured by another when clicking, or perhaps not being visible on the DOM.

Solution

As the element is a JavaScript enabled element you need to induce WebDriverWait to be element to be clickable and you can use either of the following solutions:

  • Wait Until Element Is Visible:

    Wait Until Element Is Visible    xpath=//div[contains(@class, 'slick-cell') and starts-with(@aria-describedby,'inforDataGrid')]/div[@class='edit-cell']    20  seconds
    Set Focus To Element    xpath=//div[contains(@class, 'slick-cell') and starts-with(@aria-describedby,'inforDataGrid')]/div[@class='edit-cell']
    # invoke click
    
  • Wait Until Element Is Enabled:

    Wait Until Element Is Enabled    xpath=//div[contains(@class, 'slick-cell') and starts-with(@aria-describedby,'inforDataGrid')]/div[@class='edit-cell']    20  seconds
    Set Focus To Element    xpath=//div[contains(@class, 'slick-cell') and starts-with(@aria-describedby,'inforDataGrid')]/div[@class='edit-cell']
    # invoke click
    

Upvotes: -1

Related Questions