I have a search component in Vue.js. When you type into the text input, a list of search results is fetched from the server, then displayed in a list beneath the search field. When you click one of the results, the submit event fires.
However, now I tried adding a blur event to the text input, which should hide the list of results when the user clicks away from the input. This works fine, except for one crucial situation - clicking on a result no longer fires the submit event.
I understand why this is - the blur event apparently fires before the click event, and hides the results list before the click can be registered on one of the results. My question is, how do I get around this? I need the results list to close when clicking outside the text input, but I obviously also need the submit method to function.
Here is the component in full:
<template>
<div class="search basic-search">
<input type="text" v-model="search_string" v-on:keyup="search" v-on:focus="activate" v-on:blur="inactivate" class="form-control search" placeholder="Search stocks" />
<div :class="['search-results', active === true ? 'active' : '']">
<div class="search-result" v-for="result in search_results" v-on:click="submit(result.id)">
{{ result.name }} ({{ result.ticker }})
</div>
</div>
</div>
</template>
<script>
export default {
data: function() {
return {
search_string : '',
search_results : [],
active : false
};
},
methods : {
search : function() {
const axios_data = {
_token : $('meta[name="csrf-token"]').attr('content'),
str : this.search_string
};
axios.post('/stock-search', axios_data).then(response => {
if(response.data.success){
this.search_results = response.data.stocks;
this.active = true;
}
});
},
activate : function() {
if(this.search_string !== '')
this.active = true;
},
inactivate : function() {
this.active = false;
},
submit : function(stock_id) {
document.location = "/graphs/" + stock_id;
}
}
}
</script>
You could delay the hiding of the box until click fires
inactivate : function() {
setTimeout( () => this.active = false, 100)
},
You may also try to use mousedown instead of click
<div class="search-result" v-for="result in search_results" v-on:mousedown="submit(result.id)">
I don't know if the order of the events is determined, but mousedown should be triggered before blur.
Related
<a-select
showSearch
placeholder="Select a person"
optionFilterProp="children"
style="width: 200px"
:open="open"
#mouseenter="openSelect"
#mouseleave="closeSelect"
>
<a-select-option value="jack">Jack</a-select-option>
<a-select-option value="lucy">Lucy</a-select-option>
<a-select-option value="tom">Tom</a-select-option>
</a-select>
</template>
data() {
open: false,
}
methods: {
openSelect() {
this.open = true;
}
closeSelect() {
this.open = false;
}
}
how to close a ant-design-vue select option when the user is no longer using it? i've tried by using onBlur and onMouseLeave. and also i have tried with create a function onFocus() {this.open = true} and function for onBlur(){ this.open=false } but still not work
the mouseleaveevent will be triggered after pointer is not in the field but the option still not be able to choose
http://vue.ant-design.cn/components/select/#events
according to the doc of select component, there is a mouseleave event supported.
try using #mouseleave="foo" to set options invisible
I am trying to make it so that when I press enter OR if I lose focus from an element it triggers a function but when I press enter it then triggers the blur event also. So the function is called twice. It should only be called once.
<input v-on:blur="saveField('name')" keyup.enter="saveField('name')">
The problem is that my saveField() function hides the element, triggering also the blur event.
I guess the other issue is how not to have to write the same function call twice. (DRY).
You can use some kind of condition to check if the value needs to be updated. It seems complicated to avoid the two events from being triggered:
<div id="app">
<input v-model="inputValue" type="text" #blur="save()" #keyup.enter="save()">
<div v-for="saving in savings">
{{ saving }}
</div>
</div>
new Vue({
el: '#app',
data: {
inputValue: '',
savedValue: '',
savings: []
},
methods: {
save () {
if (this.inputValue !== this.savedValue) {
this.savings.push('Saving value ' + this.inputValue)
this.savedValue = this.inputValue
}
}
}
})
Here is a working JsFiddle: https://jsfiddle.net/L6kfz48m/
I have a very simple jQuery UI spinner as follows:
<input value="2" class="form-control ui-spinner-input" id="spinner" aria-valuemin="2" aria-valuemax="24" aria-valuenow="2" autocomplete="off" role="spinbutton" type="text">
Using jQuery I set the above text box readonly true/false. The readonly and value is set based on the checkbox a user selects and that function looks like
function checkBoxes() {
var $targetCheckBoxes = $("#BoxFailure,#InstallationFailure");
$targetCheckBoxes.change(function () {
var isChecked = this.checked;
var currentElement = this;
var $radioButton = $('.usage-failure-type-radio');
$targetCheckBoxes.filter(function () {
return this.id !== currentElement.id;
}).prop('disabled', isChecked);
$('#spinner').val(isChecked ? this.value : '').prop('readonly', isChecked);
$radioButton.first().prop('checked', isChecked);
$radioButton.not(':checked').toggle(!isChecked).parent('label').toggle(!isChecked);
$('.usage-before-failure > div > span.ui-spinner > a').toggle(!isChecked);
});
}
Now what I'm trying to achieve is when the #spinner input is readonly and if the user presses the back space I want to prevent the default behaviour e.g. do navigate away from the page. For this I thought I'd do the following:
$('.prevent-default').keydown(function (e) {
e.preventDefault();
});
Which works fine if the input has the class prevent-default on page load. However, if I add it in my checkBoxes function in the following line
$('#spinner').val(isChecked ? this.value : '').prop('readonly', isChecked).toggleClass('prevent-default')
Then I press the backspace it ignores e.prevenDefault();
But if I do
$('#spinner').val(isChecked ? this.value : '').prop('readonly', isChecked).keydown(function (e) { e.preventDefault(); });
Then it works absolutely fine.
Can someone tell me why this is happening please.
The reason I want to use a separate function with a class name is because I have various inputs which get set to read only based on different check/radio values.
Can someone tell me why this is happening please
This is because of the DOM parser and the timing when JavaScript is executed.
If you already have an element with a class prevent-default in your DOM before JS is executed, then the JavaScript will recognise and handle it correctly. If you instead add the class afterwards with JS, then you have to re-initialise the keydown-event again to make it work.
To re-initialise you will need something like this:
function checkBoxes() {
var $targetCheckBoxes = $("#BoxFailure,#InstallationFailure");
$targetCheckBoxes.change(function () {
...
$('#spinner').val(isChecked ? this.value : '').prop('readonly', isChecked).toggleClass('prevent-default');
// assign new keydown events
handleKeyDown();
...
});
}
function handleKeyDown() {
// release all keydown events
$('#spinner').off( "keydown", "**" );
$('.prevent-default').keydown(function (e) {
e.preventDefault();
// do more stuff...
});
}
I'm trying to update an input value on conditional blur in Knockout - basically I want the element not to trigger a value update when a specific element triggers the blur. I know I can watch mousedown on every element on the document and determine what was last clicked, but seems a bit excessive. Any other work around anyone can think of?
<input class="edit" data-bind="value: title, valueUpdate: 'afterkeydown', enterKey: $root.stopEditing, selected: editing, event: { blur: $root.checkEditing }">
The code I was trying to achieve to pull this off isn't working with document.activeElement.
self.checkEditing = function( item, event ) {
if (document.activeElement == $('a.cancel')) {
// revert to previous title, aka cancel the editing
item.title(item.previousTitle);
item.editing( false );
} else {
// this will update value with whatever was typed right before the blur
item.editing( false );
if ( !item.title().trim() ) {
self.remove( item );
}
}
};
Looks like to appropriately capture the element that has triggered the blur, setTimeout has to be used. After the blur is processed, setTimeout ensures that the focused element has become available.
For example:
The input:
<input class="edit" data-bind="value: title, valueUpdate: 'afterkeydown', selected: editing, event: { blur: $root.checkEditing, click: $root.editItem }">
The method that checks the active element after blur:
self.checkEditing = function( item, event ) {
setTimeout(function(){
console.log("The active element is: " + document.activeElement)
// check if the user selected cancel
if ($(document.activeElement).is("a.cancel")) {
// revert to previous title
item.title(item.previousTitle);
}
});
item.editing( false );
if ( !item.title().trim() ) {
self.remove( item );
}
};
Full fiddle demonstrating this is here: http://jsfiddle.net/hUA9v/5/
I have a select dropdown and if a user clicks 'outside' of that select dropdown, I want it to disappear. This is what I currently have:
$(this).html($("<select/>", {
id: 'sel',
change: function() {
selectdone(this, title_id, status_type);
},
blur: function() {
selectdone(this, title_id, status_type);
},
In the above, if I click outside of the dropdown, nothing happens. The only time the function is firing is if I change the value of the select dropdown.
How would I accomplish the above, such that when a user clicks anywhere on the document outside of the select dropdown, it fires this function?
I don't think this is possible. As pointed out in another answer, it appears that the active <select> is prohibiting other input from being accepted.
See my updated fiddle. Notice that when you click the background you get the alert test when the select isn't expanded. When it is expanded and you click off, the alert doesn't fire. This appears to be the default behavior of the browser. It appears to be ignoring all other inputs (mouse movements included) while the select is activated.
I was, however, able to get your event to fire for any selected element by setting the selectedIndex to -1. This way any valid option will result in a change.
Example
$(function(){
var title_id = '', status_type = '';
$('body').html(
$("<select/>", {
id: 'sel',
change: function() {
selectdone(this, title_id, status_type);
},
blur: function() {
selectdone(this, title_id, status_type);
}
})
.append($('<option />', { 'text':'one'}))
.append($('<option />', { 'text':'two'}))
);
$('#sel').prop('selectedIndex', -1);
});
function selectdone(element, titleid, statustype){
$(element).hide().prop('selectedIndex', -1);
}