How can I find a <ul> elements from dropdown in a page using Protractor? - javascript

<ul _ngcontent-c9 class="results-dropdown">
<li _ngcontent-c9>
<a _ngcontent-c9>XYZ</a>
</li>
</ul>
I want to find XYZ value from dropdown and click on it.
Please let me know if anyone is having guideline for the same.
TIA

var dropdown = element(by.css('ul'));
var dropdownValues = element(by.css('ul > li > a'));
var expectedText = 'XYZ';
dropdown.click();
return dropdownValues.filter((eachValue) => {
return eachValue.getText().then((valueText) => {
if (valueText === expectedText) {
return eachValue.click();
}
});
});
or
var dropdown = element(by.css('ul'));
var expectedText = 'XYZ';
dropdown.click();
element(by.linkText(expectedText)).click();

Related

how to render appendChild Js without duplicate

I am a new learning JS. Who can help me complete this code. I have 2 problem:
render child Node user Chat when click without duplicate
how to remove child Node user when close chat window
full code is here: Jsfiddle
// event handling when click
handleEvents: function () {
let _this = this;
userChatList.onclick = function (e) {
const userNode = e.target.closest(".user-chat__item");
if (userNode) {
userIndex = Number(userNode.getAttribute("user-num"));
_this.renderUserChat(userIndex);
const getChatWithItems = document.querySelectorAll(".chat-with__item");
getChatWithItems.forEach(item => {
item.onclick = function(e){
const itemNode = e.target.closest(".chat-with__top i");
if(itemNode){
chatWithList.removeChild(chatWithItem);
}
}
})
}
}
},
//render user chat with someone
renderUserChat: function (num) {
// console.log(userIndex);
chatWithItem = document.createElement("li");
chatWithItem.classList.add("chat-with__item");
chatWithItem.setAttribute('user-num', num);
chatWithItem.innerHTML = `
<div class="chat-with__top">
<div class="chat-with__img">
<img src="${this.users[num].img}" alt="${this.users[num].name}">
<span class="user__status ${this.users[num].status}"></span>
</div>
<p class="chat-with__name">${this.users[num].name}</p>
<i class="fa-solid fa-xmark"></i>
</div>
<div class="chat-with__body">
<ul class="chat__text">
<li class="chat-text__user">Hey. 👋</li>
<li class="chat-text__user user__chatting">I am here</li>
<li class="chat-text__user user__chatting">What's going on?</li>
<li class="chat-text__user">Have you finished the "project 2" yet?</li>
<li class="chat-text__user user__chatting">I have been fixed bugs</li>
<li class="chat-text__user">OK.</li>
</ul>
</div>
<div class="chat-width__footer">
<i class="fa-solid fa-image"></i>
<i class="fa-solid fa-folder"></i>
<div class="chat-width__input">
<input type="text" id="send-sms" name="send SMS" placeholder="...">
</div>
<i class="fa-solid fa-paper-plane-top"></i>
</div>
`
chatWithList.appendChild(chatWithItem);
},
<ul class="chat-with__list">
</ul>
I have not still known how to solve it, up to now
Just keep track which chat windows are opened in an object.
To give you basic idea of the concept:
// storage for opened chat windows
// this variable must be accessible by event handlers
const openedChats = {};
In chat opened event handler:
if (openedChats[userId]) //check if chat already opened
return;
const chatWithItem = document.createElement("li");
...
openedChats[userId] = chatWithItem; //store window
chatWithList.appendChild(chatWithItem); //show window
In chat close event handler:
const chatWithItem = openedChats[userId]; // get opened chat
if (chatWithItem)
{
chatWithItem.parentNode.removeChild(chatWithItem); // destroy window
delete openedChats[userId]; // remove window
}
If you need to get list of all userIds that have opened chat windows, use:
const openedChatsIds = Object.keys(openedChats);
Finnaly I find the way to code. This is my way
handleEvents: function () {
let _this = this;
let currentChat = [];
userChatList.onclick = function (e) {
const userNode = e.target.closest(".user-chat__item");
if (userNode) {
userIndex = Number(userNode.getAttribute("user-num"));
// get value 'userIndex' for currentChat array
function getCurrentChat(arr, index) {
arr.push(index);
}
// check value userIndex in a currentChat array
function checkCurrentChat(arr, index) {
if (arr.indexOf(index) < 0) {
getCurrentChat(currentChat, userIndex);
return true;
} else {
return false;
}
}
let isExisted = checkCurrentChat(currentChat, userIndex);
// console.log(isExisted);
if (isExisted) {
_this.renderUserChat(userIndex);
}
const getChatWithItems = chatWithList.querySelectorAll(".chat-with__item");
getChatWithItems.forEach( function(item) {
item.onclick = function (e) {
const closeChat = e.target.closest(".chat-with__top i");
if(closeChat){
const getNum = Number(closeChat.parentElement.getAttribute("user-num"));
chatWithList.removeChild(item);
const findNum = currentChat.indexOf(getNum);
currentChat.splice(findNum, 1);
}
}
})
}
}
}
inside, i add an attribute to get number (userIndex):
<div class="chat-with__top" user-num ="${num}">
if you use second .parentElement, it will ok.
closeChat.parentElement.parentElement.getAttribute("user-num")

