Discovering a particular element involves iterating through the results returned by the findElements method in JavaScript

I am attempting to locate and interact with a specific item by comparing text from a list of items. The element distinguished by .list_of_items is a ul that consists of a list of li>a elements.

I am uncertain about how to transfer the determined element to the next function. There are no available identifiers, such as id or class, for pinpointing the element.

driver.isElementPresent(By.css(".list_of_items")).then(function(trueFalse){
  if (trueFalse){
    return driver.findElements(By.css(".list_of_items a"));                         
  }else{
    console.log('err');
  }
}).then(function(eleArray){
  for (var i=0; i < eleArray.length; i++){
    eleArray[i].getInnerHtml().then(function(html){
      if (html.trim().toLowerCase() == item_to_search.toLowerCase()){
        //
        // How can I pass the matched element to the subsequent function??
        //
        return true;
      }
    }).then(function(element){
        console.log(element);
      }
    });
  }
});

Answer №1

If you're looking to filter elements, consider using the filter method:

driver.isElementPresent(By.css(".list_of_items")).then(function(trueFalse){
  if (trueFalse){
    return driver.findElements(By.css(".list_of_items a"));                         
  }else{
    console.log('error');
    throw new Error('Element not found.'); // Updated error message for clearer handling.
  }
}).then(function(eleArray){
  return webdriver.promise.filter(eleArray, function(element){
    return element.getInnerHtml().then(function(innerText){
      return innerText.trim().toLowerCase() == item_to_search.toLowerCase();
    });    
  });
}).then(function(reducedElements){
  console.log('filtered elements: ', reducedElements.length);
});

Answer №2

It's worth noting that your current code is performing a case-insensitive search. If you have the capability to conduct a case-sensitive search, consider using the following method to locate your element:

browser.findElement(By.linkText(item_to_search)).then(...);

In my experience developing numerous application tests with Selenium, I've consistently been able to search by link text without requiring case sensitivity. I highly recommend organizing your code in a way that enables this functionality.

If conducting a case-sensitive search is not an option, you may need to scan through each element to identify the desired one. While it is possible to create an XPath expression that matches text regardless of case, I tend to avoid using XPath for matching CSS classes like in your scenario. Consequently, scanning through elements as you were previously doing might be preferable. Note that when comparing text values against HTML content, it is advisable to use getText() rather than getInnerHtml(), as testing against HTML can be fragile: there could be additional elements within the HTML that do not affect the actual text of the link. For example,

<a><b>This</b> is a test</a>
would display "This is a test" but checking the inner HTML of the <a> tag would yield
<b>This</b> is a test
, which is not ideal for matching purposes.

Below is an implementation showcasing the By.linkText method mentioned earlier and including an illustrative example:

var webdriver = require('selenium-webdriver');
var By = webdriver.By;
var chrome = require('selenium-webdriver/chrome');
var browser = new chrome.Driver();

browser.get("http://www.example.com");

// Modifying the example.com page to add test data.
browser.executeScript(function () {
    document.body.innerHTML = '\
<ul class="list_of_items">\
  <li><a>One</a></li>\
  <li><a>  Two  </a></li>\
  <li><a>Three</a></li>\
</ul>';
});

// Example of utilizing By.linkText for a case-sensitive search.
browser.findElement(By.linkText("Two")).getOuterHtml().then(function (html) {
    console.log("case-sensitive: " + html);
});

var item_to_search = "TwO"; // Intentionally mixed case for illustration.

browser.findElements(By.css(".list_of_items a")).then(function (els){
    if (els.length === 0)
        throw new Error("abort!");

    var item_to_search_normalized = item_to_search.toLowerCase();

    function check(i) {
        if (i >= els.length)
            throw new Error("element not found!");

        var el = els[i];

        return el.getText().then(function (text) {

            if (text.trim().toLowerCase() === item_to_search_normalized)
                return el;

            return check(i + 1);
        });
    }

    return check(0);
}).then(function (el) {
    el.getOuterHtml().then(function (html) {
        console.log("case-insensitive: " + html);
    });
});


browser.quit();

Additional tips:

  1. Your initial code checked if .list_of_items existed before proceeding. Instead of this, my approach assumes the page is correctly structured, reducing unnecessary operations. If troubleshooting why no elements are being retrieved, modify the throw new Error("abort!") statement for diagnosis purposes.

  2. Your original code halted the search upon finding a match, which I preserved. Using webdriver.promise.filter scans every element even after locating a target, unlike my recursive method which terminates early upon success. This reduces round-trip interactions between the script and the browser, especially significant for remote server setups where each test adds latency. The recursion mirrors the behavior of webdriver.promise.filter while optimizing efficiency.

In my code snippet, I utilize executeScript to minimize exchanges between the Selenium script and the browser:

browser.executeScript(function () {
    var item_to_search = arguments[0].toLowerCase();
    var els = document.querySelectorAll(".list_of_items a");
    for (var i = 0; i < els.length; ++i) {
        var el = els[i];
        if (el.textContent.trim().toLowerCase() == item_to_search)
            return el;
    }
    return null;
}, item_to_search).then(function (el) {
    if (!el)
        throw new Error("can't find the element!");

    el.getOuterHtml().then(function (html) {
        console.log("case-insensitive, using a script: " + html);
    });
});

The script passed to executeScript runs within the browser context, necessitating the use of arguments for parameters and .then for result retrieval. Any console.log calls within executeScript output to the browser console.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

