How to select a svg with title using cypress? - javascript

i want to click element within div whose name is "name1", description is "description1"
and title is "title1" using cypress.
below is the dom,
<div data-testid="table-body" role="rowgroup">
<div role="row" data-testid="table-row-0">
<div role="cell">
<input data-testid="table-row-checkbox"/>
</div>
<div data-testid="table-cell-row-0-column-name">
<button data-testid="expand-row"></button>
<a class="subtle-link">name1</a>
</div>
<div data-testid="table-cell-row-0-column-description">
description1
</div>
<div data-testid="table-cell-row-0-column-isIcon">
<div>
<svg>
<title>title2</title>
</svg>
</div>
</div>
</div>
<div role="row" data-testid="table-row-1">
<div role="cell">
<input data-testid="table-row-1-checkbox"/>
</div>
<div data-testid="table-cell-row-1-column-name">
<button data-testid="expand-row"></button>
<a class="subtle-link">name1</a> //i want to click this element
</div>
<div data-testid="table-cell-row-column-description">
description1
</div>
<div data-testid="table-cell-row-column-isIcon">
<div>
<svg>
<title>title1</title>
</svg>
</div>
</div>
</div>
<div role="row" data-testid="table-row-2">
<div role="cell">
<input data-testid="table-row-checkbox"/>
</div>
<div data-testid="table-cell-row-2-column-name">
<button data-testid="expand-row"></button>
<a class="subtle-link">name2</a>
</div>
<div data-testid="table-cell-row-1-column-description">
description2
</div>
<div data-testid="table-cell-row-0-column-isIcon">
<div>
<svg>
<title>title1</title>
</svg>
</div>
</div>
</div>
</div>
As seen from above dom there is two divs with name name1 and description description1. but title is "title1", "title2"
i have tried like below,
cy.get('div[role="row"]')
.find('div', 'name1')
.contains('svg', 'title1')
.parent()
.click();
this selects the first element with name1 although title is title2

This is quite tricky, the simplest way I found is to use a filter with callback.
The row parameter is the raw element (not jQuery wrapped), so use .textContent() to get the text of the node and its descendants - MDN - textContent().
cy.get('div[role="row"]') // 3 elements found
.filter((_, row) => {
return row.textContent.includes('title1') &&
row.textContent.includes('description1') &&
row.textContent.includes('name1') // filters to 1 element
})
.find('a')
.click()
You can also use a selector with the filters built in
const selector = 'div[role="row"]' +
':contains("title1")' +
':contains("description1")' +
':contains("name1")';
cy.get(selector)
.find('a')
.click();
Text search within the row may be a bit imprecise, depending on if the text you are looking for appears in other columns.
A more precise filter would target the elements of each text (using jQuery here),
cy.get('div[role="row"]')
.filter((_, row) => {
const title = Cypress.$(row).find('title').text();
const description = Cypress.$(row).find('div[data-testid="table-cell-row-column-description"]').text();
const name = Cypress.$(row).find('a.subtle-link').text();
return title === 'title1' &&
description === 'description1' &&
name === 'name1';
})
.find('a')
.click()

Related

Change text in a div if another div has a specific attribute

I would like to change a text in a div if another div has a specific attribute, in my case data-value.
In the specific if a div has an attribute data-value="cars" another div should have the text "Buy".
In the specific if data-value="cars" I would like to change the text "1" into "Buy"
<div class="one">1</div>
Here the sample code
<div class="dynamic">
<div class="content">
<div data-value="cars">
Cars
</div>
<div data-value="truck">
Truck
</div>
<div data-value="moto">
Moto
</div>
</div>
</div>
<div class="change">
<div>
1
</div>
<div>
2
</div>
</div>
How can I achieve that in JS?
you need to first perform a query on the existence of any element within the content having the data-value attribute of "cars"
use the document.querySelector() function
if (document.querySelector('.dynamic .content [data-value="cars"]')) ...
then you need to set the text content of all the div elements within the change that have the value 1, using the .textContent attribute
document.querySelectorAll('.change div')
.forEach(div => if( div.textContent=="1") div.textContent="Buy");
putting all pieces together
if (document.querySelector('.dynamic .content [data-value="cars"]')) {
document.querySelectorAll('.change div')
.forEach(div => if(div.textContent=="1") div.textContent="Buy" );
}

