getText() not working on a select from dropdown - javascript

I have a page with a select box with various options, and I'm writing an acceptance test to check that when the page first loads, the correct option is selected. I hoped to do this using WebdriverJs but for some reason getText() is always returning an empty string.
On load, I would hope that the page looks like this:
<select class='nav-menu'>
<option value="global" selected="selected">WORLDWIDE</option>
<option value="us">USA</option>
<option value="uk">UNITED KINGDOM</option>
<option value="au">AUSTRALIA</option>
<option value="ca">CANADA</option>
<option value="de">GERMANY</option>
<option value="es">SPAIN</option>
<option value="fr">FRANCE</option>
</select>
I'm then trying to find out the value of the current selected option as follows:
browser.findElement(webdriver.By.css('.nav-menu option[selected="selected"]')).getText().then(function(selectedText){
console.log("selectedText: " + selectedText);
next();
});
However, this is also logging out an empty string.
Calling the JavaScript equivalent using the Chrome developer tools is returning "WORLDWIDE" - any thoughts what is wrong with this?

I don't use WebDriverJS, so I can't prove my theory, but I guess it's because Selenium getText() will only work for visible elements?
Try using getAttribute("textContent")
Try clicking on .nav-menu first, then use getText()

Hmm, well some playing around suggests that the CSS select was working okay, but the getText() method wasn't working.
For the moment, I've got it working use the innerHTML attribute:
browser.findElement(webdriver.By.css('.nav-menu option[selected="selected"]')).getAttribute('innerHTML').then(function(selectedText){
console.log("selectedText: " + selectedText);
next();
});
So CSS selector seems reliable, but I will also try out the textContent suggestion from user1177636.

I've found selecting 'option' elements via css + the selected attribute to be unreliable.
The following works for me:
var By = require('selenium-webdriver').By;
var dd = driver.findElement(By.id('myId'));
dd.findElements(By.tagName('option')).then(function (options) {
options.forEach(function(option) {
option.isSelected().then(function(selected) {
if (selected) {
option.getText().then(function(text) {
console.log(text);
done(); //mocha async callback
});
}
});
});
});
You could wrap this into a function, something like:
var wd = require('selenium-webdriver'),
By = wd.By;
function getFirstSelected(selectList) {
var d = wd.promise.defer();
selectList.findElements(wd.By.tagName('option')).then(function (options) {
options.forEach(function(option) {
option.isSelected().then(function(isSelected) {
if (isSelected) {
d.fulfill(option);
}
});
});
});
return d.promise;
}
and use it like:
var sl = driver.findElement(wd.By.id('myId'));
getFirstSelected(sl).then(function(option) {
option.getText().then(function(text) {
console.log(text);
done();
});
});

Related

create dynamic selector in jquery plugin

