I would like to trigger the input box to focus (and thus keyboard to pop up) when a Vue component appears.
It does not to work on iOS.
I tried using Vue's example directive (here), and HTML5 autoFocus but neither worked.
I created this example in a sandbox (https://codesandbox.io/s/lw321wqkm).
FWIW, I do not believe it is a JS limitation, as I've seen example work (such as React Native Web using autoFocus- see example)
Parent component
<template>
<div>
<TextComp v-if='showText' />
<button #click='showText = !showText'> Show/hide input </button>
</div>
</template>
...
Child component
<template>
<div>
<input ref='focusMe' type='text'/>
</div>
</template>
<script>
export default {
name: 'TextComp',
mounted () {
this.$refs.focusMe.focus()
}
}
</script>
I hope you must have found a solution by now.
But I just wanted to add this for other new users like me.
mounted () {
this.$refs.focusMe.focus() // sometime this doesn't work.
}
Try adding this instead.
this.$nextTick(() => this.$refs.focusMe.focus())
For more info check this
Edit: 14/06/2022
Prashant's answer also helped me understand the nextTick in more depth.
nextTick allows you to execute code after you have changed some data and Vue.js has updated the virtual DOM based on your data change, but before the browser has rendered that change on the page.
You can create a fake input field and focus it during the click event.
<template>
<div>
<TextComp v-if='showText' />
<button #click='onShowText'> Show/hide input </button>
<input type="text" ref="dummykeyboard" style="opacity:0">
</div>
</template>
<script>
export default {
methods:{
onShowText() {
this.showText = !this.showText;
this.$refs.dummykeyboard.focus();
}
}
}
</script>
I would suggest triggering a click event on that field instead of focus
this.showText = !this.showText;
this.$nextTick(function () {
this.$refs.dummykeyboard.click();
})
I would go with this solution and it works for me. Just add set timeout and put focus method inside it
<template>
<div>
<input ref='focusMe' type='text'/>
</div>
</template>
<script>
export default {
name: 'TextComp',
mounted () {
setTimeout(() => {
this.$refs.focusMe.focus();
}, 500);
}
}
</script>
EDIT:
Ideally you can use $nextTick to wait until the DOM fully loaded, but sometimes it didn't work. So using setTimeout is just quick workaround to me.
https://v2.vuejs.org/v2/guide/custom-directive.html
In vue's official guide it says auto focus does not work in mobile safari. When the page loads, that element gains focus (note: autofocus doesn’t work on mobile Safari).
In iOS mobile safari, focus() only works when responding to a user interaction, like a click event. See: Mobile Safari: Javascript focus() method on inputfield only works with click?
My guess why it does not work in Vue:
In your Vue example, when you click on the button, it merely inserts a watcher into a batcher queue. A watcher has the information about what needs to update. You can see it as an update action, or update event. And later (almost immediately, at the next tick), Vue reads it (watcher) from the queue, and update the virtual dom / dom subsequently. However, this means, your code focus() is not "inside" a click event handler, rather, it is executed after the click event handler finishes.
I don't know the internal implementation of React though, so cannot explain why it works in the React example.
Still focus doesn't works for you???
Here is the solution :
setTimeout(() => {
this.$refs["input-0"].focus();
}, 1000);
The trick is to use setTimeout.
Related
I am using the ng-multiselect-dropdown package in angular 5 for creating a multi-select dropdown.
I want to do call a function on close or hide of the drop-down component.
like this
closeDropdown : function(){
console.log("dropdown close triggered");
}
According to the documentation you can pass closeDropDownOnSelection value true to close the dropdown whenever the selection is done
ng-multiselect dropdown
Incase of multiple selection you can call (onSelect)="onItemSelect($event)"
for more information check this Demo documentation
You can call the function within (change) event.
ex :
<ng-multiselect-dropdown
(blur)="closeDropdown($event)"
>
</ng-multiselect-dropdown>
To solve the bug identified by satira ( I couldn't comment due to low reputation), ie.
"When the component which has this multi-dropdown opens for the first time or you reload the page and click anywhere outside the dropdown, onDropDownClose() gets called." For me, it didn't happen after the first time. Anyway, i solved it by getting the id of any element on the screen(header, footer or any div) and used docuement.getElementById('element_id').click() on ngAfterViewInit.
ngAfterViewInit() { document.getElementById('header').click(); }
This made sure that no sideeffects take place on my app. I know this is a messy solution but since closeDropdown() of ng-multidropdown doesn't work, this was my only way out.
I had this issue recently and found a solution that works for me using a combination of (ngModelChange) and (click). When using ng-multiselect-dropdown the other normal HTML Element triggers like (blur) and (change) don't work, but the (ngModelChange) does work. Only problem with that is it triggers when being initialized. But I added a boolean variable to the (click) trigger that does seem to work.
Note that this also works to cover the onSelect, onDeSelect, etc
component.ts:
...
dropDownSelect: boolean = false;
dropDownSelection: number;
...
saveFunction(event) {
if(!this.dropDownSelect) return;
...
this.dropDownSelect = false;
}
component.html:
...
<ng-multiselect-dropdown [data]="dataSource" [(ngModel)]="dropDownSelection" [settings]="dropDownSettings" (click)="dropDownSelect = true" (ngModelChange)="saveFunction($event)"></ng-multiselect-dropdown>
...
I tried #misterz's solution but it didn't work. However I modified it and it works perfectly.
The trick:
In addition to (onDropDownClose), listen to a click event;
// this act as a differentiator between other calls(bug) and an intended call
(click)="dropDownSelect = true".
In your component, declare your variable and use it like this:
dropDownSelect = false;
saveFunction($event) {
if (this.dropDownSelect) {
// close the opening to subsequent actions
this.dropDownSelect = false;
// Perform action;
};
}
I'm simply trying to bind the blur event to a set of inputs found on my page.
var $urlTextBoxes = $(`input[type='url'][data-url-prefixer]`);
$urlTextBoxes.on("blur", onUrlTextBoxBlur);
whereas onUrlTextBoxBlur is a simple function checking the current value,
If the <input type="url" data-url-prefixer /> is already visible when prefixing with 'http://' if not already set.$(function () { // ... } is called, the onUrlTextBoxBlur is called on blur.
On the other hand, if the input is hidden on load (inside a div) and shown later, the onUrlTextBoxBlur is never hit and I don't get the point why?
I've tried
$(document).on("blur", $urlTextBoxes, onUrlTextBoxBlur);
which is not working at all.
Putting
if (jQuery._data($("#ExternalRegistrationUrl")[0] !== undefined)) {
console.log($("#ExternalRegistrationUrl")[0]);
console.log(jQuery._data($("#ExternalRegistrationUrl")[0], "events"));
}
shows
input#ExternalRegistrationUrl.form-control
Object {blur: Array[1]}
in master, and
input#ExternalRegistrationUrl.form-control
undefined
in my sub-view. Somehow, the event gots lost. But, how can this be? The Masters onReady is called before the SubViews onReady, or not?
Seems like another plugin is removing the content and re-adding it afterwards. Therefore, all bindings are lost. Now I need a way to re-bind by events after the plugin has done it's magic!
HIDDEN FIELD ALSO WORKING WITH BLUR
FIDDLE
SCRIPT
$(document).ready(function(){
$(":text").hide();
$(":text").blur(function(){
alert("Blurred");
});;
$(":text").show();
});
HTML
<input type="text" />
CSS
input
{
display:none
}
I've been working on trying to trigger an onchange listener with java script in Mozilla Firefox. I've found a lot on Stack Overflow posted about this, but nothing seems to be working for my unique case.
I've created this HTML with a onchange listener from an onchange event using this helpful post (JavaScript OnChange Listener position in HTML markup). Here's my code:
<HTML>
<head>
<script type="text/javascript">
window.onload= function () {
if(window.addEventListener) {
document.getElementsByClassName('search-box')[0].addEventListener('change', loadXMLDoc, false);
} else if (window.attachEvent){
document.getElementsByClassName('search-box')[0].attachEvent("onchange", loadXMLDoc);
}
function loadXMLDoc(){
alert('It worked');
}
}
function addTextCallListener() {
var searchBox = document.getElementsByClassName("search-box")[0];
searchBox.value = "Hello";
}
</script>
</head>
<BODY>
<input type="text" class="search-box" placeholder="Player Search">
<br \>
<button type="button" onclick="addTextCallListener()">Click Me!</button>
</BODY>
</HTML>
I also saved it as this jsfiddle (for some reason I had to keep it all together for it to work, I couldn't break it up into js and html).
https://jsfiddle.net/josephfedor42/crogL0zd/1/
If you play with this jsfiddle you can see that entering text and pressing enter will trigger the listener and the pop up with the message “It worked” will appear.
But if the button “Click Me!” is pressed it only changes the value of the text box, and the onchange listener is not called.
I realize I could easily add an onchange event to this button. But I want to to trigger the listener by programatically/ superficially using javascript in my addTextCallListener() function.
I've tried the simple stuff, like calling
searchBox.onchange();
searchBox.focus();
searchBox.click();
And a combination of these to add and remove the focus. But it doesn't seem to work. I've found quite a few posts on triggering an onchange event, but nothing that works in Firefox.
Any help would be appreciated.
Thanks for that link of a possible duplicated question. I had checked out that link before.
But I gave it a try again. I saved the jsfiddle from them both and neither one work.
My implementation of Dorian's answer
https://jsfiddle.net/josephfedor42/zaakd3dj/
My implementation of Alsciende's answer
https://jsfiddle.net/josephfedor42/xhs6L6u2/
emphasize mine
According to the mdn page about the change event,
The change event is fired for <input>, <select>, and <textarea>
elements when a change to the element's value is committed by the
user.
and to whatwg specs :
When the input and change events apply (which is the case for all
input controls other than buttons and those with the type attribute in
the Hidden state), the events are fired to indicate that the user has
interacted with the control.
Therefore, setting the value of an input is not an action "committed by the user" nor a sign that "the user has interacted with the control", since it was made by the code.
So, even if the specifications for this event are kind of unclear, the event should not fire when you change its value by code.
Something like this should work:
function addTextCallListener() {
var searchBox = document.getElementsByClassName("search-box")[0];
searchBox.value = "Hello";
//fire the event
if (document.createEvent) {
searchBox.dispatchEvent('change');
} else {
searchBox.fireEvent("onchange");
}
}
Here is the code I needed to add to my function addTextCallListener:
var evObj = document.createEvent('HTMLEvents');
evObj.initEvent( 'change', true, true );
searchBox.dispatchEvent(evObj);
I updated the jsfiddle. The working code is here https://jsfiddle.net/josephfedor42/crogL0zd/7/
Replace onchange with change in this part:
document.getElementsByClassName('search-box')[0].attachEvent("onchange", loadXMLDoc);
It seems disabled button "onclick" function is still fired when triggering it programmaticaly, eg:
<div>
<input type="button" onclick="save()" id="saveButton" value="save" disabled="disabled" />
<input type="button" onclick="byPassDisabled()" value="bypass disabled button"/>
<div id="counter">0</div>
function save(){
var count = parseInt($('#counter').html());
$('#counter').html(++count);
}
function byPassDisabled(){
$('#saveButton').click();
}
see http://jsfiddle.net/WzEvs/363/
In my situation, keyboards shortcuts are bound to functions triggering the ".click()" on buttons. I'll find it very annoying to have to disable the shorcuts or check if the button is disabled myself. I'd prefer a general solution fixing this problem.
But why? This behavior doesn't seem fair to me.
Any workaround?
The attribute only disables user interaction, the button is still usable programmatically.
So yeah, you gotta check
function byPassDisabled(){
$('#saveButton:enabled').click();
}
Alternatively don't use inline handlers.
$(document).on('click', '#saveButton:enabled', function(){
// ...
});
For future use...the OP code works because jQuery will still call it's own handlers even if the DOM element is disabled. If one were to use vanilla javascript, the disabled attribute would be honored.
const element = document.getElementById('saveButton');
element.click() //this would not work
You can programmatically trigger click on a disabled button.
There are ways to find if the event is a click on button by user or it has been trigger programmatically. http://jsfiddle.net/WzEvs/373/
$(function () {
$("#saveButton").on('click', function (e) {
if (!e.isTrigger) {
var count = parseInt($('#counter').html());
$('#counter').html(++count);
}
});
$("#bypassButton").on('click', function (e) {
$("#saveButton").click();
});
});
e.isTrigger is true if you call the click() programmatically. Basically you are triggering the click event manually in code.
You can trigger click still although made it disable .As Spokey said it just shows the user-interaction(the usability still persists that can be turned on programmatically) .
off or unbind the click will solve this issue.
Thanks
I have a problem which I don't know is related to the meteor implementation of events or to Javascript events in general.
I have a textbox attached to a "change" event.
Next to it, I have a button attached to a "click" event.
When I do a change in the textbox and click the button, the click event does not fire (only the change event does). So I have to click the button two times for the click event to fire.
In Firefox, it works if I attach a mousedown event instead of the click event to the button. In Chrome it doesn't work either ways.
Minimal code reproducing the problem:
JAVASCRIPT: testevent.js
if (Meteor.isClient) {
Session.set("something", "something");
Template.hello.foo = function() {
return Session.get("foo");
};
Template.hello.something = function() {
return Session.get("something");
}
Template.hello.events({
'click .buttonid' : function () {
console.log("click !");
},
'change .textid' : function (e,t) {
console.log("change !");
var bar = e.target.value;
Session.set("foo",bar);
}
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
});
}
HTML: testevent.html
<head>
<title>testevent</title>
</head>
<body>
{{> hello}}
</body>
<template name="hello">
<input type="text" class="textid" value="{{foo}}"/>
<input type="button" class="buttonid" value="{{something}}" />
</template>
When I replace class with id the click event fire, but when I have multiple fields with the same id the events work only on one field.
The issue has to do with the hello.foo:
Template.hello.foo = function() {
return Session.get("foo");
};
and the fact that the value of foo is used to reactively populate the text input. If you remove the hello.foo function everything works as expected. When the user clicks the button, the change event fires which sets the "foo" session variable which in turn causes the template to re-render. I think the rendering process clears the remaining event queue, so the click handler never fires.
There are a couple of ways you can fix this. An easy (but crude) way is just to delay setting the session variable in the change event handler. For example:
Meteor.setTimeout(function(){Session.set("foo", bar);}, 100);
Obviously you would need to choose an appropriate delay and that may be browser/data dependent. Alternatively, you can just put the text input in its own template. For example:
<template name="hello">
{{> helloText}}
<input type="button" class="buttonid" value="{{something}}" />
</template>
<template name="helloText">
<input type="text" class="textid" value="{{foo}}"/>
</template>
After binding the events properly to this new template, you will find that helloText will be rendered separately from hello and thus your events will be preserved.