How to retrieve the div first child element sibling node using querySelector?

I have the DOM structure like below
<div class="table_body">
<div class="table_row">
<div class="table_cell">first</div>
<div class="table_cell">chocolate products</div><!-- want to access this div content -->
</div>
<div class="table_row">
<div class="table_cell">third</div>
<div class="table_cell">fourth</div>
</div>
</div>
From the above HTML I want to access the div content of second div with classname table_cell inside first table_row div.
So basically I want to retrieve the content of div with classname table_cell with content chocolate products.
I have tried to do it like below
const element = document.querySelector('.rdt_TableBody');
const element1 = element.querySelectorAll('.rdt_TableRow')[0]
const element2 = element1.querySelectorAll('.rdt_TableCell')[0].innerHTML;
When I log element2 value it gives some strange output and not the text "chocolate products"
Could someone help me how to fix this. Thanks.
You can use:
the :nth-of-type pseudo-selector
combined with the immediate-child selector (>)
Example:
const selectedDiv = document.querySelector('.table_body > div:nth-of-type(1) > div:nth-of-type(2)');
Working Example:
const selectedDiv = document.querySelector('.table_body > div:nth-of-type(1) > div:nth-of-type(2)');
selectedDiv.style.color = 'white';
selectedDiv.style.backgroundColor = 'red';
<div class="table_body">
<div class="table_row">
<div class="table_cell">first</div>
<div class="table_cell">chocolate products</div> //want to access this div content
</div>
<div class="table_row">
<div class="table_cell">third</div>
<div class="table_cell">fourth</div>
</div>
</div>
In your code
element1.querySelectorAll('.table_cell')[0], this is targeting the first element i.e., <div class="table_cell">first</div>. That's the reason why you are not getting the expected output.
I have made it to element1.querySelectorAll('.table_cell')[1], so that it'll target <div class="table_cell">chocolate products</div>.
const element = document.querySelector('.table_body');
const element1 = element.querySelectorAll('.table_row')[0]
const element2 = element1.querySelectorAll('.table_cell')[1].innerHTML;
console.log(element2);
<div class="table_body">
<div class="table_row">
<div class="table_cell">first</div>
<div class="table_cell">chocolate products</div>
</div>
<div class="table_row">
<div class="table_cell">third</div>
<div class="table_cell">fourth</div>
</div>
</div>
Since the element that you want to target is the last div with having class table_cell, you can use :last-of-type on table_cell class using document.querySelector. But otherwise you can also use :nth-of-type if there are more than 2 elements and you want to target any element in between first and last.
Below is the example using :last-of-type.
const elem = document.querySelector(".table_row > .table_cell:last-of-type");
console.log(elem?.innerHTML);
<div class="table_body">
<div class="table_row">
<div class="table_cell">first</div>
<div class="table_cell">chocolate products</div> //want to access this div content
</div>
<div class="table_row">
<div class="table_cell">third</div>
<div class="table_cell">fourth</div>
</div>
</div>
For more info you can refer :nth-of-type, :last-of-type and child combinator(>).

Link simillary name classes so that when one is clicked the other is given a class

