Selenium操作textarea时ID/XPath选择器失效问题咨询
By.id/By.xpath Fail But By.css('textarea') Works for Your Textarea? Let’s break down the root of this discrepancy and share fixes that let you avoid relying on array indexes:
1. Hidden Duplicate Elements with the Same ID
Even though HTML standards require unique IDs, dynamic UIs (like your dropdown) often sneak in hidden duplicate elements with identical IDs. When you use By.id('notes') or By.xpath('//*[@id="notes"]'), Selenium grabs the first matching element it finds—which is almost always the hidden version that existed before you expanded the dropdown.
In contrast, By.css('textarea') returns all textareas on the page. Your extra waits for visibility and interactability on notes[0] end up targeting the actual visible textarea that gets added to the DOM when the dropdown opens, not the hidden duplicate.
2. Timing & Element Freshness Issues
until.elementLocated stops searching as soon as it finds any element matching the selector. If the hidden #notes is already in the DOM before the dropdown opens, Selenium locks onto that element immediately—even though it’ll never become interactable.
until.elementsLocated, on the other hand, waits until at least one textarea exists, and your subsequent waits give Selenium time to recognize the new, visible textarea that loads when the dropdown expands.
Fixes to Ditch Array Indexes
Here are reliable ways to target the correct textarea without relying on notes[0]:
Option 1: Target Only Visible Textareas Directly
Modify your selector to filter out hidden elements. For XPath:
const notes = await driver.wait(until.elementLocated(By.xpath('//textarea[@id="notes" and not(@hidden) and not(contains(@style,"display:none"))]')), delay); await driver.wait(until.elementIsEnabled(notes), delay); await notes.sendKeys('Something to write'); await notes.sendKeys(Key.TAB);
Or with Selenium’s :visible CSS pseudo-class:
const notes = await driver.wait(until.elementLocated(By.css('textarea#notes:visible')), delay); await driver.wait(until.elementIsEnabled(notes), delay); await notes.sendKeys('Something to write'); await notes.sendKeys(Key.TAB);
Option 2: Wait for the Parent Container First
Since the textarea lives inside a .mx-0.w-100.row div that only appears when the dropdown is selected, wait for that parent first, then find the textarea inside it:
// Wait for the parent container to become visible await driver.wait(until.elementIsVisible(driver.findElement(By.css('.mx-0.w-100.row'))), delay); // Now target the textarea within this container const notes = await driver.wait(until.elementLocated(By.css('.mx-0.w-100.row textarea#notes')), delay); await notes.sendKeys('Something to write'); await notes.sendKeys(Key.TAB);
Option 3: Use elementIsInteractable for Simplified Waits
Selenium has a built-in condition that combines visibility and enabled checks. This ensures you only target elements ready for interaction:
const notes = await driver.wait(until.elementIsInteractable(By.id('notes')), delay); await notes.sendKeys('Something to write'); await notes.sendKeys(Key.TAB);
内容的提问来源于stack exchange,提问作者Don Diego

