In my application, I have a list of <select> controls. Change of selection in each of these controls can be made only after user confirmation. So I deal with it as follows:
<select onchange="changeSelect(this)" onmousedown="clickSelect(this)">
<option value="1" selected="true">A</option> // here is 'default' value 1
<option value="2" >B</option>
</select>
<select onchange="changeSelect(this)" onmousedown="clickSelect(this)">
<option value="1" >A</option>
<option value="2" selected="true">B</option> // here is 'default' value 2
</select>
// more select controls with arbitrary 'default' values, they can be added and removed dynamically
When the user clicks the select to choose another option, the clickSelect(this) method saves the currently chosen (old) selection:
function clickSelect(select) {
globalScope.previouslySelected = $(select).val();
}
And now, if the user really tries to change the selection, I ask the user for confirmation, and if she does not confirm, I use the saved value to restore the previous state:
function changeSelect(select) {
var response = confirm("Current changes to the threat log will be lost. Continue anyway?");
if (response == false) {
// change it back
$(select).val(globalScope.previouslySelected);
return;
}
}
}
Now, on IE, Chrome and Opera it works just fine. However, when I use Firefox, it does not work. THe reason is that FF calls the onmousedown handler twice during the selection -- first time when the user clicks to roll down the select control, and second time when she selects a new selection. Therefore the last remembered value of globalScope.previouslySelected will become the new selected value and not really the old one, as in other browsers.
I know using global variables is considered bad practice. This is not the issue here. Using any other storage will not make it work.
My only idea right now is to write a Firefox specific piece of code in the clickSelect(select) handler and ignore the second notification. However, for some reason the following code I found on stack overflow does not work:
if($.browser.mozilla) {
console.log("Olalala");
}
E.g., in Chrome it throws following exception:
Uncaught TypeError: Cannot read property 'mozilla' of undefined
So how can I make it work so that ti would only run in FF? Or if any of you guys have a better solution to the problem of confirmation, I would gladly hear it. Just remember, there is a dynamic set of select controls, not just a single one. It has to work for all of them. Plus they can be added or removed dynamically, so in a single page request their number can vary. User loads page with a single select control, presses a '+' button and adds another select control, etc., but the solution has to work uniformly with all the controls.
Is there a specific reason you're saving the currently selected option using the onmousedown listener? You can just initialize your global variable to hold the default option, and update it whenever a new selection is made.
Something like this:
HTML
<select id="mySelect" onchange="changeSelect(this)">
<option value="1" >A</option>
<option value="2" >B</option>
</select>
JS
var globalScope = {previouslySelected: $("#mySelect").val()};
function changeSelect(select) {
var response = confirm("Current changes to the threat log will be lost. Continue anyway?");
if (response == false) {
// change it back
$(select).val(globalScope.previouslySelected);
return;
}
globalScope.previouslySelected = $(select).val();
}
EDIT
I noticed that on Firefox, the second mousedown event's target is the option, so you can use the following code to ignore the event in this case:
$("select").mousedown(function(e) {
if ($(e.target).is('option'))
return;
globalScope.previouslySelected = $(this).val();
});
With this code you can remove the onmousedown registration from your DOM.
Related
for example - a profile page - users want to change some values in select tags
you cannot say - <select id='lorem' value='ipsum'>
you must go to javascript - $('#lorem').val('ipsum')
is this really true ?
is there any better practice - if you have a lot of select tags ?
btw - why html developers made this like this - there must be some strong reason
Your "question" really embodies multiple questions, hence I divided my answer into multiple sections.
<select> and <option>
To select an <option> of <select> initially, add the selected-attribute to <option>. Selecting an <option> makes the <select> have the value of the selected option.
var select = document.querySelector('select');
var code = document.querySelector('code');
select.addEventListener('change', () => code.textContent = select.value);
code.textContent = select.value;
body {display:flex;align-items:center;gap:0.5rem}
code {padding:0 0.2rem;background:lightgray}
<select>
<option value="first">First option</option>
<option value="second" selected>Second option, initially selected</option>
<option value="third">Third option</option>
</select>
<span>Select's 'value': <code></code></span>
Giving <select> a value-attribute will be ignored by the browser-engine, since HTML5 doesn't specify the <select>-tag to have a value-attribute.
Note: The HTML-attribute value and the JavaScript property .value are not necessarily related to each other.
See this example for reference:
var select = document.querySelector('select');
var code = document.querySelector('span > code');
select.addEventListener('change', evt => {
code.textContent = evt.target.value;
});
code.textContent = select.value;
body {display:flex;flex-flow:column;align-items:flex-start;gap:0.5rem}
code {padding:0 0.1rem;background:lightgray}
<div>
<code><select></code> has the attribute <code>value="from-select"</code>.
</div>
<select value="from-select">
<option value="from-opt1">Opt1</option>
<option value="from-opt2">Opt2</option>
</select>
<span>Select's 'value': <code></code></span>
To change the value of an <option> dynamically, you would have to use JavaScript.
var input = document.querySelector('input');
var option = document.querySelector('option');
var code = document.querySelector('code');
// Ignore; update 'pre.textContent' when changing 'value'
option.setAttribute = function(name, value) {
HTMLElement.prototype.setAttribute.call(this, name, value);
if (name === 'value') code.textContent = value;
};
// How to change the 'value'-attribute using JS
document.querySelector('button').addEventListener('click', () => {
option.setAttribute('value', input.value);
input.value = '';
});
body {display:flex;flex-flow:column;align-items:flex-start;gap:0.5rem}
code {padding:0.1rem 0.2rem;background:lightgray}
<span>
<input>
<button>Change value of 'Option'</button>
</span>
<select>
<option value="initial-value">Option</option>
</select>
<span>Option's value: <code>initial-value</code></span>
Regarding "why html developers made this like this"
Paraphrased: Why can't HTML change its markup on its own?
Reason is, HTML was initially created for scientific documents served via the internet.
Since the internet is by nature a web of interconnected, potentially completely different devices, HTML had to be as unspecific and "device-agnostic" as possible, to allow rendering it on virtually any device that has a display.
For that reason, any further resource (e.g. stylesheets, scripts, images) should only be optional for the rendering of the HTML, as the HTML should be renderable "as is".
HTML should also be backwards-compatible, meaning that future changes to the HTML specification should in no way hinder the serving of the HTML. That means, any unknown... thing, basically, will be ignored by the rendering engine (todays browsers) so that all the then-existing features can still work as intended.
Initially, HTML was not intended to have any further functionality than serving static content. However, over the last few decades, the internet has had its "boom" of technological advancement, and now, JavaScript is de facto part of any browser-engine and website, enabling them to have advanced functionality and be interactive.
The internet is in a state of ongoing development, and always will be. Neglecting the new state of the internet and its specification (e.g. for the browser-engines, etc.) would mean that one is not moving with the times, withholding new, modern and current information and knowledge from oneself, essentially leaving one behind.
"Tradition" is difficult to find in any technical field, as new advancements and discoveries are simply spoken part of them.
To learn more about the roots of the internet (even though the video is thematically about CSS) I recommend watching MDN's video about why CSS is weird (and how HTML came to be).
Regarding your "question" ...
as you have further explained in the comments:
I have some data about each user in database. Those data are from
registration form. That form has select tags. Now, user want to change
some data on his profile. Tha data need to be presented on select
tags. It would be normal to do that using value attribute, like in any
other input - without javascript
You could do it two ways:
Create a <form> with one field for each user-data. With this, you have two more options:
Prepare the fields to hold their current data. The user can then submit his changes easily.
Leave the fields empty. If a field is empty after submission, ignore the proposed "change".
Let the user select what data to change one at a time. Again, two more options to achieve this:
Fetch the current data for that field. The user can change it and then submit it easily, again.
Let the user enter the new data anew, then submit it.
Here is a simple example of how the HTML for option 2.2 could look when implemented:
Note: I changed the <button>'s default type from type="submit" to type="button" for demonstration-purposes.
var select = document.querySelector('select');
var input = document.querySelector('input');
document.querySelector('button').addEventListener('click', () => {
console.log(`Would change user-data '${select.value}' to the value '${input.value}'.`);
});
<form>
<select>
<option value="name">Name</option>
<option value="address">Address</option>
<option value="email">E-Mail</option>
</select>
<input>
<button type="button">Submit</button>
</form>
I'm trying to fill a form using Selenium, but the form has a disabled field.
Disabled field
The field is only editable when I modify the field above it.
Open field
When I set the value directly using the code below, the field is not open for editing
js.executeScript("document.getElementById('field_id').value='" + brand + "'");
Example
I tried to simulate the click in the field, press the tab key, press the enter key, but none had any effect.
Is there any way for me to trigger the same event that the user is performing on the screen to release the field through selenium or javascript?
In the HTML code, the options are not listed, so the options are loaded from a javascript function that is executed after filling the first field
Options
Because I really liked it I'll copy Tschallackas Intro:
Your test is flawed. You are not following user behaviour.
Sadly I totally disagree with the rest of the answer :(
I would like to ask WHY are you trying to use JavaScript?
Is this something a real User would do? I really doubt it!
The crucial thing with End2End-Tests is to simulate your User behaviour as close as possible. Therefore I would suggest to use the Webdriver to do things like that in your Seleniumtest.
Select dropdown = new Select(webdriver.findElement(By.id("field_id")));
dropdown.selectByVisibleText("ONESOURCE");
(Assuming you are using Java by the tag on your question)
Your test is flawed. You are not following user behaviour.
You are doing:
js.executeScript("document.getElementById('field_id').value='" + brand + "'");
Which tries to change a value on a dropdown. This doesn't work because dropdowns work via a selectedIndex, which you can use to get the correct value from the options collection on the dropdown element.
Also, when a user changes a value, a change event is triggered, which notifies other scripts that listen to that event that something has changed. You need to emulate this too to trigger your change script.
js.executeScript("let select = document.getElementById('field_id');"+
"select.selectedIndex = 1;/* change this to the value corresponding to the correct index of the value you wish to test. */"+
"select.dispatchEvent(new Event('change'));");
See the example below for how the javascript should work.
document.getElementById('field_id').addEventListener('change', (e) => {
if(e.target.options[e.target.selectedIndex].value > 1) {
document.getElementById('the_disabled').disabled = false;
}
else {
document.getElementById('the_disabled').disabled = true;
}
});
document.getElementById('trigger').addEventListener('click',() => {
let select = document.getElementById('field_id');
select.selectedIndex = 1;// change this to the value corresponding to the correct index of the value you wish to test.
select.dispatchEvent(new Event('change'));
});
<select id="field_id">
<option value="1">--none--</option>
<option value="2">COMPANY A</option>
<option value="3">COMPANY B</option>
</select>
<BR/>
<select id="the_disabled" disabled="disabled">
<option value="0">--none--</option>
<option value="1">SELECT A</option>
<option value="2">Select B</option>
</select>
<BR/>
<button id="trigger">Trigger selenium emulation</button>
I want to create two selections on my webpage which will be connected to each other so that both selections will always be the same. Let me explain in a mock example:
I have a webpage with two selects:
<select class="myselector">
<option value='1'>1
<br>
</option>
<option value='2'>2
<br>
</option>
</select>
<select class="myselector">
<option value='1'>1
<br>
</option>
<option value='2'>2
<br>
</option>
</select>
Note: in reality, I have more than just the two selects and I want them all to be connected.
Now, I want the webpage to automatically set the second selector to the value of the first if the first is changed. I also want to perform some other things on the select that has changed, so I also want the change event to trigger on the other selector. My first thought was simply to do this:
function valChange() {
myfun();
$('.myselector').val($(this).val());
}
$('.myselector').on('change', valChange);
This does what I need in that it changes the selected values so they match
This does not trigger the change event in the other select, so myfun runs only once
My next thought was to add $('.myselector').change(); to the end of the valChange function, but this (naturally) causes an infinite loop of change events.
My question is, therefore:
How can I trigger the change event in only the elements that have been
changed automatically (not by a user)?
I think it could be done by having both the change and the click event, but that just seems wrong and ugly to me.
EDIT: I found a way to solve my problem through the use of jquery selectors. If there exists a more elegant way of solving the problem, I will still be happy to see it though.
function valChange() {
myfun()
var others = $('.myselector[value!=' + $(this).val() + ']');
others.val($(this).val());
others.change();
}
$('.myselector').on('change', valChange);
JSfiddle for my solution:
http://jsfiddle.net/5xum/svjbdxgs/
you can use each method of jquery
function valChange() {
var _this = $(this)
$('.myselector').each(function(){
myFun();
$(this).val(_this.val());
});
}
$('.myselector').on('change', valChange);
here is fiddle for the same fiddle link
The select values are confusing me. When the user edits a row in my app I clone a tag with jquery (called empty-X) and put it on a modal window so that the user can edit the values. At the same time I get a json object (data) from server and fill in the current fields on the modal window as it stands in the database :
empty_X.find('#id_deals-1-currency').val(data[0].fields['currency']);
Now when the modal shows, the user can see how the correct currency is selected in the dropdown.
Yet when I check the HTML for this element with Firebug, I get a different picture, nothing seems selected.
<select id="id_deals-1-currency" name="deals-1-currency">
<option selected="selected" value="">---------</option>
<option value="1">USD - $</option>
<option value="2">EUR - €</option>
<option value="3">GBP - £</option>
</select>
And yet when I send the form to the server, there are no validation errors and the currency is the same value as it was previously set through val(). Life is good.
While this works by itself, there is a problem. What if the user wants to get back to the edit mode and verify the currency once more before saving it?
In this case I can't load the values from the database any more. His previous local changes matter now. I have to clone the current record with currency inside back in the modal window, so the user can see what he had changed previously and verify it. The problem is now the user doesn't see the currency he had changed in the previous step. In fact he would see an empty dropdown instead.
What are my options here? Is there a way to set the selected flag to the actual selection rather than using val()?
When cloning a <select>, the option with the 'selected' attribute becomes the current option in the cloned object - instead of the actual current object (as per value attribute).
To counter this, you can find the currently selected option from the value returned by val() and then apply the selected attribute to it prior to cloning it. This way you wont need to set the value after cloning.
Demo: http://jsfiddle.net/DqADq/
Code: (.x1 is the <select>)
// simple cloning
$('.x1:first').clone().appendTo('.out');
// setting selected attr before cloning
var v = $('.x1:first').val();
$('.x1:first option').removeAttr('selected'); // remove 'selected' from all options
$('.x1:first option').each(function() {
if($(this).attr('value') == v) {
$(this).attr('selected', true); // apply 'selected' to current option
}
});
$('.x1:first').clone().appendTo('.out');
I have a select, and I need to fire some js each time an option is selected, even if it is already selected.
Example:
<select id="select_one">
<option value="">Choose One...</option
<option value="one">One</option>
<option value="two">Two</option>
</select>
If someone selects "one" I want to fire functionOne(). But, while "one" is selected, if the user selects "one" again I want to fire functionOne() again. So, that means onChange won't work, since the selection isn't changing.
Anyone have any thoughts?
Contrary to some of the current answers, you don't actually get a click event on <option> in IE.
The only reliable way to detect the case of an option being clicked that was already selected is to put the onclick handler on the <select>. Naturally this will also detect any other clicks on the select element, so depending on what functionOne() is doing this may not be safe either.
You might be better off with something that looks like a select but isn't, such as a pop-up div with buttons on it. What is it you are trying to do? If you're trying to do a “jump menu” where selecting an option navigates to a new page: don't, it's an old and discredited mechanism with serious usability problems.
add onClick to the <option> tag
after firing the onSelect, change the select value to "Add Another"?
eg. forst option has an id of #selectBox, value of '' and a html of "Select One".
onChange, retrieve the value $('#selectBox').val(); and turn around after your actions and go $('#selectBox').val('').html('Select Another');
Add an onclick even to each option tag. Inside the function do:
function() {
var timesClicked = $(this).attr('times-clicked') || 0;
$(this).attr('times-clicked', ++timesClicked);
if (timesClicked == 1) {
functionONe();
}
else if (timesClicked == 2) {
functionTwo(); // and so on
}
}