I am creating a jquery plugin and my html is like
<select class="filter">
<option value="">Select Brand</option>
<option value="value1">value1</option>
<option value="value2">value2</option>
<option value="value3">value3</option>
</select>
my plugin code is like
(function( $ ) {
$.fn.filterCategory = function( options ) {
$(".filter option").each(function(){
var text = $(this).text();
$(this).attr("data-val",text);
});
// other code...
};
}( jQuery ));
and i call it like
$("select.filter").filterCategory();
the problem is if i want to call this plugin by id like
$("select#filter").filterCategory();
or by other class or id like
$("select.new_filter").filterCategory();
$("select#new_filter").filterCategory();
then the html will be like
<select id="filter">
<option>...</option>
</select>
then i will have to make changes on my plugin. It means it works only for .filter only. How can i make this code dynamic
$(".filter option").each(function(){ ... }
You need to use the this keyword to reference the elements that the plugin was intialised on:
(function($) {
$.fn.filterCategory = function(options) {
$(this).find('option').each(function() {
var text = $(this).text();
$(this).attr('data-val', text);
});
// other code...
};
}(jQuery));
You should also ensure you return a jQuery object from your plugin, assuming you wish to maintain the chaining pattern that jQuery uses. Finally note that you can simplify the each() loop to just call attr() only:
$(this).find('option').attr('data-val', function() {
return $(this).text();
});
This is of course assuming that you need to have the data-val attribute within the DOM. If not, I'd suggest using data() instead to use jQuery's internal cache.

jquery wait on change event

With jQuery I built a dynamic form; when the first select-input (named category) changes a second select-input (named subcategory) appears with sub-select items. And this works perfect now.
When a specific url parameter is given I want to auto-fill in the form after the page is ready.
$(document).ready(function () {
// initialize defaults
var category = getUrlParameter('category');
var subcategory = getUrlParameter('subcategory');
if (category && subcategory) {
$("select#category").val(category).change();
$("select#subcategory").val(subcategory).change();
}
});
Where getUrlParameter is a helper function I copied from Sameer Kazi.
Currently the first select-input is filled in, the second select-input is generated but not filled in. So actually, the change command needs to wait untill the first change command is ready.
You can using jQuery's .when() function, which handles an asynchronous function first before .then() is called.
$.when($("select#category").val(category).change()).then(function() {
$("select#subcategory").val(subcategory).change();
});
Here below is a small example using those two functions. I have added a console.log function to the select change event handlers so that you can follow what happens.
$("select#category").on('change', function() {
console.log('[on change] category changed');
$("select#subcategory").show();
});
$("select#subcategory").on('change', function() {
console.log('[on change] subcategory changed');
});
$(document).ready(function () {
// initialize defaults
var category = 1;
var subcategory = 2;
if (category && subcategory) {
$.when($("select#category").val(category).change()).then(function() {
console.log('[then] activating subcategory');
$("select#subcategory").val(subcategory).change();
});
}
});
select#subcategory {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<select id="category">
<option>...</option>
<option value="1">value 1</option>
<option value="2">value 2</option>
</select>
<select id="subcategory">
<option value="1">subvalue 1</option>
<option value="2">subvalue 2</option>
</select>
setTimeout(function(){
$("select#subcategory").val(subcategory).change();
},300);
maybe this?
Wait 300 ms till make other change. I guess its enough to have a first one done :)
$("select#category").on("load",function(){
$("select#subcategory").val(subcategory).change();
});
Maybe this will work if I got your problem
Maybe you can listen for event onChnage
$("select#category").on("change",function(){
$("select#subcategory").val(subcategory).change();
});
also instead of $("select#category").val(category).change();
try
$("select#category").val(category).trigger('change');

jQuery UI Combobox clear selection event

