What is the best way to access the text content of a nested HTML element for automation tasks with Selenium or Protractor?

Can anyone help me with this HTML code snippet? I want to extract and display only the text within the desc class - "Print this", ignoring the text in the spell class. This needs to be done using either Protractor or Selenium.

<span class="desc">
Print this
    <a class="new-link" href="#">
        <span class="spell">And not this</span>
    </a>
</span>

I attempted using the getText() method, but it printed both parts of the statement as shown below:

Print this And not this

In Protractor with Javascript:

element(by.css('.desc')).getText().then(function(text){
    console.log(text);
});

In Selenium with Java:

System.out.println(driver.findElement(by.xpath('//*[@class=".desc"]')).getText());

Any suggestions on how to only print the first part of the text ("Print this")?

Your assistance is greatly appreciated! Thank you.

Answer №1

ElementFinder.getText() retrieves the text content of an element by calling innerHTML and then removing any leading and trailing whitespaces. However, innerHTML includes all child elements within the element at any level of nesting. While there isn't a built-in property in the DOM to specifically retrieve only first-level text, it can be implemented manually. Text within the DOM is treated as a node and stored in the DOM tree similarly to tag elements, just with different characteristics. By accessing the property Element.childNodes, we can obtain the first-level children elements of the main element, iterate through them to extract only text nodes, concatenate their content, and return the result.

To streamline this process in Protractor, I have created a custom method that extends the prototype of ElementFinder. This enhancement allows for simple usage across all Protractor elements. You may choose where to include this additional code, but I recommend placing it before your tests, perhaps in protractor.conf.js.

protractor.ElementFinder.prototype.getTextContent = function () {
    // Inject script onto the page
    return this.ptor_.executeScript(function () {
        // Note: this scope is not specific to Protractor

        // Current element
        var el = arguments[0];
        var text = '';

        for (var i = 0, l = el.childNodes.length; i < l; i++) {
            // Extract text from text nodes exclusively
            if (el.childNodes[i].nodeType === Node.TEXT_NODE) {
                text += el.childNodes[i].nodeValue;
            }
        }

        // Optionally trim leading and trailing whitespace
        text = text.trim();

        return text; // The final outcome - Promise resolves with this value

    }, this.getWebElement()); // Pass the current element to the script
};

This method returns a Promise that resolves with the value stored in the text variable. To apply it:

var el = $('.desc');

expect(el.getTextContent()).toContain('Print this');

// or 

el.getTextContent().then(function (textContent) {
    console.log(textContent); // 'Print this'
});

Answer №2

I recently implemented a solution proposed by Michael directly into my test case, skipping the function call. While this approach works fine, I would still recommend using it as a standalone function if you anticipate needing to reuse it. However, for those looking for an inline solution, here is a way to achieve it -

it("Extracting First part of text", function(){
    browser.executeScript(function () {
        var element = arguments[0], extractedText = '';
        for (var j = 0, len = element.childNodes.length; j < len; j++)
            if (element.childNodes[j].nodeType === Element.TEXT_NODE)
                extractedText += element.childNodes[j].nodeValue;
        return extractedText.trim();
    },$('.desc').getWebElement()).then(function(extractedText){
        //include expect statements with "extractedText" as necessary
    });
});

Wishing you success with your implementation.

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

Plugin for Vegas Slideshow - Is there a way to postpone the beginning of the slideshow?

Is there a way to delay the start of a slideshow in JavaScript while keeping the initial background image visible for a set amount of time? I believe I can achieve this by using the setTimeout method, but I'm struggling to implement it correctly. Be ...

How to access a Selenium element using JavaScriptExecutor