listen to keypress and add to input form

I am trying to add a search filter to my website, and I would like it to be dynamic without clicking on the input form, i.e. listen to keystrokes.
currently I have the following code:
JS:
function myFunction() {
var input, filter, ul, li, a, i, txtValue;
input = document.getElementById("myInput");
filter = input.value.toUpperCase();
ul = document.getElementById("myUL");
li = ul.getElementsByTagName("li");
for (i = 0; i < li.length; i++) {
a = li[i].getElementsByTagName("a")[0];
txtValue = a.textContent || a.innerText;
if (txtValue.toUpperCase().indexOf(filter) > -1) {
li[i].style.display = "";
} else {
li[i].style.display = "none";
}
}
}
HTML:
<input type="text" id="myInput" onkeyup="myFunction()" placeholder="Search for names..">
<ul id="myUL">
<li>
<a href="#">
Adele
</a>
</li>
<li>
<a href="#">
Vivian
</a>
</li>
<li>
<a href="#">
Lucy
</a>
</li>
</ul>
I have tried adding the following to my script tag:
window.addEventListener('keypress', function (e) {
document.getElementById("myInput").value += event.key;
});
But:
it doesn't recognize backspace in order to delete from the search
filter.
it doesn't search. I have to press the search box, which I want to eventually hide, in order for the filter to work.
I am pretty new to JS and would like to hear if anyone encountered this or something similar.
Thanks.
I'm not encouraging this interface pattern... but I am encouraging you to have fun - and be creative / which you are doing.
Here's how I'd think about it / loosely. https://jsfiddle.net/sheriffderek/dbr1ku3e/
// "I am trying to add a search filter to my website"
// "and I would like it to be dynamic without clicking on the input form, i.e. listen to keystrokes."
// (1) keep a string in memory - of what is typed - (allow for backspace etc...)
// (2) keyup - filter the menu - based on the string + (3) render the updated list ?
const queryLetters = []; // not really a "string though" - but maybe easier to work with!
let queryString = '';
const $output = document.querySelector('.current-search');
function buildSearchString(letterArray) {
var string = '';
letterArray.forEach( function(letter) {
string += letter; // yes - you could also use map and split instead etc.
})
console.log(`Search for "${string}"`);
return string;
}
function noteQuery(pressedKey) {
if (pressedKey == "Backspace") { // BUT WHAT ABOUT *shift* and other stuff...
queryLetters.pop(); // remove last letter from array
} else {
queryLetters.push(pressedKey);
}
queryString = buildSearchString(queryLetters);
$output.textContent = queryString;
}
const $menu = document.querySelector('[rel="filtered-menu"]');
function collectMenuInfo() {
// go get the HTML stuff... and then we can use it to rerender the list - instead of hiding and showing things....
// assuming this might be server-side rendered to start...
const menuItems = $menu.querySelectorAll('a');
console.log(menuItems);
const itemArray = Array.from(menuItems).map( function(item) {
return { // from NodeList to array... make a new array of 'items'
text: item.textContent,
href: item.href,
};
});
console.log(itemArray);
window.menuItems = itemArray;
}
collectMenuInfo();
function renderMenu(stuff) {
var filteredItems = stuff.filter( function(thing) {
var lowercase = thing.text.toLowerCase();
return lowercase.includes(queryString);
})
var template = '';
filteredItems.forEach( function(item) {
template += `<a href='${item.href}' class='z'>${item.text}</a>`;
});
$menu.innerHTML = template;
}
document.addEventListener('keyup', function() {
noteQuery(event.key);
renderMenu(menuItems); // could combine these into a handler -
});
span {
font-style: italic;
color: green;
}
a {
display: block;
padding: 6px;
}
<p>Type to search for: <span class="current-search"></span></p>
<nav class="menu" rel="filtered-menu">
Adele
Carl
Sheriff
Derek
Vivian
Lucy
</nav>
Use the onchange event. Whenever the value of the input changes the event is fired
https://www.w3schools.com/jsref/event_onchange.asp

Get attributes for all (unknown) <li> elements with Javascript