I have a combo box of which the HTML looks like this:
<select id="myCombo">
<option value=""></option> <!-- Not shown in jQuery UI Combobox because empty value -->
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</select>
I've attached the jQuery UI Combobox to I as such:
$("#myCombo").combobox({
select: function () {
if ($("#myCombo").val() == "") {
//DO SOMETHING
}
else {
//DO SOMETHING ELSE
}
}
});
When I select an option by either typing and/or selecting an option, the "DO SOMETHING ELSE" part is triggered like it should, so no issue there.
However, when I clear the selection (this selects the first option of my original combo) by deleting the text or clicking the "x", nothing get's triggered, so I can't execute the "DO SOMETHING" part of my function.
How can I trigger this part? Is there maybe another event that is triggered when clearing a selection?
I have searched and found lots of topic on selecting an item, but none on clearing/deselecting something(that were answered at least).
This can be accomplished with select2.
$("#myCombo").select2({
placeholder: "Select report type",
allowClear: true,
});
$("#myCombo")
.on("select2-selecting", function(e) {
log("selecting val=" + e.val + " choice=" + JSON.stringify(e.choice));
})
.on("select2-removed", function(e) {
log("removed");
})
After looking further into it I've found a way to trigger an event when clearing the text.
For this I had to edit the original custom.combobox widget JS, provided by the example on the jQuery UI site
In this js look for the _removeIfInvalid function and edit it like so
_removeIfInvalid: function (event, ui) {
// Selected an item, nothing to do
if (ui.item) {
return;
}
// Search for a match (case-insensitive)
var value = this.input.val(),
valueLowerCase = value.toLowerCase(),
valid = false;
this.element.children("option").each(function () {
if ($(this).text().toLowerCase() === valueLowerCase) {
this.selected = valid = true;
return false;
}
});
// Found a match, nothing to do
if (valid) {
//**ADD this piece of code**
this._trigger("change", event, {
item: null
});
return;
}
// Remove invalid value
this.input
.val("")
.attr("title", value + " didn't match any item")
.tooltip("open");
this.element.val("");
this._delay(function () {
this.input.tooltip("close").attr("title", "");
}, 2500);
this.input.data("ui-autocomplete").term = "";
//**ADD this piece of code**
this._trigger("change", event, {
item: null
});
}
The added piece of code (I know it's added 2 times, but the code would otherwise jump out of the function before it was ran), will add a change event to the combobox that can be used in your own script. This change event, I can use to run any code I want when the selection is cleared.
$("#myCombo").combobox({
select: function () {
//DO SOMETHING ON SELECT
//IMPORTANT: The other function can't be triggered before the combobox loses focus!!
$(".custom-combobox-input").blur();
},
change:function () {
//DO SOMETHING ON CLEAR
}
});
The removeIfInvalid function is always called by default when an option can't be found and by adding an event to the control when this function is invoked is invoked, we finally have a place to execute our algorithmns.
Hopefully, more people are helped with this small fix.
Empty value cannot be selected in the browser. Hence select() is never called on empty contents.
If you try this, it becomes a bit more clear:
<select id="myCombo">
<option value=""></option> <!-- Not shown in jQuery UI Combobox because empty value -->
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
<option value="4"></option>
<option value="5">Option 5</option>
</select>
I have slightly modified the javascript, note that I'm empty is never logged.
$("#myCombo").combobox({
select: function(event, ui) {
var idx = $("#myCombo").val();
var txt = $("#myCombo option:selected").text();
alert(idx);
if (idx === undefined) {
console.log("select: I'm empty");
} else {
console.log("select: " + idx + ": " + txt);
}
}
});
Interestingly, none of the other callbacks (search(), change(), response(), focus()) seems to function from autocomplete, so there is no way to detect a zero-ing out of the select.
I had spent what felt like a lot of time trying proposed solutions for this and those did not work for me, then I solved it myself.
Look for the _createAutocomplete function where you created your combobox instance.
Look for the chain of properties being set to this.input.
Then add
.on("focus",function(){
$(this).val('');
})
Anywhere in the chain.
So there are many examples on here of how to do this, but they are all (my opinion) cumbersome. You can clear the selection much easier by doing the following:
1) your _createAutocomplete: function() needs to have the following:
.attr( "title", "mytitle" )
Use any title that you want, or set another attribute (name, id etc)- I just happened to have a use for the title, so this worked for me.
2) whatever function you are running only needs the following to clear:
$("input[title='mytitle']").val('');
$("#combobox").val('');
(You have to clear both).
My need was a form with one of the fields being an autocomplete with minimum number of characters. There's an add entry button that submits, refreshes the entry list and resets the form line for additional entries. Much cleaner.

CasperJS fails to set select list values

