Use Webdriver.IO to select element by class - javascript

I use the browser from Webdriver.io and want to select an element with the class js-add-board (which is a button i would like to click) inside this constellation:
<div id="content">
<div class="wrapper">
<div class="news-sidebar sidebar">
..
</div>
<ul class="board-list clearfix">
<li class="js-add-board">
<a class="board-list-item label" href="#">Add a new board</a>
</li>
</ul>
</div>
</div>
This is from the styles:
.board-list .js-add-board {
text-align: center;
}
What I tried was:
browser.element('[js-add-board]');
but it returned undefined.

[js-add-board] is an attribute selector. It would match things like <a js-add-board="something"> or <div js-add-board="1">.
To match a class, use the CSS class selector: .js-add-board
If you must use an attribute selector, you could do [class~="js-add-board"], but I don't recommend that.
Tip: when you're trying to figure out the correct selector to use in your end-to-end tests, as long as it's not one of Protractor's Angular-specific selectors you can just run it through document.querySelector or document.querySelectorAll and see if it gets anything.
For more on attribute selectors, see this CSS Tricks post.

As far I can see, you should use selector by class, not by attribute.
So, in this case, please try the following:
browser.element('.js-add-board');

Related

Get <li> text number using jquery

i am trying to get li tag text value using js but i am not getting the expected output
i.e ("Pens").
I have added a code snippet.
Note - I cannot change html.
console.log(jQuery('#accordionItem li span').html());
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="accordionItem" class="filter_middle-stage2-list_wrapper">
<li style="font-weight: bold;">Pens<span>(1200)</span></li>
</div>
Any thoughts on this ?
Use the text method like below:
console.log($('#accordionItem li span').text());
from what I understand you want the text of the li without the text of the span.
So you can use the replace function to do it like so:
console.log($('#accordionItem li').text().replace($('#accordionItem li span').text(), ''));
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="accordionItem" class="filter_middle-stage2-list_wrapper">
<li style="font-weight: bold;">Pens<span>(1200)</span></li>
</div>
if you want a more general solution that will get you just the text of the li without any of its children that would be a better solution:
console.log($('#accordionItem li').contents().get(0).nodeValue);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="accordionItem" class="filter_middle-stage2-list_wrapper">
<li style="font-weight: bold;">Pens<span>(1200)</span></li>
</div>
You seem to be selecting the <span> tag on your jQuery selector.
Although I'd suggest using the text method to achieve what you are looking for.
No jQuery needed.
Since you have an element with ID, you can access it directly, and get the node's text and simply remove any non-digit characters, and you'll be left with the numeral value you are after.
The benefit of this method is the irrelevance of the DOM structure - it will always work for that element (with that ID), but can be applied to any <li> element, regardless if it has a <span> child (or any other children)
console.log(
accordionItem.children[0].firstChild.textContent
)
<div id="accordionItem" class="filter_middle-stage2-list_wrapper">
<li style="font-weight: bold;">Pens<span>(1200)</span></li>
</div>

How can one select an attribute with a Windows-format path in a CSS rule?