I need to concatenate all the title value starting from second li elements with Javascript.
The problem is that I want to use it in different pages, so I can't know the exact number of li elements.
<div id="breadcrumb">
<ul>
<li title="One">One</li>
<li title="Two">Two</li>
<li title="Three">Three</li>
<li title="Four">Four</li>
</ul>
</div>
I use a variable for each element but if one or more element is missing the var is not valid and the concat function doesn't work.
var a = document.querySelector(".breadcrumb li:nth-child(2) > a").getAttribute("title");
var b = document.querySelector(".breadcrumb li:nth-child(3) > a").getAttribute("title");
var c = document.querySelector(".breadcrumb li:nth-child(4) > a").getAttribute("title");
var d = document.querySelector(".breadcrumb li:nth-child(4) > a").getAttribute("title");
var str = a.concat(b,c,d);
console.log(str)
Is there a way to do that?
Use querySelectorAll() and map():
const res = [...document.querySelectorAll("#breadcrumb li:not(:first-of-type)")].map(el => el.getAttribute("title")).join(" ")
console.log(res)
<div id="breadcrumb">
<ul>
<li title="One">One</li>
<li title="Two">Two</li>
<li title="Three">Three</li>
<li title="Four">Four</li>
</ul>
</div>
Using a little jquery i achieved this and it should solve your issues.
let list = [];
$('#breadcrumb li').each(function(i){
if(i !== 0) { list.push($(this).attr('title')); }
});
list.toString() //One,Two,Three,Four
The method you tried to use wont scale on a large list
Two minor remarks:
If you want to access the id=breadcrumb, you have to use #breadcrumb instead of .breadcrumb
There is no a tag in your HTML-code, therefore your querySelector won't give you any result
However, let's discuss a solution:
let listElements = document.querySelectorAll("#breadcrumbs li"); // get all list elements
listElements = Array.from(listElements); // convert the NodeList to an Array
listElements = listElements.filter((value, index) => index >= 1); // remove the first element
let titleAttributes = listElements.map(listElement => listElement.getAttribute("title")); // get the title attributes for every list elements. The result is an array of strings containing the title
console.log(titleAttributes.join(", ")); // concatenate the titles by comma
You can write the above statements in a single line:
Array.from(document.querySelectorAll("#breadcrumbs li"))
.filter((value, index) => index >= 1)
.map(listElement => listElement.getAttribute("title"))
.join(", ");
EDIT: I fix my answer, thanks to Barmar
something like that ?
const All_LI = [...document.querySelectorAll('#breadcrumb li')];
let a = ''
for (let i=1; i<All_LI.length; i++) { a += All_LI[i].title }
console.log('a-> ', a )
// .. or :
const b = All_LI.reduce((a,e,i)=>a+=(i>0)?e.title:'', '' )
console.log('b-> ', b )
<div id="breadcrumb">
<ul>
<li title="One">One</li>
<li title="Two">Two</li>
<li title="Three">Three</li>
<li title="Four">Four</li>
</ul>
</div>

Apply toggle function to only clicked element using pure java script

This is my html code where i'm displaying data
<ul id="menu">
<li *ngFor="let category of componentContents.dashboardMenu;let i = index" >
<p (click)="toggle_visibility()"class="parent-menu">{{category.category}</p>
<ul id="{{(category.category).split(' ').join('-')}}" class="toggled"
*ngIf="category.subCategory.length > 0" style="display:none">
<li *ngFor="let subCat of category.subCategory">
<a routerLink={{subCat.router}} routerLinkActive="active"
{{subCat.subcategory}}</a>
</li>
</ul>
</li>
This is the function where i'm trying to display sub menus on click but all the sub menus of all p tags are getting displayed.I want to apply toggle function to only clicked p element.
toggle_visibility() {
var pm = document.getElementByClassName('parent-menu');
var e = document.getElementsByClassName('toggled');
for (let i = 0; i < pm.length; i++) {
if (e[i].style.display == 'block' || (e[i].style.display == '') {
e[i].style.display = 'none';
}
else {
e[i].style.display = 'block';
}
};
}
As i'm new to java script and angular 2. Need help to figure this out.
You should rather use
[style.display]="e[i].style.display == '' ? 'none' : 'block'"
(click)="toggle_visibility(i)"
toggle_visibility(i) {
this.e[i] = !this.e[i];
}
where e is an array with the same number of items as
componentContents.dashboardMenu

Group list-items into sub-lists based on a data attribute

I want to append the <li> from one <ul> to another <ul> that's created on the fly. I want to group the list-items into new sub-lists based on their data-group attribute.
<ul id="sortable1">
<li data-group="A">test</li>
<li data-group="A">test1</li>
<li data-group="B">test2</li>
<li data-group="B">test3</li>
<li data-group="C">test4</li>
</ul>
Basically I'm trying to loop through this list and grap all <li> from each group, and then move it to another <ul>.
This is what I have so far, but I'm not getting the expected results. I have done this in Excel in the past but can't get it to work with jQuery.
var listItems = $("#sortable1").children("li");
listItems.each(function (idx, li) {
var product = $(li);
//grab current li
var str = $(this).text();
if (idx > 0) {
//append li
str += str;
if ($(this).data("group") != $(this).prev().data("group")) {
//I should be getting test and test1.
//but alert is only giving test1 test1.
alert(str);
//need to break into groups
//do something with groups
}
}
});
How about something like this:
$(function() {
var sortable = $("#sortable1"),
content = $("#content");
var groups = [];
sortable.find("li").each(function() {
var group = $(this).data("group");
if($.inArray(group, groups) === -1) {
groups.push(group);
}
});
groups.forEach(function(group) {
var liElements = sortable.find("li[data-group='" + group + "']"),
groupUl = $("<ul>").append(liElements);
content.append(groupUl);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul id="sortable1">
<li data-group="A">test</li>
<li data-group="A">test1</li>
<li data-group="B">test2</li>
<li data-group="B">test3</li>
<li data-group="C">test4</li>
</ul>
<div id="content">
</div>
I hope I didn't misunderstand you.

Categories