If a user enters an incorrect path, the goal is to automatically redirect them to the homepage while displaying the correct URL in AngularJS

When the URL is manually edited, the webpage displays the same content with a different URL structure. For instance, http://www.example.com/# and http://www.example.com/#/abc both show identical content. I would like to implement a redirect for any edite ...

Enhance your app with the seamless navigation experience using Ionic 2

As a beginner in Angular2, Ionic2, NodeJS ... I am experimenting with writing some code to learn. In my journey, I attempted to create a screen with 3 tabs and a menuToggle. When the application is launched, clicking the menuToggle button on the first tab ...

Javascript problem involving multiple scopes in closures

What is the most effective solution for addressing this issue related to closure in JavaScript? Here is a simple problem I am facing: I have 10 spans with onclick events (I want an alert showing the number of the span clicked on each click): var spans = ...

Using Material-UI in a project without the need for create-react-app

My objective is to utilize Material-UI without relying on create-react-app, as I feel that it abstracts too much and hinders my learning process. Unfortunately, all the instructions I have come across are centered around create-react-app. I am aiming to s ...

Creating custom ExpectedConditions with Protractor for detecting attribute changes

I've been working on creating a custom ExpectedConditions method that can wait for an element attribute to change. Here is the approach I came up with: const CustomExpectedCondition = function() { /** * Check if element's attribute matches ...

I am encountering difficulties importing the React-hexgrid library

Although I have verified that the library path is correct, I am still encountering an error. Here is the code snippet: "react-hexgrid": "2.0.1", "react": "18.2.0" "next": "13.4.3" Click here to v ...

Troubleshooting JavaScript for Sidebar Disappearance due to marginRight and display CSS Issue

I need to adjust the layout of my website so that the sidebar disappears when the window is resized below a certain width, and then reappears when the window is expanded. Here is the HTML code: <style type="text/css"> #sidebar{ width:290 ...

"Critical issue: Meta tags are not present on the dynamic pages of the NextJS

In my NextJS application, the pages are structured as follows: App --pages ----_app.js ----index.js ----company.js ----users ------[userID].js I have a dynamic page named [userID].js that retrieves the userID through the router to display information for ...

How to utilize Python Selenium for opening elements in new tabs

Task: The objective is to automate the process of opening a webpage, finding URLs by class, and then opening each URL in a new tab in Google Chrome. Approach attempted: Using Selenium WebDriver in Python to locate elements by class name and open them in t ...

Tips for accessing the 'styled' function in Material UI ReactHow to utilize the 'styled' function

Hey there, I'm facing an issue while trying to export a styled AppBar. Check out my code below: import * as React from 'react'; import { styled, useTheme } from '@mui/material/styles'; import MuiAppBar from '@mui/material/AppB ...

Utilizing AngularJS Directives to Transclude and efficiently organize data chunks

I'm facing a challenge as I try to convert a jQuery plugin I developed into an AngularJS directive. The issue lies in the transclusion aspect of the process. Link to jQuery Widget plugin: http://plnkr.co/edit/xxZIb2DyAere7pBY6qm7?p=preview Link to A ...

What is the best way to transfer data between windows using titanium (classic)?

'The code I've written utilizes MyHTTPlink to retrieve information such as Restaurant Name, url, and address. Currently, it lists all restaurant names and urls in a table. Now, I want to figure out how to send the details of a table row to the ne ...

Detecting browser reload in React/Typescript: A guide

Is there a way to determine if the browser has been reloaded? I've explored various solutions, but most suggest using code like this: const typeOfNavigation = (performance.getEntriesByType("navigation")[0] as PerformanceNavigationTiming).type ...

Can Node.js Utilize AJAX, and if So, How?

Coming from a background in browser-based JavaScript, I am looking to dive into learning about node.js. From my current understanding, node.js utilizes the V8 engine as its foundation and offers server-side JavaScript capabilities along with pre-installed ...

Error message 'AVV_ERR_PLUGIN_NOT_VALID' encountered within fastify

I've encountered an issue while setting up the environmental variables in my fastify - react app. Can someone help me with resolving the 'AVV_ERR_PLUGIN_NOT_VALID' error that I'm receiving for the following fastify code? Your assistance ...

Issues with sending parameters via an initialisation function in JavaScript

I am struggling with passing arguments to a new object when using an initializer function. Let's look at my examples where I aim to create an object that returns an array. Object Ex1 works as expected: Ex1 = function() { myVar = []; myVar = ...

What is the best way to develop shared singleton components that work seamlessly across various platforms?

How about developing a React component called LoadingMask that can toggle the display of a loading mask based on the current state? The purpose would be to show the mask before an ajax call and hide it once the data is received. To avoid showing multiple ...

Javascript function to deselect all items

One of my functions is designed to reset all checkbox values and then trigger an AJAX request. However, there are instances when the function initiates before the checkboxes have been unchecked. function clear() { $("#a").prop("checked", false); $("#b ...

Ways to access the scrollTop attribute during active user scrolling

I've been working on a website that utilizes AJAX to keep a chat section updated in real-time. One issue I encountered was ensuring the chat automatically scrolled to the bottom when a user sent a message, but remained scrollable while new messages we ...

Encountering a problem with td element: stale reference error - element is not connected to the page

I am a beginner in selenium coding and I am encountering an issue with the code below. The code fetches values from a table that spans multiple pages. Initially, it reads all values from the table successfully but when moving to the next page, I encounter ...