The click on the div element with role='button' isn't operate.I need to click on the icon, but I can't do it.
html:
<div class="list">
<div class="item">
<div role="button" tabindex="-1">
<strong>ItemName2</strong>
</div>
<div class="d">
<div class="item-icon" role="button" tabindex="-1" style="display: none">
<i aria-label="icon: edit" class="edit"></i>
</div>
</div>
</div>
<div class="item"> ... </div>
<div class="item"> ... </div>
<div class="item"> ... </div>
</div>
js:
try {
await driver.get("http://127.0.0.1");
let findButtons = await driver.findElements(By.tagName('strong'));
let buttons = findButtons.map(elem => elem.getText());
const allButtons = await Promise.all(buttons);
console.log(allButtons); // It is displayed all button values, such as ItemName1
let tButton;
for (let i = 0; i < findButtons.length; i++) {
if (allButtons[i] == 'ItemName2') {
tButton = await findButtons[i];
tButton.click(); // I try to click on this button, where value = ItemName2
console.log(allButtons[i]); // It is displayed button value 'ItemName2'
}}}
Console error:
(node:12254) UnhandledPromiseRejectionWarning: StaleElementReferenceError: stale element reference: element is not attached to the page document
You are getting stale element exception because you are trying to get the element with old references. Each time you click on the element in your loop, the elements reference will be updated and allButtons[i] does not work. In order to handle this you have to get the latest refers of buttons. Try the below.
js:
const { By, Key, until } = require("selenium-webdriver");
const webdriver = require("selenium-webdriver");
require("chromedriver");
async () => {
let driver = await new webdriver.Builder().forBrowser("chrome").build();
try {
await driver.get("http://10.203.201.77:8000/login");
let findButtons = await driver.findElements(By.tagName('strong'));
let buttons = findButtons.map(elem => elem.getText());
const allButtons = await Promise.all(buttons);
console.log(allButtons); // It is displayed all button values, such as ItemName1
let tButton;
for (let i = 0; i < findButtons.length; i++) {
buttons = findButtons.map(elem => elem.getText()); # getting the button so that the elements refererence will refresh
if (allButtons[i] == 'ItemName2') {
tButton = await findButtons[i];
tButton.click(); // I try to click on this button, where value = ItemName2
console.log(allButtons[i]); // It is displayed button value 'ItemName2'
}
}
console.log("DONE");
} catch (e) {
console.log(e);
} finally {
await driver.quit();
}
}
}
I found solution:
let findButtons = await driver.findElements(By.tagName('strong'));
let buttons = findButtons.map(async elem => await elem.getText()); // I add async & await
const allButtons = await Promise.all(buttons);
console.log(allButtons); // There are all itemName
Related
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")
I tried to make this function getWeekly() run by default when the site first loads but it only runs this part of the code:
dailyBtn.classList.add("active");
weeklyBtn.classList.remove("active");
monthlyBtn.classList.remove("active");
but not the loop under. But it'll show data when I click on the tags. Any ideas? Thanks.
Git link: https://github.com/thusmiley/time-tracking-dashboard.git
Live site link: https://thusmiley.github.io/time-tracking-dashboard
index.html
<div class="report-bottom">
Daily
Weekly
Monthly
</div>
</div>
<div class="stat-wrapper">
<div class="work-bg bg"></div>
<div class="stat" id="work">
<div class="category">
<h2>Work</h2>
<img src="./images/icon-ellipsis.svg" alt="" />
</div>
<div class="card">
<h3 class="work-current"></h3>
<p class="work-previous"></p>
</div>
</div>
</div>
script.js
let Data = [];
fetch("./data.json")
.then((response) => response.json())
.then((data) => Data.push(...data));
let card = document.querySelectorAll(".card");
let dailyBtn = document.getElementById("daily");
let weeklyBtn = document.getElementById("weekly");
let monthlyBtn = document.getElementById("monthly");
function getDaily() {... }
function getWeekly() {
dailyBtn.classList.remove("active");
weeklyBtn.classList.add("active");
monthlyBtn.classList.remove("active");
for (let i = 0; i < Data.length; i++) {
let splitTitle = Data[i].title.split("");
splitTitle = splitTitle.filter((e) => String(e).trim());
let joinTitle = splitTitle.join("");
let current = document.querySelector(`.${joinTitle.toLowerCase()}-current`);
let previous = document.querySelector(
`.${joinTitle.toLowerCase()}-previous`
);
current.innerHTML = `${Data[i].timeframes.weekly.current + "hrs"}`;
previous.innerHTML = `${
"Last Week - " + Data[i].timeframes.weekly.previous + "hrs"
}`;
}
}
function getMonthly() {... }
window.onload = getWeekly();
The very first time that you load the page Data.length is equal to 0, and that's why the loop doesn't iterate. You are using an asynchronous call to load Data, and when getWeekly() is called for the first time, Data is not ready with the info yet (and it only works after when its ready).
You should wait until Data is completely load first, you can try a callback function or even try $.when() using jquery.
Why when you are searching for something else is deleting the previous contents ?For example first you search for egg and show the contents but then when you search for beef the program deletes the egg and shows only beef.Code :
const searchBtn = document.getElementById('search-btn');
const mealList = document.getElementById('meal');
const mealDetailsContent = document.querySelector('.meal-details-content');
const recipeCloseBtn = document.getElementById('recipe-close-btn');
// event listeners
searchBtn.addEventListener('click', getMealList);
mealList.addEventListener('click', getMealRecipe);
recipeCloseBtn.addEventListener('click', () => {
mealDetailsContent.parentElement.classList.remove('showRecipe');
});
// get meal list that matches with the ingredients
function getMealList(){
let searchInputTxt = document.getElementById('search-input').value.trim();
fetch(`https://www.themealdb.com/api/json/v1/1/filter.php?i=${searchInputTxt}`)
.then(response => response.json())
.then(data => {
let html = "";
if(data.meals){
data.meals.forEach(meal => {
html += `
<div class = "meal-item" data-id = "${meal.idMeal}">
<div class = "meal-img">
<img src = "${meal.strMealThumb}" alt = "food">
</div>
<div class = "meal-name">
<h3>${meal.strMeal}</h3>
Get Recipe
</div>
</div>
`;
});
mealList.classList.remove('notFound');
} else{
html = "Sorry, we didn't find any meal!";
mealList.classList.add('notFound');
}
mealList.innerHTML = html;
});
}
It's because you are replacing the contents in the mealList element every time.
A simple workaround would be to retrieve the the innerHTML values before you update it.
Something like
let html = mealList.innerHTML;
rather than starting off empty every time you call the function should do the trick.
When i try to run my index.html file
<div class="quote-text">
<i class="fas fa-quote-left"></i>
<span id="quote"></span>
</div>
<!-- author element -->
<div class="quote-author">
<span id="author"></span>
</div>
my textContent method is not showing anything.
const quoteText = document.getElementById("quote");
const authorText = document.getElementById("author");
let apiQuotes = []
function newQuote(){
const ranQuote = apiQuotes[Math.floor(Math.random() * apiQuotes.length)];
console.log(ranQuote);
console.log(ranQuote.text);
console.log(ranQuote.author);
console.log(quoteText);<!-- this shows null -->
console.log(authorText); <!-- this shows null -->
quoteText.textContent = ranQuote.text;
authorText.textContent = ranQuote.author;
};
async function getQuotes(){
const apiUrl = 'https://type.fit/api/quotes';
try{
const response = await fetch(apiUrl);
apiQuotes = await response.json()
newQuote();
}
catch(error){
}
}
getQuotes();
I console logged span element value and its logging null. And i can't change my desired value in span element
this is console screeenshot
Your code is working as expected, as you can see in the snippet below.
Make sure you are running this script after DOM elements are created.
Put your script just before </body>, or make sure it executes after DOMContentLoaded (Stack snippet below does this ^)
const quoteText = document.getElementById("quote");
const authorText = document.getElementById("author");
let apiQuotes = []
function newQuote() {
const ranQuote = apiQuotes[Math.floor(Math.random() * apiQuotes.length)];
console.log(ranQuote);
console.log(ranQuote.text);
console.log(ranQuote.author);
console.log(quoteText); <!-- this shows null -->
console.log(authorText); <!-- this shows null -->
quoteText.textContent = ranQuote.text;
authorText.textContent = ranQuote.author;
};
async function getQuotes() {
const apiUrl = 'https://type.fit/api/quotes';
try {
const response = await fetch(apiUrl);
apiQuotes = await response.json()
newQuote();
} catch (error) {}
}
getQuotes();
<div class="quote-text">
<i class="fas fa-quote-left"></i>
<span id="quote">Wait for the request to finish...</span>
</div>
<!-- author element -->
<div class="quote-author">
<span id="author"></span>
</div>
I want to get a specific element when the button clicked. I select the clicked data with DOM Traversal method, Is there any method I can use to get an element faster?
<div class="item" id="dessert">
<div class="item-image-div">
<img src="mcd/<?= $row["image"]; ?>" alt="" class="item-image" onclick="getSrc(this.src)">
</div>
<div class="item-detail">
<div class="item-name" onclick="getSrc(this.name)"><?= $row["item"]; ?></div>
<div class="item-order">
<div class="item-price"><?= $row["price"]; ?></div>
<button class="order">
<img src="icon/order.png" alt="" width="40px">
</button>
</div>
</div>
</div>
let orderBtn = document.querySelectorAll('.order');
orderBtn.forEach(function(btn){
btn.addEventListener('click', (e)=>{
if (e.target.parentElement.classList.contains('order')) {
let fullPath = e.target.parentElement.parentElement.parentElement.previousElementSibling.children[0].src;
let pos = fullPath.indexOf('mcd') + 3;
let partPath = fullPath.slice(pos);
let itemId = e.target.parentElement.parentElement.parentElement.parentElement.id;
const item = {};
item.img = `${partPath}`;
console.log(item);
let itemName = e.target.parentElement.parentElement.previousElementSibling.textContent;
let itemPrice = e.target.parentElement.previousElementSibling.textContent;
console.log(itemName);
console.log(itemPrice);
}
});
});
Instead of adding a listener to the button and checking inside the handler that the clicked parent is the .order, add a listener to the <img> instead. (Or, is that even needed? Could you permit clicks on both the <img> and the outer <button>, maybe? That'd make more sense to me, if possible)
Utilize .closest to avoid having to use lots of difficult-to-read .parentElement accesses.
Use querySelector to readably navigate down to descendants of the whole .item.
document.querySelectorAll('.order > img').forEach((img) => {
img.addEventListener('click', () => {
const item = img.closest('.item');
const fullPath = item.querySelector('img').src;
const pos = fullPath.indexOf('mcd') + 3;
const partPath = fullPath.slice(pos);
const itemId = item.id;
const itemName = item.querySelector('.item-name').textContent;
const itemPrice = item.querySelector('.item-price').textContent;
});
});