Basically, I'm asking for a way to optimize this code. I'd like to cut it down to a few lines because it does the same thing for every click bind.
$("#arch-of-triumph-button").click(function(){
$("#arch-of-triumph-info").addClass("active-info")
});
$("#romanian-athenaeum-button").click(function(){
$("#romanian-athenaeum-info").addClass("active-info")
});
$("#palace-of-parliament-button").click(function(){
$("#palace-of-parliament-info").addClass("active-info")
});
Is there a way to maybe store "arch-of-triumph", "romanian-athenaeum", "palace-of-parliament" into an array and pull them out into a click bind? I'm thinking some concatenation maybe?
$("+landmarkName+-button").click(function(){
$("+landmarkName+-info").addClass("active-info")
});
Is something like this even possible?
Thanks in advance for all your answers.
EDIT: Here's the full HTML.
<div class="landmark-wrapper">
<div class="page-content landmark">
<div class="heading span-after">
<span>Arch of Triumph</span>
</div>
<div class="landmark-button" id="arch-of-triumph-button"></div>
</div>
</div>
<div class="landmark-wrapper">
<div class="page-content landmark">
<div class="heading span-after">
<span>Romanian Athenaeum</span>
</div>
<div class="landmark-button" id="romanian-athenaeum-button"></div>
</div>
</div>
----------------------------------------------------------
<div class="landmarks-info-wrapper">
<div class="landmark-info" id="arch-of-triumph-info">
<div class="info-landmark section">
<span class="landmark-title">Arch of Triumph</span>
<span class="landmark-coord">44°28′1.99″N 26°4′41.06″E</span>
</div>
</div>
<div class="landmark-info" id="romanian-athenaeum-info">
<div class="info-landmark section">
<span class="landmark-title">The Romanian Athenaeum</span>
<span class="landmark-coord">44.4413°N 26.0973°E</span>
</div>
</div>
Assuming you're not able to modify your HTML markup (in which case with use of CSS classes would be cleaner), a solution to your question would be as shown below:
// Assign same click handler to all buttons
$("#arch-of-triumph-button, #romanian-athenaeum-button, #palace-of-parliament-button")
.click(function() {
// Extract id of clicked button
const id = $(this).attr("id");
// Obtain corresponding info selector from clicked button id by replacing
// last occurrence of "button" pattern with info.
const infoSelector = "#" + id.replace(/button$/gi, "info");
// Add active-info class to selected info element
$(infoSelector).addClass("active-info");
});
Because each .landmark-button looks to be in the same order as its related .landmark-info, you can put both collections into an array, and then when one is clicked, just find the element with the same index in the other array:
const buttons = [...$('.landmark-button')];
const infos = [...$('.landmark-info')];
$(".landmark-button").click(function() {
const i = buttons.indexOf(this);
$(infos[i]).addClass('active-info');
});
This does not rely on IDs at all - feel free to completely remove those from your HTML to declutter, because they don't serve any purpose now that they aren't being used as selectors.
Live snippet:
const buttons = [...$('.landmark-button')];
const infos = [...$('.landmark-info')];
$(".landmark-button").click(function() {
const i = buttons.indexOf(this);
$(infos[i]).addClass('active-info');
});
.active-info {
background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="landmark-wrapper">
<div class="page-content landmark">
<div class="heading span-after">
<span>Arch of Triumph</span>
</div>
<div class="landmark-button" id="arch-of-triumph-button">click</div>
</div>
</div>
<div class="landmark-wrapper">
<div class="page-content landmark">
<div class="heading span-after">
<span>Romanian Athenaeum</span>
</div>
<div class="landmark-button" id="romanian-athenaeum-button">click</div>
</div>
</div>
----------------------------------------------------------
<div class="landmarks-info-wrapper">
<div class="landmark-info" id="arch-of-triumph-info">
<div class="info-landmark section">
<span class="landmark-title">Arch of Triumph</span>
<span class="landmark-coord">44°28′1.99″N 26°4′41.06″E</span>
</div>
</div>
<div class="landmark-info" id="romanian-athenaeum-info">
<div class="info-landmark section">
<span class="landmark-title">The Romanian Athenaeum</span>
<span class="landmark-coord">44.4413°N 26.0973°E</span>
</div>
</div>
Older answer, without knowing the HTML: You can extract the ID of the clicked button, slice off the button part of it, and then select it concatenated with -info:
$(".landmark-button").click(function() {
const infoSel = this.id.slice(0, this.id.length - 6) + 'info';
$(infoSel).addClass('active-info');
});
A much more elegant solution would probably be possible given the HTML, though.

HTML JavaScript querySelector to find an element whose child has a specific data attribute

In my below HTML markup, I'd like to query the <div> that has a data-parent set to "true", and the contained child has data-child-gender set to "true" and inner html is "male".
<div id="grandparent">
<div id="parent1" data-parent="true">
<div id="child1" data-child-gender="false">
male
</div>
</div>
<div id="parent2" data-parent="true">
<div id="child2" data-child-gender="true">
female
</div>
</div>
<div id="parent3" data-parent="false">
<div id="child3" data-child-gender="true">
female
</div>
</div>
<div id="parent4" data-parent="true">
<div id="child4" data-child-gender="true">
male
</div>
</div>
</div>
Given the above scenario, the expected <div> is parent4.
What is the JavaScript querySelector to use?
First use querySelectorAll which will give an array. Then iterate over it and check and get element with required data attribute.
After that you can use use a if & check the content inside it
let k = document.querySelectorAll('[ data-parent=true]').forEach(function(item) {
let elem = item.querySelector('[data-child-gender=true]');
if (elem !== null && elem.innerHTML.trim() === 'male') {
console.log(item.id)
}
})
<div id="grandparent">
<div id="parent1" data-parent="true">
<div id="child1" data-child-gender="false">
male
</div>
</div>
<div id="parent2" data-parent="true">
<div id="child2" data-child-gender="true">
female
</div>
</div>
<div id="parent3" data-parent="false">
<div id="child3" data-child-gender="true">
female
</div>
</div>
<div id="parent4" data-parent="true">
<div id="child4" data-child-gender="true">
male
</div>
</div>
</div>
There isn't one querySelector you can use for this (as you can't use it to select specific text within elements). However, you can use .querySelector() with .filter() to get more specific results:
const true_children = [...document.querySelectorAll("[data-parent='true'] [data-child-gender='true']")];
const res = true_children.filter(({innerHTML:g}) => g.trim() === "male");
console.log(res);
<div id="grandparent">
<div id="parent1" data-parent="true">
<div id="child1" data-child-gender="false">
male
</div>
</div>
<div id="parent2" data-parent="true">
<div id="child2" data-child-gender="true">
female
</div>
</div>
<div id="parent3" data-parent="false">
<div id="child3" data-child-gender="true">
female
</div>
</div>
<div id="parent4" data-parent="true">
<div id="child4" data-child-gender="true">
male
</div>
</div>
</div>
The problem that the question describes, cannot be solved using query-selectors alone. This is because of following reasons:
The query selectors always works on descendants, so while evaluating that the child div has data-child-gender="true", there will be no way to return the parent element. The query-selector will return the child div.
There is no way to evaluate the inner text or contained text of an element in query-selector.
These two limitations can be worked around by using JavaScript, provided that you were going to use the query-selector in JS.
Something like the following snippet should work.
document.querySelectorAll('div[data-parent=true] div[data-child-gender=true]')
.filter(function(elem) {
return elem.innerText === 'male'; // filter the elements containing male string.
})[0].parentElement; // return the parent of matched element.
An equivalent logic could be derived for selenium too. Otherwise if this much logic is unacceptable, you can always use the much richer xpath selectors. xpath wouldn't have either of the limitations mentioned above.