I've got a select list that looks like this:
<select id="ListingType" name="Criteria/ListingTypeID" onchange="Search.toggleListingType(this);">
<option selected="selected" value="1">RH1</option>
<option value="2">BC4</option>
<option value="3">RR3</option>
<option value="4">RH2</option>
<option value="5">RE0</option>
</select>
I'm trying to set the value to 3. I've unsuccessfully tried 3 ways to do this.
1.
this.evaluate(function(value) {
document.querySelector('select#ListingType').value = value;
return true;
}, '3');
// insure the onChange JS is run
this.evaluate(function() {
var element = document.querySelector('select#ListingType');
var evt = document.createEvent('HTMLEvents');
evt.initEvent('change', false, true);
element.dispatchEvent(evt);
});
This appears to work (throws no errors), but the value doesn't get changed.
2.
this.fillSelectors('select#ListingType', {
'select[name="Criteria/ListingTypeID"]' : "3"
}, true);
This throws the error message: ``CasperError: Errors encountered while filling form: no field matching css selector "select[name="Criteria/ListingTypeID"]" in form''
What's wrong with my selector?
3.
// In desperation...
this.sendKeys('select#ListingType', 'RR3');
This also seems to work (no errors), but again the value is unchanged.
Just for completeness, I've tried sending the "change" event after each of the variations. I also dump out the attributes of the selected element after each, and it never shows the value as being set.
Note the page that this selector is on has a ton of JS code on it, and this <select> element is not wrapped in a <form> element.
I'm sure I'm making some really stupid mistake, but after hacking on this code for the last several days, I've not made any progress. What's a working way to do this?
You can use slimerJS (it opens firefox) to easily check a change in value.
this.fillSelectors('form#id_form', {
'select[name="Criteria/ListingTypeID"]' : "3"
}, false);
this.wait(6000,function(){
this.echo("i saw my new value");
});
And if you want to fill the option by its text and not value (i find it easier): use this function :
casper.fillSelectOptionByText = function (selectSelector,text){
this.evaluate(function(sel,setByText) {
$(sel + " > option").each(function() {
if($(this).text() === setByText) {
$(this).attr('selected', 'selected');
}
});
},selectSelector,text);
};
And in your test :
this.fillSelectOptionByText("select[name='year of birth']","1991");
The page has to have jquery of course, or you inject it.

jQuery prevent change for select