I am looking to style some elements in VS Code (but really could be any web app, Electron/NWjs-based app, Neutralino-based app, etc, anything that has a webview and can enumerate a filesystem) based on an element attribute that contains their absolute path but it's Windows formatted. Example:
<li data-path="C:\Windows\System32\etc">
/etc
</li>
as
li[data-path="C\:\\Windows\\System32\\etc"]
In JavaScript I've attempted to select it using Element.querySelector with CSS.escape but attempting to use the whole path fails and I essentially have to programmatically generate a usable rule by splitting on backslash and making a super-specific selector like:
li[data-path^="C\:"]
[data-path*="Windows"]
[data-path*="System32"]
[data-path*="etc"]
Not only does that look terrible it also could easily match the wrong item:
<li data-path="C:\Windows\System32\Drivers\etc\some-extra-folder\some-folder-with-the-word-etc\etc">
/etc
</li>
Is there some working method to generate a valid CSS attribute selector for such a string?
If possible I'd prefer to avoid using 3rd-party libraries like jQuery to minimize dependencies, but if something like Sizzle.js or other CSS querySelector-like implementation is the absolute only way to get a working attribute selector function I'll use if absolutely necessary.
Currently I'm using document.querySelectorAll('li[data-path]') and looping through and raw-comparing the string values while I'm still looking for a CSS attribute selector that actually works for Windows-style paths.
Edit: adding the HTML that VS Code actually generates for a file in its Explorer view
<div class="monaco-list-row focused selected" role="treeitem" data-index="16" data-last-element="false" aria-setsize="8" aria-posinset="5" id="list_id_4_16" aria-label="app.js" aria-level="3" draggable="true" style="top: 352px; height: 22px; line-height: 22px;" aria-selected="true">
<div class="monaco-tl-row">
<div class="monaco-tl-indent" style="width: 40px;">
<div class="indent-guide" style="width: 16px"></div>
<div class="indent-guide active" style="width: 16px"></div>
</div>
<div class="monaco-tl-twistie" style="padding-left: 40px;"></div>
<div class="monaco-tl-contents">
<div class="monaco-icon-label file-icon app.js-name-file-icon js-ext-file-icon ext-file-icon javascript-lang-file-icon explorer-item" title="C:\Users\liqui\Documents\dev\neu-lx-dash\app\assets\app.js" style="display: flex;">
<div class="monaco-icon-label-container">
<span class="monaco-icon-name-container">
<a class="label-name">
<span class="monaco-highlighted-label" title="C:\Users\liqui\Documents\dev\neu-lx-dash\app\assets\app.js">
<span>app.js</span>
</span>
</a>
</span>
<span class="monaco-icon-description-container"></span>
</div>
</div>
</div>
</div>
</div>
This is a service for Regex. Unfortunatelly AFAIK there is no such thing as a regex selector for CSS. But if you instead of doing in directly in CSS you could run some js, it makes things much easier as you can filter elements using regex. Here's an example:
var lis = document.querySelectorAll('li[data-path]');
var windowsPathRegEx = /[A-Z]\:\\.*/
var winLis = [...lis].filter(
li=>li.dataset.path.match(windowsPathRegEx)
);
console.log(winLis);
<li id='a' data-path="C:\Windows\System32\etc">
li with windows path
</li>
<li id='b' no-data>
li without path
</li>
<li id='c' data-path="C:\Program Files\whatever">
another li with windows path
</li>
<li id='d' data-path="/usr/local/share">
li with linux path
</li>
Just develop an adequate regex for your needs and you're ready to go.
I don't use VSCode, but I imagine that if it's stylable, there should be some kind of js initialization where you can put this code.
There is now a solution!
I asked the CSSWG on GitHub and in JavaScript at least the escaped attribute needs to be run through the String.raw method to further solidify the escape. The example given was as follows (uses Element.matches and manually escapes, but also does indeed work for Element.querySelector and/or with CSS.escape results):
var li = document.createElement("li");
li.dataset.path = String.raw`c:\WINDOWS\system32\mspaint.exe`;
li.matches(String.raw`li[data-path^="c:\\WINDOWS\\system32\\mspaint.exe"]`); // true
li.matches(String.raw`li[data-path^=c\:\\WINDOWS\\system32\\mspaint\.exe]`); // true
Huzzah! Will require a properly ES6-compatible browser.

How to traverse the DOM to get to specific element

I'm having a brain freeze trying to remember what the best way is to access the data attribute on an anchor tag when clicking a button that's not inside the tags container, could someone assist me on how I would do this, so if I click .js-watchlist-add I want to get the data-id of .js-film-entry:
JS
<div class="ctn">
<a href="/movie/{{id}}" class="film-entry js-film-entry" data-id="{{id}}">
<img src="{{poster}}" class="film-img">
<div class="result-film-details">
<h2 class="film-title">{{title}}</h2>
<p class="film-release-date">Released {{releaseYear}}</p>
<ul class="result-stats-tabs clearfix">
<li>{{vote_average}} <span>Vote Average</span></li>
<li>{{vote_count}} <span>Vote Count</span></li>
</ul>
</div>
</a>
<div class="cta-ctn">
<button class="watchlist-add js-watchlist-add">Add to watchlist</button>
<button class="watchlist-remove js-watchlist-remove">Remove from watchlist</button>
</div>
</div>
You can use closest() to get the parent element and find() to get the descendant within parent.
Live Demo
$(this).closest('.ctn').find('.js-film-entry').data('id');
You can use jQuery's has and find methods.
$('.ctn').has(this).find('.js-film-entry')
Adil's answer is great,
Though it may be more efficient to use children versus find, to avoid traversing descendants.
$(this).closest('.ctn').children('.js-film-entry').data('id');
Just to give another DOM traversal example, you could also use:
$(this).parent().siblings('.js-film-entry').data('id');