Background url from href

I am trying to get url from href and paste it in background image.
The problem is that it is applying to just first div not on all div. Is this possible without giving unique classes?
HTML
<div id="main-bg"> text
<div id="inner-content"> </div>
</div>
<div id="main-bg"> text
<div id="inner-content"> </div>
</div>
<div id="main-bg"> text
<div id="inner-content"> </div>
</div>
<div id="main-bg"> text
<div id="inner-content"> </div>
</div>
JS
$('#main-bg').css(
'background-image', 'url(' + $('#inner-content a').attr('href') + ')'
);
JSFIDDLE: https://jsfiddle.net/pr07zymf/
An id MUST be unique to each element! Using it multiple times may work at times in CSS, though, JavaScript isn't so forgiving. The reason only the first <div> is styled is because JavaScript ignores all other elements with the same id.
You can use classes instead as shown in the example below!
Example Code:
/* Iterate over every element having the given class. */
$('.main-bg').each(function (index, element) {
/* Cache a jQuery instance of the element. */
var el = $(element);
/* Get the 'href' and assign it as the 'background-image' for each element. */
el.css('background-image', 'url(' + el.find('.inner-content a').attr('href') + ')');
});
.main-bg {color: #fff}
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="main-bg"> text
<div class="inner-content">
</div>
</div>
<div class="main-bg"> text
<div class="inner-content">
</div>
</div>
<div class="main-bg"> text
<div class="inner-content">
</div>
</div>
<div class="main-bg"> text
<div class="inner-content">
</div>
</div>
When you are using HTML, please remember, the ID should always be UNIQUE.
You cannot make them the same name like "main-bg".
Solutions:
1, change their id's name to <div id ="main-bg1">, <div id ="main-bg2">, <div id ="main-bg3">, and <div id ="main-bg4">
2, use CLASS like
<div class="main-bg"> text
<div class="inner-content"> </div>
And remember, every time when you do
$('#ID')
or
document.getElementById(ID)
It will always choose the FIRST element which has this ID.

Categories

Resources