I have this input that when focused opens up a select.
The behavior I want could be achieved with a simple select but I wish to further add functionalities that a simple select does not allow. Simply put when you have the 'box' focused, you can view the options, when you don't you cannot.
I tried to implement this logic using a state that checks if the parent div where the select and the input is focused.
Clicking the input displays the options as expected, however when I try to select an option from the list, before I can select it, the select gets hidden and I can't choose anything.
Demo
Is there another approach for this?
The trouble is that another focusable element (the select) receives focus. You can't avoid that, but you can change how you react to that: you need to check whether the element causing the blur is a descendant of your div. If it is, you don't need to close your select box.
Change your onBlur handler to this:
const onBlur = ({ currentTarget, relatedTarget }) => {
if (!currentTarget.contains(relatedTarget)) {
setMenuActive(false);
}
};
Forked demo. Note that I also changed your state to a simple boolean instead of an object.
Related
How can I select all the elements that a user can press tab to navigate to?
Do I have to hard code for input, textarea, a, button, and so on, or is there a way to identify them via the browser's default?
What I am ultimately trying to accomplish is here: Default tabbing behavior puts links behind sticky header
You could write a function that queries for every element that is focusable and filter out those that are disabled, because that disables the focus.
const getKeyboardFocusableElements = (element = document) =>
Array.from(element.querySelectorAll(
'a, button, input, textarea, select, details,[tabindex]:not([tabindex="-1"])'
)).filter(el => !el.hasAttribute('disabled'));
Can you try using a selector like document.querySelectorAll("[tab-index]")?
As having tab-index attribute as a non negative value assures that it is focus by the tab
I am trying to automate something here. The website on which I run the Javascript,
When I click on an <option> element inside <select>. The website changes the content of another <select> below it. This only happens when I manually select an option.
But when I select an option using Javascript like this: No content gets changed:
document.getElementById("Category").selectedIndex = 8;
Try
let options= document.querySelectorAll('select option')
You can access the innerHTML property of first option using options[0].innerHTML
Add onChange event to you select element using
document.querySelector('select').addEventListener('change',()=>{
alert('Changed')
})
Hope it helps
Edit - You might also want to check the current selected option and want to make changes accordingly. You can check the current selected option by
document.querySelector('select').value //Returns the value of selected option
Disclaimer: There is no reproducible example, so this is my best guess.
The website you are talking about probably captures the click events and uses that to alter the selects. For example,
$("select > option").click(function(){
//do something
...
});
When you change the selectedIndex, yes, you are selecting a new option, but you are not sending the click event, so the above function is not called. You will need to inspect their source/provide more details for more clarity.
I want to be able to detect when a user has selected an option from the dropdown box on an input element linked to a datalist so that I may use that action to trigger another.
For some reason, clicking on the datalist dropdown box does not fire a click event. So using that is totally out of the question.
Using the change event doesn't work for two reasons. Chrome's change event is pretty nice, it will fire when someone selects an option, however, in Firefox the event is not fired until the input has lost focus. You may think that you could at least use the change event for Chrome, but alas, there is another major issue—if you type in the full text of what you're selecting and then click an option, the change event is never fired, because nothing changed :(
Finally, we come to the very ugly but seemingly only option: comparing the current value of the input element to the options in the datalist with an input event. This has many issues.
It doesn't work if one option is a prefix of another (e.g. foo, foobar). The code will run prematurely if the user types foo before selecting foobar.
Assuming none of your options are prefixes of each other, your code will still run if the user types out the whole text of something in the datalist, as opposed to waiting for them to select it.
It has to loop through every single option in the datalist every time you
type a character.
You could try to mitigate these issues using setTimeout to detect when someone has stopped typing, but that still wouldn't be able to achieve the desired behaviour.
I can't believe there's not a simple event that deals with this, but I know there must be a way.
Indeed, there is a way! Our hero is the Invisible Separator (U+2063). It is a character that adds no visible effect to a string, it merely exists. Copy and paste this foo and check its length. You will see it equals 4!
We can append this character to the end of every option in the datalist. Then we can set up a listener for an input event, and check if the value of the input element ends in U+2063. If it does, you know the user has selected an option. You then should change the value of the input element to get rid of the U+2063 (unless the selection is going to trigger something that resets the value anyway).
This overcomes every issue you've stated because the user can't actually type in anything that matches something in the datalist, an option must be selected for anything to happen.
document.querySelector('input').addEventListener('input', function(){
if (this.value.slice(-1) === '\u2063') {
this.value = this.value.slice(0, -1);
let div = document.querySelector('div');
div.textContent = `you selected: ${this.value}`
div.classList.toggle('red'); //so you can see when this is called even if the text doesn't change
}
});
body {
display: flex;
}
div {
margin-left: 1em;
}
.red {
color: red;
}
<input list='test'>
<datalist id='test'>
<option>foo</option>
<option>bar</option>
<option>foobar</option>
</datalist>
<div></div>
I have a table with data, and when I click on a cell in a certain column, I want it to change into a select dropdown for the user to choose a category for that row (which will be written to the database by AJAX but that'll come later).
I've done something similar before with text boxes using this, which works great, but I'm not sure if I'm modifying it correctly.
I've created a JSFiddle which shows the problem I'm having. I click on the text and it turns into a select element as expected, but when I click on that to choose an option, the dropdown doesn't stay open and I can't select anything. Debugging has shown me that when I click the dropdown, it runs the $("td.ChooseType").click() routine again so I've tried to suppress that by removing the class then adding it back on on selection, but that hasn't solved it. On the rare occasion that the dropdown stays open, I am unable to select anything by either mouse or keyboard.
All of the users will be on IE8 unfortunately, so I need it to be compatible with that.
Thanks!
You need to use event delegation, as otherwise that click event is always bound to that td - regardless of whether its class changes.
Simply change:
$("td.ChooseType").click(function() {
To:
$("table").on('click', '.ChooseType', function () {
JSFiddle demo.
Purely as an alternative to the accepted answer, you can remove an attached handler with unbind. So instead of adding and removing the class, you could unbind and rebind your handler. Only requirement is that the function can't be in-line, but has to be declared separately.
example: http://jsbin.com/qiqunici/1/edit
var handler = function () {
$(this).unbind('click', handler); //unbind the clicked element only
//create and change the element
//inside the select-change event, instead of addClass, re-attach:
{
//$(this).parent().addClass("ChooseType").text(selected).find('select').remove();
$(this).parent().click(handler).text(selected).find('select').remove();
}
};
$("td.ChooseType").click(handler);
I am working on a plugin for jQuery that will essentially style certain elements. Like jqTransform I could just replace the element but I chose to position the real element off screen and make a new element thats styled. This allows triggering the actual events of the real element. Also, if an onclick handler or onchange handler for a textbox is there, jqTransform will not include that whereas this way, it will include it.
Here is my problem. Say a user has a button. Later on in the app the user decides to change the value of the button. It will change the original button's value but not the styled button. Is there any way I can connect the elements so that if the original button's value is changed the styled button's value is changed as well?
you can catch any property change on object using onpropertychanged event.
$("#buttonid").bind('propertychange', function(e) {
if (e.originalEvent.propertyName == "value") {
// value is changed, handle it here
}
});