My task involves working with a collection of elements in Selenium, specifically located using the By.CssSelector method: var contentRows = new List<TableRow>(); for (var i = 1; i < PositiveInfinity; i++) { var cssSelectorToFind = $"tbody &g ...

Making adjustments to a row in the free jqGrid is a breeze with the ability

Using free jqGrid 4.12.1, I aim to incorporate functionality for adding, editing, and deleting rows in the grid with server-side calls for each operation. Below is the implementation of editurl and 'actions' formatter, { name: "actions", wi ...

Can an array be generated on-the-fly with objects contained within it?

Seeking advice on looping through an array of objects to achieve a specific result. Here is the initial array: var testArray = [{'name':'name1', 'xaxis':'xaxis1', 'yaxis':'yaxis1'}, ...

Is it possible to utilize the same database connection for multiple routes in an

I have taken inspiration from Express's route-separation example and created a Node.js app. Now, I aim to enhance it by integrating the MongoDB driver Mongoose for listing Users and Kittens. var express = require('express'); var app = expre ...

Modifying the color of multiple cells simultaneously

I'm currently facing an issue where I need to change the cell color of multiple cells at once, all stored within a specific range. However, I only want certain cells to change based on a particular condition. While I can manually change each cell, it& ...

Decoding JavaScript content with Python

Currently, I am dissecting this specific portion of HTML <script type="text/javascript"> var spConfig = new Product.Config({"attributes":{"150":{"id":"150","code":"size_shoe","label":"Schuhgr\u00f6\u00dfe","options":[{"id":"494","label ...

Include personalized headers to the 'request'

I have configured my express server to proxy my API using the following setup: // Proxy api calls app.use('/api', function (req, res) { let url = config.API_HOST + req.url req.pipe(request(url)).pipe(res) }) In this instance, confi ...

Extension for Chrome that enhances YouTube video playback experience

I'm struggling to figure out why this isn't functioning. I've reviewed the Google extension dev docs and examined some sample code. Checked various Stack Overflow questions and answers, but haven't received any helpful feedback or res ...

Having trouble applying styles to a component using forwardRef

I'm relatively new to React and still trying to wrap my head around the component's lifecycle. However, the issue at hand is proving to be quite perplexing. One thing that confuses me is why adding "setState(10);" causes the style of the "Test" ...

How can I ensure that VueJS only starts loading data after the initial API call has been completed?

After retrieving data from an API, I populate a form in my component. The challenge I am facing is that the watchers are triggered immediately after populating the initial data. I want them to be triggered asynchronously. Additionally, I need to prevent th ...

Distinct elements within a JavaScript hash

Is there a jQuery method to add a hash into an array only if it is not already present? var someArray = [ {field_1 : "someValue_1", field_2 : "someValue_2"}, {field_1 : "someValue_3", field_2 : "someValue_4"}, {field_1 : "someValue ...

How to dynamically apply the orderBy filter in AngularJS

Attempting to input ordering criteria from a text box and dynamically order the content. The current code is functioning properly, but when attempting to change: orderBy:'age' to orderBy:'{{criteria}}' the functionality breaks. Her ...

Is it possible to switch the background-image after a gif has finished loading?

I am currently using a GIF as a background image, but I would like it to show a static image until the GIF is fully loaded and ready to play. After reading several similar discussions, I understand that you can manipulate elements with JavaScript once the ...

Using Angular with OpenStreetMap and $routeProvider allows for dynamic routing and

Check out this awesome single page app that I found: https://github.com/tombatossals/angular-leaflet-directive/blob/master/examples/simple-example.html However, I am looking to enhance it by adding a menu... <html ng-app="App"> <head> <lin ...

Searching for an object in Vue 3 Composition API and displaying its contents

Experiencing a challenge with my first Vue.js project, seeking assistance in resolving the issue. Upon receiving a response from my API, I retrieve a list of projects and aim to locate the one matching the ID provided in the URL parameter. A peculiar error ...

Retrieving JSON information from asynchronous JavaScript and XML (AJAX)

I am struggling with making a URL that contains JSON Data work properly. The issue lies in the data field - I am unsure of what to input here as it is currently undefined. My goal is to store an array of the JSON data received from the ajax request. What ...

The compare function in bcryptjs will result in a false output if the passwords include numerical

I have successfully used bcryptjs to hash my passwords during user registration. However, I am facing an issue with the bcrypt.compare function when attempting to log in. The function returns a false promise when passwords contain numbers or special charac ...

The selected element does not support the addition of setSelectionRange

I have encountered an error while trying to add the "setSelectionRange" method to an input element that I selected using "getElementById". The error message states that "property 'setselectionrange' does not exist on type 'htmlelement'" ...

jquery target descendants with specific names

In the provided HTML code snippet, there is a main div with the class name of cxfeeditem feeditem, and it contains multiple child elements with similar class names and structure. My query pertains to extracting values from specific children within all the ...