Here is the source code in a page for a user to choose to export a CSV file.
<div class="btn-group pull-right" style="margin-right: 10px">
<a class="btn btn-sm btn-twitter"><i class="fa fa-download"></i> 导出</a>
<button type="button" class="btn btn-sm btn-twitter dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>
<span class="sr-only">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu" role="menu">
<li>全部</li>
<li>当前页</li>
<li><a href="/admin/order?_export_=selected%3A__rows__" target="_blank" class='export-selected'>选择的行</a></li>
</ul>
</div>
The purpose of the code is to choose 当前页 out of 3 options and fire up the export event. Here is the snippet of the code for exporting CSV:
await page._client.send('Page.setDownloadBehavior', {behavior: 'allow',
downloadPath: '/tmp'})
res = await Promise.all([
page.waitForNavigation({waitUntil: 'load', timeout: 60000}),
page.click('a[href$="/admin/order?_export_=page%3A1"]'),
]);
The error is:
Error: No node found for selector: a[href$="/admin/order?_export_=page%3A1"]
at assert (C:\node_modules\puppeteer\lib\helper.js:259:11)
at Frame.click (C:\node_modules\puppeteer\lib\FrameManager.js:704:5)
at <anonymous>
There are 2 questions here. The first is: is page.click() right for the purpose? There is page.select() which does the similar thing. Second is: what is the correct selector for the 2nd options I would like to choose?
Make sure you are clicking the dropdown button before clicking the link:
await page.click('.btn-twitter.dropdown-toggle');
Then, once the link is visible, you can click it using the following selector:
await page.click('a[href$="/admin/order?_export_=page%3A1"]');
You may need to use page.waitForSelector() to wait for the element to be added to the DOM if the element is dynamically generated:
await page.waitForSelector('a[href$="/admin/order?_export_=page%3A1"]');
page.click() is correct if you want to follow the link. page.select() is for selecting a value in a select box.
Regarding your query selector, you are missing a closing bracket and quotation marks. It should work like this:
page.click('a[href$="/admin/order?_export_=page%3A1"]')
Related
I am trying to navigate a dropdown button that operates via JavaScript. However, no matter what I try, the HTML list items it should have never seem to show up in selenium.
An image of the dropdown button:
Inspector page source:
<div class="dropdown" style="border-bottom: 1px solid #ebebeb; padding-bottom: 2px;">
<a class="btn btn-default btn-sm" onclick="$(this).parent().toggleClass('open')" title="Select an Event" style="width: 220px">
<span id="event-selection-span-id">No event selected.</span>
<span class="fa fa-caret-down routing-toolbar-menu"></span>
</a>
<ul class="dropdown-menu user-dropdown dropdown-menu-left" id="call-events-list-id">
<li title="ADRC Archived Call" event_definition_id="2f617fc5-c0b0-492a-92e2-561c39c239fc" form_code="AACOG_ADRC_CCC_ARCH" onclick="CallCenter.SetEvent(this)" class="list-group-item event-group-list-item">ADRC Archived Call</li>
<li title="ADRC Information Call" event_definition_id="0a22deba-4788-4647-bee6-47305e182eca" form_code="AACOG_ADRC_CCC" onclick="CallCenter.SetEvent(this)" class="list-group-item event-group-list-item">ADRC Information Call</li>
</ul>
</div>
Selenium page source:
<div class="dropdown open" style="border-bottom: 1px solid #ebebeb; padding-bottom: 2px;">
<a class="btn btn-default btn-sm" onclick="$(this).parent().toggleClass('open')" title="Select an Event" style="width: 220px">
<span id="event-selection-span-id">No event selected.</span>
<span class="fa fa-caret-down routing-toolbar-menu"></span>
</a>
<ul class="dropdown-menu user-dropdown dropdown-menu-left" id="call-events-list-id"></ul>
</div>
Here is what I've tried so far, all unsuccessful:
Originally just tried finding the elements and using the .click() method to click in them. (e.g. driver.find_element_by_xpath('//*[#id="step-3"]/div[2]/a').click() then driver.find_element_by_xpath('//*[#id="call-events-list-id"]/li[2]').click(). Selenium could not find the list element in the second line.
Then I tried a method that's worked for me before when the previous one didn't: finding the elements then using driver.execute_script("arguments[0].click()", btn) for each one. Like before, it worked for the main dropdown button but not the list item that should show up afterwords.
So I figured I could just execute the javascript manually with the elements' JS paths using driver.execute_script("$(document.querySelector('#step-3 > div.dropdown > a')).parent().toggleClass('open');") then driver.execute_script("CallCenter.SetEvent(document.querySelector('#call-events-list-id > li:nth-child(2)'));"). This still didn't work and the list elements still did not show up in selenium's page source.
The strangest thing is the list elements show up in the inspector before you even click the main dropdown button. Therefore I should just be able to execute the second JS line manually with no problems, and that works fine when I do it in the browser. I have also tried just waiting for the list elements to show up when the source is loaded but they never show up no matter how long I set the delay for.
First to click on the element with text as Select an Event and then from the dropdown to click on the element with title as ADRC Information you need to induce WebDriverWait for the element_to_be_clickable() and you can use either of the following locator strategies:
Using CSS_SELECTOR:
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "a.btn.btn-default.btn-sm[title='Select an Event']"))).click()
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "a.btn.btn-default.btn-sm[title='Select an Event'] +ul#call-events-list-id li[title='ADRC Information Call']"))).click()
Using XPATH:
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//a[#class='btn btn-default btn-sm' and #title='Select an Event']"))).click()
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//a[#class='btn btn-default btn-sm' and #title='Select an Event']//following::ul[#id='call-events-list-id']//li[#title='ADRC Information Call']"))).click()
Note: You have to add the following imports :
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
I have an if/else statement for our navigation where when a user clicks on the link associated with the dropdown-toggle class a submenu pops up.
We have multiple links on our navigation with the dropdown-toggle class. I only need to target 1 not all of them that's why I'm using .eq(0).
When the submenu appears a new class called open gets injected to the parent <li>.
For some reason I can't get this if/else statement to fully work. I have the first half working but not the second.
For the else portion I'm trying to add what would happen if the open class got injected and the active-nav-tab class gets added.
active-nav-tab is the class used for the state when users are on the current page. active-nav-tab is supposed to disappear when a user clicks on dropdown-toggle of the current page only and when they click outside the window of the submenu. Then when a user clicks on a navigation link with dropdown-toggle, the active-nav-tab again is supposed to appear on the current page. Hopefully that makes sense. I've been at this for like two days.
Code Below:
$('.dropdown-toggle').click(function (e) {
if ($(this).not('open')) {
$('.dropdown-toggle').eq(0).removeClass('active-nav-tab');
alert('test1');
}
else {
$('.dropdown-toggle').eq(0).addClass('active-nav-tab');
alert('test2');
}
});
HTML
<li>
<a class="link dropdown-toggle active-nav-tab" role="button">
<span class="tab-icon icon-header-language"></span>
<span class="tab-text">Region</span>
</a>
</li>
<li class="open">
<a class="link dropdown-toggle" role="button">
<span class="tab-icon icon-header-language"></span>
<span class="tab-text">Region</span>
</a>
</li>
<li>
<a class="link dropdown-toggle" role="button">
<span class="tab-icon icon-header-language"></span>
<span class="tab-text">Region</span>
</a>
</li>
I don't know what I'm exactly missing. Any help is gladly appreciated.
Thanks!
The quickest change you can make to your code to get the behavior you want is to replace
if ($(this).not('open')) {
With
if ($('.dropdown-toggle').eq(0).hasClass('active-nav-tab')) {
But without knowing your use case or how many "dropdown-toggle" elements you have it's hard to say if this would be a complete solution or not.
This seems to have solved my issue more or less. I'll see what QA says lol
Thank you!
$('.active-nav-tab').click(function (e) {
if ($(this).parents().hasClass("open")) {
$(this).addClass('active-nav-tab');
alert('activated');
}
else {
$(this).removeClass('active-nav-tab');
alert('deactivated');
}
});
this will process clicking on the first element with "dropdown-toggle" class
$('.dropdown-toggle:first').click(function () {
$(this).toggleClass("active-nav-tab");
if ($(this).hasClass("active-nav-tab")) {
alert ("Tab activated");
} else {
alert ("Tab deactivated");
}
}
also jQuery function not(selector) should be used on enumeration of objects to filter it.
I have several dynamically created links which rendered as buttons and the buttons texts are replaced with icons. I need to toggle one of the link button icons when clicked. The method that I am using is not working out. See code below: I do not want to use JQuery at this time unless it’s within a function.
<a class="button" onclick="command('removeFormat');" title="Remove Format"><i class="fas fa-eraser"></i></a>
<a class="button" onclick="command('fullScreen');" title="Full Screen"><i class="fas fa-expand"></i></a>
<a class="button" onclick="doToggleView();" title="Source"><i class="fa fa-code"></i></a>
<a class="button" onclick="submitForm();" title="Save"><i class="far fa-save"></i></a>
//JS
function command(cmd){
if(cmd == 'fullScreen'){
$(".fa-expand").toggleClass('fa-expand fa-compress');
}else{
$(".fa-compress").toggleClass('fa-compress fa-expand');
}
}
I also try using the following codes:
$("i").toggleClass('fa-compress fa-expand');
$("a .button").find("i").toggleClass('fa-expand fa-compress');
This is the fix to resolve the issue.
function command(cmd){
$('i.fas').toggleClass('fa-expand fa-compress');
}
I'm trying to click a button that brings up an edit screen on the same page.
Eg. Click "edit user", edit screen pops up on the same page without redirecting, change name, save. This button does not redirect to a new page.
The code for this button is the following:
<i class="fa fa-pencil"></i> Edit
For some reason I can click every other button on this website that's in the same exact form but I can't click this one. There are no IDs at all so calling by the class name is the only other method I know.
This is what I've tried:
setTimeout(function() {
document.getElementsByClassName(" btn btn-xs btn-inverse btn-modify")[0].click();
}, 3000);
I tried using some Jquery instead as well but no luck. Am I doing something wrong or is this all that I can really do? The other buttons I clicked either redirected me to a different site or just brought up some information on the same screen.
Thanks in advance.
Edit:
It seems that when I try iterating through the different buttons, who all have similar starting class names, I can iterate and click every button except for the edit button. So it's safe to assume that this isn't an issue with the code, so thank you everyone for the help and suggestions.
Edit #2:
Here is the code for all three buttons:
<div class="btn-group"><i class="stm stm-goog"></i>  
<i class="fa fa-pencil"></i> Edit
<i class="fa fa-bar-chart"></i><button type="submit" class="btn btn-xs btn-danger btn-submit"><i class="fa fa-trash-o"></i></button></div>
When searching for elements by class, it's better to use:
document.querySelector(); // Finds first matching element only
and
document.querySelectorAll(); // Finds all matching elements
instead of document.getElementsByClassName() as this returns a "live node list" and hinder performance.
When searching for classes with these methods, remember to include the dot (.) to signify classes and when multiple classes are used, do not include spaces. The spaces are only used when setting multiple classes.
Also, if there is only one class that is unique to the element you wish to find, you only need to search on that one class.
Lastly, only use a elements for navigation. If you simply need something to click, just about any object can have a click event handler.
var edit = document.querySelector(".btn-modify");
edit.addEventListener("click", function(){ console.log("clicked")});
setTimeout(function() {
edit.click();
}, 3000);
// Addtional test
var edit = document.querySelectorAll(".btn.btn-xs.btn-inverse")[1];
edit.addEventListener("mouseover", function(){
console.log("moused over");
});
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<div class="btn-group">
<a href="https://google.com" target="_blank" class="btn btn-xs btn-inverse">
<i class="stm stm-goog">X</i> <!-- <-- You missed a semi-colon here. -->
</a>
<a href="#" data-id="29508"
class="btn btn-xs btn-inverse btn-modify">
<i class="fa fa-pencil"></i> Edit
</a>
<a href="#" data-page-modal="https://*randomwebsite*.com/manage/stats/301&q=11"
class="btn btn-xs btn-inverse">
<i class="fa fa-bar-chart">X</i>
</a>
<button type="button" class="btn btn-xs btn-danger btn-submit"><i class="fa fa-trash-o"></i>TEST</button></div>
Because you are trying to select the element by its class you need to loop through the elements.
So if you wanna use jQuery you would do like so:
$(".btn-modify").each(function(){
$(this).click(function(){
your code here
});
});
If you want to fetch the element having all the class, try this:
JQuery
$(".btn.btn-xs.btn-inverse.btn-modify")
Plain JavaScript
document.querySelectorAll(".btn.btn-xs.btn-inverse.btn-modify")
Giving a space in between a class names in a css like selector meant that (next one) is a nested element in any depth. use . to indicate its a class, and write them together (without a space) you are searching for elements having all the lasses. And getElementsByClassName is not a css like selector, its can take only a class name and all those element having that class.
I can't seem to make the drop-down work. When you click on 'action' nothing happens.
I double-checked the CSS and JS links and all seems ok, tried with Bootstrapv4 and 3.3.7 and none works. Any thoughts?
Check my app https://codepen.io/thomasfaller/pen/NpGpPP
<iframe height='265' scrolling='no' title='XE.com WebApp' src='//codepen.io/thomasfaller/embed/NpGpPP/?height=265&theme-id=0&default-tab=html,result&embed-version=2' frameborder='no' allowtransparency='true' allowfullscreen='true' style='width: 100%;'>See the Pen <a href='https://codepen.io/thomasfaller/pen/NpGpPP/'>XE.com WebApp</a> by Thomas Faller (<a href='http://codepen.io/thomasfaller'>#thomasfaller</a>) on <a href='http://codepen.io'>CodePen</a>.
You are trying to use bootstrap dropdown as a select input. Dropdowns are supposed to just call some actions, not behave like "select" elements. So there is no such functionality out of the box as you want. You have to have some JS to make it work.
So my very quick and dirty solution just to make it work:
1) Assign id="selected" to currently selected value:
<button type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-eur" aria-hidden="true" id="selected"></i>
</button>
2) Assign onclick handler on dropdown items:
<a class="dropdown-item" onclick="select('eur')"><i class="fa fa-eur" aria-hidden="true"></i></a>
3) And this handler could be like this:
function select(currency) {
let selected = document.getElementById('selected');
selected.className = `fa fa-${currency}`
}
Codepen: https://codepen.io/anon/pen/KWdOxa
But you still have to save somehow a selected value to make your calculations proper in JS code.
So you had better use a special bootstrap element for select inputs - https://silviomoreto.github.io/bootstrap-select/