CSS Multiple ID with Same Name Work Around?

First I realize ID's should be unique. But right now I can't do much about that. I have a javascript plug-in that is generating ID names and for one page it works great. The issue is in creating another page, it will start over using the same naming convention. For example:
Page 1
<ul id="1">
Page 2
<ul id="1">
So if I am trying to style ul#1 on Page 1 it will also style ul#1 on Page 2. So, any suggestions on how to separate our the two id's? This html is generated by the JS, otherwise I would just attach a class to it.
Thanks.
First, the unique ID suggestion is restricted to a page. It is perfectly fine to have multiple ID's on different pages. The best way to overcome this is to add a ID to the body.
Page1
<body id="Page1">
<ul id="1">
<li></li>
</ul>
</body>
Page2
<body id="Page2">
<ul id="1">
<li></li>
</ul>
</body>
CSS
#Page1 #1
{
//Some style for page 1, ID 1
}
#Page2 #1
{
//Some style for page 2, ID 1
}
Can you attach a class around it ? Have a div or span some other element surround your code that does the generation and assign a class to it.
I'd say you have to use different style sheets on each page if you need different styles for the same ids, but this will be a pain to maintain as you make styling changes.
Alternatively you could you assign a class to one of the page's UL tags and then create a style for that class.
First of all, the plugin is still not generating the correct ids because ids can't be numbers. To answer your question, try to figure out some parent element that might be different between the two pages probably in which case you can use CSS such as this:
#parent ul#1{
/* styles here */
}
#parent2 ul#1{
/* styles here */
}
page1:
<div id="parent">
<ul id="1">
............
page2:
<div id="parent2">
<ul id="1">
............
So you need to find out a some parent element of ul which is not common between the two pages. This is the only possibility that comes to my mind where you have no control over changing the ids or replacing them with classes since they are being generated by the plugin.
You need something to distinguish them if you want them styled separately. If you cannot modify those tag you could probably use some parent container like:
<div id="parent1">
<ul id="id1" />
</div>
<div id="parent2">
<ul id="id1" />
</div>
and then:
#parent1 ul {
...
}
#parent2 ul {
...
}
Also notice that an id cannot start with a number as in your case. You should probably consider switching/modifying this plugin.
One thing I commonly do is attach a class to the body for each page. <body class="home_section"> and then you could style based on that class .home_section ul#1 {}.
Also, IDs must begin with a letter.

How to aceess span directly using jQuery

I want to know how to get access of this [span class="myclass"] in below html structure..
<ul>
<li class="first">
<span class="myclass"></span>
<div class="xx">
<span></span>
........
</div>
<li >
<span class="myclass"></span>
<div class="xx">
<span></span>
........
</div>
</ul>
Here I need to write one function in [span class="myclass"], but i cant do it using $(".myclass") [I have few issues] I just want to directly access the span itself.How to do this..?
EDIT:the sln by phoenix is working fine..but lets say(just for my knowledge) the structure is
<li >
<span class="myclass"></span>
<div class="xx">
<li>
<span></span>
</li>
........
</div>
</ul>
so why the span inside 2 nd li(which is under div) is not getting the ref, is it bcoz they are not in the same level..if I need to access them do I need to do some thing like
enter code here
$("li").next(".xx").find(li span:first-child )..or something else is there?
Thanks.
$("li span.myclass")
EDIT: Okay then maybe with
$("li span:first") //EDIT: Don't do that. See below.
apparently :first stops after the first hit. So :first-child is the way to go.
which will select the first span in every li-element. But this can be tricky in case you have other li-elements with spans inside...
EDIT: If you can't use the class you already have, maybe assigning an additional class helps?
<span class="myclass newClass"></span>
...
var spans = $("li span.newClass");
EDIT:
As phoenix pointed out
$("li span:first-child")
returns a list with all span elements that are the first child of a li-element. I don't know if jQuery treats textnodes as child nodes. So if you have a space between <li> and <span>, this might be counted as the first-child. So check if you have any whitespace between your elements beside line breaks.
If span is the first child then you can use
first-child
selector
$("li span:first-child");

Categories

Resources