I want to prevent a select box from being changed if a certain condition applies. Doing this doesn't seem to work:
$('#my_select').bind('change', function(ev) {
if(my_condition)
{
ev.preventDefault();
return false;
}
});
I'm guessing this is because by this point the selected option has already changed.
What are other ways of doing this?
Try this:
http://jsfiddle.net/qk2Pc/
var my_condition = true;
var lastSel = $("#my_select option:selected");
$("#my_select").change(function(){
if(my_condition)
{
lastSel.prop("selected", true);
}
});
$("#my_select").click(function(){
lastSel = $("#my_select option:selected");
});
In the event someone needs a generic version of mattsven's answer (as I did), here it is:
$('select').each(function() {
$(this).data('lastSelected', $(this).find('option:selected'));
});
$('select').change(function() {
if(my_condition) {
$(this).data('lastSelected').attr('selected', true);
}
});
$('select').click(function() {
$(this).data('lastSelected', $(this).find('option:selected'));
});
If you simply want to prevent interaction with the select altogether when my_condition is true, you could always just capture the mousedown event and do your event prevent there:
var my_condition = true;
$("#my_select").mousedown(function(e){
if(my_condition)
{
e.preventDefault();
alert("Because my_condition is true, you cannot make this change.");
}
});
This will prevent any change event from ever occurring while my_condition is true.
Another option to consider is disabling it when you do not want it to be able to be changed and enabling it:
//When select should be disabled:
{
$('#my_select').attr('disabled', 'disabled');
}
//When select should not be disabled
{
$('#my_select').removeAttr('disabled');
}
Update since your comment (if I understand the functionality you want):
$("#dropdown").change(function()
{
var answer = confirm("Are you sure you want to change your selection?")
{
if(answer)
{
//Update dropdown (Perform update logic)
}
else
{
//Allow Change (Do nothing - allow change)
}
}
});
Demo
None of the answers worked well for me. The easy solution in my case was:
$("#selectToNotAllow").focus(function(e) {
$("#someOtherTextfield").focus();
});
This accomplishes clicking or tabbing to the select drop down and simply moves the focus to a different field (a nearby text input that was set to readonly) when attempting to focus on the select. May sound like silly trickery, but very effective.
You can do this without jquery...
<select onchange="event.target.selectedIndex = 0">
...
</select>
or you can do a function to check your condition
<select onchange="check(event)">
...
</select>
<script>
function check(e){
if (my_condition){
event.target.selectedIndex = 0;
}
}
</script>
I was looking for "javascript prevent select change" on Google and this question comes at first result. At the end my solution was:
const $select = document.querySelector("#your_select_id");
let lastSelectedIndex = $select.selectedIndex;
// We save the last selected index on click
$select.addEventListener("click", function () {
lastSelectedIndex = $select.selectedIndex;
});
// And then, in the change, we select it if the user does not confirm
$select.addEventListener("change", function (e) {
if (!confirm("Some question or action")) {
$select.selectedIndex = lastSelectedIndex;
return;
}
// Here do whatever you want; the user has clicked "Yes" on the confirm
// ...
});
I hope it helps to someone who is looking for this and does not have jQuery :)
You might need to use the ".live" option in jQuery since the behavior will be evaluated in real-time based on the condition you've set.
$('#my_select').live('change', function(ev) {
if(my_condition)
{
ev.preventDefault();
return false;
}
});
Implement custom readonly like eventHandler
<select id='country' data-changeable=false>
<option selected value="INDIA">India</option>
<option value="USA">United States</option>
<option value="UK">United Kingdom</option>
</select>
<script>
var lastSelected = $("#country option:selected");
$("#country").on("change", function() {
if(!$(this).data(changeable)) {
lastSelected.attr("selected", true);
}
});
$("#country").on("click", function() {
lastSelected = $("#country option:selected");
});
</script>
Demo : https://jsfiddle.net/0mvajuay/8/
This was the ONLY thing that worked for me (on Chrome Version 54.0.2840.27):
$('select').each(function() {
$(this).data('lastSelectedIndex', this.selectedIndex);
});
$('select').click(function() {
$(this).data('lastSelectedIndex', this.selectedIndex);
});
$('select[class*="select-with-confirm"]').change(function() {
if (!confirm("Do you really want to change?")) {
this.selectedIndex = $(this).data('lastSelectedIndex');
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<select id='fruits' class="select-with-confirm">
<option selected value="apples">Apples</option>
<option value="bananas">Bananas</option>
<option value="melons">Melons</option>
</select>
<select id='people'>
<option selected value="john">John</option>
<option value="jack">Jack</option>
<option value="jane">Jane</option>
</select>
This worked for me, no need to keep a lastSelected if you know the optionIndex to select.
var optionIndex = ...
$(this)[0].options[optionIndex].selected = true;
$('#my_select').bind('mousedown', function (event) {
event.preventDefault();
event.stopImmediatePropagation();
});
if anybody still interested, this solved the problem, using jQuery 3.3.1
jQuery('.class').each(function(i,v){
jQuery(v).data('lastSelected', jQuery(v).find('option:selected').val());
jQuery(v).on('change', function(){
if(!confirm('Are you sure?'))
{
var self = jQuery(this);
jQuery(this).find('option').each(function(key, value){
if(parseInt(jQuery(value).val()) === parseInt(self.data('lastSelected')))
{
jQuery(this).prop('selected', 'selected');
}
});
}
jQuery(v).data('lastSelected', jQuery(v).find('option:selected').val());
});
});
None of the other answers worked for me, here is what eventually did.
I had to track the previous selected value of the select element and store it in the data-* attribute. Then I had to use the val() method for the select box that JQuery provides. Also, I had to make sure I was using the value attribute in my options when I populated the select box.
<body>
<select id="sel">
<option value="Apple">Apple</option> <!-- Must use the value attribute on the options in order for this to work. -->
<option value="Bannana">Bannana</option>
<option value="Cherry">Cherry</option>
</select>
</body>
<script src="https://code.jquery.com/jquery-3.5.1.js" type="text/javascript" language="javascript"></script>
<script>
$(document).ready()
{
//
// Register the necessary events.
$("#sel").on("click", sel_TrackLastChange);
$("#sel").on("keydown", sel_TrackLastChange);
$("#sel").on("change", sel_Change);
$("#sel").data("lastSelected", $("#sel").val());
}
//
// Track the previously selected value when the user either clicks on or uses the keyboard to change
// the option in the select box. Store it in the select box's data-* attribute.
function sel_TrackLastChange()
{
$("#sel").data("lastSelected", $("#sel").val());
}
//
// When the option changes on the select box, ask the user if they want to change it.
function sel_Change()
{
if(!confirm("Are you sure?"))
{
//
// If the user does not want to change the selection then use JQuery's .val() method to change
// the selection back to what it was previously.
$("#sel").val($("#sel").data("lastSelected"));
}
}
</script>
I hope this can help someone else who has the same problem as I did.

Categories

Resources