I have to hide same field on it's blur event.
Extjs 6 calls event delegation on component hide method.Event delegation revert focus to last field which had focus.
And, I don't want this revert focus. Is there any way I can stop event delegation while hiding elements in extjs ?
Event delegation comes with extjs 5 - Delegated Events and Gestures in Ext JS 5
Method using for hide - https://docs.sencha.com/extjs/6.0/6.0.1-classic/#!/api/Ext.Component-method-onHide
onHide() method from ExtJS source code - check revertFocus()
onHide: function(animateTarget, cb, scope) {
var me = this,
ghostPanel, fromSize, toBox;
if (!me.ariaStaticRoles[me.ariaRole]) {
me.ariaEl.dom.setAttribute('aria-hidden', true);
}
// Part of the Focusable mixin API.
// If we have focus now, move focus back to whatever had it before.
me.revertFocus(); // this revert focus making probelm
// Default to configured animate target if none passed
animateTarget = me.getAnimateTarget(animateTarget);
// Need to be able to ghost the Component
if (!me.ghost) {
animateTarget = null;
}
// If we're animating, kick off an animation of the ghost down to the target
if (animateTarget) {
toBox = {
x: animateTarget.getX(),
y: animateTarget.getY(),
width: animateTarget.dom.offsetWidth,
height: animateTarget.dom.offsetHeight
};
ghostPanel = me.ghost();
ghostPanel.el.stopAnimation();
fromSize = me.getSize();
ghostPanel.el.animate({
to: toBox,
listeners: {
afteranimate: function() {
delete ghostPanel.componentLayout.lastComponentSize;
ghostPanel.el.hide();
ghostPanel.setHiddenState(true);
ghostPanel.el.setSize(fromSize);
me.afterHide(cb, scope);
}
}
});
}
me.el.hide();
if (!animateTarget) {
me.afterHide(cb, scope);
}
},
You are doing it wrong, revertFocus() is a main problem source. The solution might be:
blurEventFunction:function(cmp){
cmp.previousFocus = null;
cmp.hide();
}
Use suspendEvents and resumeEvents in the function you are calling in the viewcontroller when the blur event fires:
It's not stopEvents is suspendEvents. My fault. :P
blurEventFunction:function(cmp){
cmp.suspendEvents();
cmp.hide();
camp.resumeEvents();
}
I got the same problem. (extjs 6.5.1 - using a modal window with closeAction: 'hide')
I was debugging the code and seems it happened because the latest field focused was in a panel and my modal window was not child of that panel.
(seems the extjs get the ancestor of the modal window to find the latest focused field, then, set the focus)
When I added the window to that panel, it worked fine. (when the modal window was closed, the focus was on the latest field focused before open the window).
Debugging the Ext.util.Focusable class, I saw a config called preventRefocus. If you add that config with value true to your modal window, the content of the revertFocus function won't be executed and you won't get the error.
revertFocus: function() {
var me = this,
focusEvent = me.focusEnterEvent,
activeElement = Ext.Element.getActiveElement(),
focusTarget, fromComponent, reverted;
// If we have a record of where focus arrived from,
// and have not been told to avoid refocusing,
// and we contain the activeElement.
// Then, before hiding, restore focus to what was focused before we were focused.
// --->>> THE IF BELOW: !me.preventRefocus <<<---
if (focusEvent && !me.preventRefocus && me.el.contains(activeElement)) {
I hope it also can help somebody in the future.
Related
I am using the TabView component from PrimeVue (Vue 3), and I want to stop the tab change if any changes are made by the user, the problem is that I don't know how. I've already tried passing the event and using preventDefault and stopPropagation but seems that it doesn't work and click event is still happening.
The procedure should be:
If any changes are made, user press the tab and a dialog appears.
If user clicks 'No', I should prevent the tab change and stop the click event
Here is the demo of what I'm trying to archive, should be simple https://codesandbox.io/s/aged-wave-yzl1k?file=/src/App.vue:0-1753
If a flag is true I want to show a confirm dialog and prevent the tab change if user dismiss it.
The component that I'm using for the TabView: https://primefaces.org/primevue/showcase/#/tabview
Thanks in advance,
From the docs it looks like that internally the component will first switch tabs and then emit "tab-click", which explains the issue you're seeing. The exception is if the tab is disabled, in which case it won't change tabs but will emit "tab-click".
It took a bit to figure out, but there is a way to get the functionality you need with only a small adjustment. It requires a change in your main.js as well as in your App.vue file.
// main.js
/*
* Put this after you import TabView.
* This will prevent automatic tab switching but still emits
* the event to your application.
*/
TabView.methods.onTabClick = function(event, i) {
this.$emit('tab-click', {
originalEvent: event,
index: i
});
}
// App.vue
const onTabClick = (event) => {
if (changes.value) {
confirm.require({
message:
"Are you sure that you want to leave this tab? You'll lose your changes",
icon: "fal fa-exclamation-triangle",
acceptLabel: "Yes",
rejectIcon: "No",
accept: () => {
alert("here we should allow tab change");
activeIndex.value = event.index; // manually set activeIndex
},
reject: () => {
alert("stop tab change");
},
});
}
};
These changes modify what the onTabClick library method to only emit the event, without automatically switching. Then in your app you can check the index property of the event to determine what should be set to active.
in my Cordova App I have a problem on iOS devices and I have no idea how to solve.
I have a custom auto-suggest which shows up below an input field while typing. All is contained in a dialog box with "position: fixed;".
Autocomplete is an unordered list. Click on < li > Element should place the selected text into the input.
The problem is, when user clicks on the li, the input loses focus, the keyboard disappears and the whole fixed dialog box "jumps" down and the click event is not recognized.
It is recognized when the keyboard already IS closed.
I tried several workarounds, like giving focus back to input field immediately after blur. But it does not help. Keyboard closes and opens instead of just keeping opened.
Any Ideas how to solve?
Here is a video showing the behaviour. It is recorded on the iOS Simulator but same behaviour on real iPhone 6s.
I have found a solution now. As I said the click event is not triggered when the keyboard hides, but a touchstart event is triggered.
So I did a workaround, looking for touchstart event followed by a blur event. If the touchstart-target does not receive a click event within a given time, I will trigger one. This works on my test iPhone 6s.
Here is the code:
var iosTapTarget=null;
if (device.platform === 'iOS') {
js.eventListener(document.body).add('iosTap', 'touchstart', function (e) {
iosTapTarget = e.target;
js.eventListener(iosTapTarget).add('iosTapClick', 'click', function(e) {
// when the target receives a click, do not trigger another click
if (iosTapTarget) js.eventListener(iosTapTarget).remove('iosTapClick', 'click');
iosTapTarget = null;
});
// after short time unset the target
window.setTimeout(function () {
if (iosTapTarget) js.eventListener(iosTapTarget).remove('iosTapClick', 'click');
iosTapTarget = null;
}, 600)
});
// on each input fields listen for blur event and trigger click event on element received touchstart before
var blurableElements = document.querySelectorAll('input[type=text],input[type=email],input[type=password],textarea');
for (var j = 0; j < blurableElements.length; ++j) {
js.eventListener(blurableElements[j]).remove('iosBlur', 'blur');
js.eventListener(blurableElements[j]).add('iosBlur', 'blur', function () {
window.setTimeout(function() {
if (iosTapTarget) {
js.eventListener(iosTapTarget).remove('iosTapClick', 'click');
js.triggerEvent(iosTapTarget, 'click');
}
}, 50);
});
}
}
PS: Event handling comes from my own JS "framework" js.js available here: https://github.com/JanST123/js.js
But you can use vanilla JS event handling or jQuery event handling too, of course.
I'm trying to put a link on a selectize dropdown in order to allow the user make an operation other than select an item while still allowing that the user selects the item as main option.
Here is an example of what I want to achieve (but is not working as expected):
What I did is plainly insert links on the HTML. But it's not working, I suppose that for some kind of event propagation stop, is it possible to achieve with selectize?
Nobody did answer yet and I think there's more to say about, so, here is an example of what I did:
render: {
option: function(item) {
return '<div><span>'+item.label+'</span>'
+ '<div class="pull-right">'
+ 'Link'
+ '</div></div>';
}
}
As you can see, I did change the "option" renderization, and inserted a link in plain HTML. The problem is that -as shown on image- when I do click the link, the browser does not follow the link, but executes the default action for selectize, which is selecting the clicked element.
What I want to achieve is to make it follow the link when clicked.
Here is a fiddle of what I did: http://jsfiddle.net/uetpjpa9
The root problem is that Selectize has mousedown and blur handlers that are dismissing the dropdown before the mouseup event that would complete the click that your link is waiting for from ever occurring. Avoiding this without direct support from Selectize is not easy, but it is possible thanks to its plugin system and the amount of access it gives you to Selectize internals.
Here's a plugin that allows a dropdown element with the class clickable to be clicked on. (demo)
Selectize.define('option_click', function(options) {
var self = this;
var setup = self.setup;
this.setup = function() {
setup.apply(self, arguments);
var clicking = false;
// Detect click on a .clickable
self.$dropdown_content.on('mousedown click', function(e) {
if ($(e.target).hasClass('clickable')) {
if (e.type === 'mousedown') {
clicking = true;
self.isFocused = false; // awful hack to defuse the document mousedown listener
} else {
self.isFocused = true;
setTimeout(function() {
clicking = false; // wait until blur has been preempted
});
}
} else { // cleanup in case user right-clicked or dragged off the element
clicking = false;
self.isFocused = true;
}
});
// Intercept default handlers
self.$dropdown.off('mousedown click', '[data-selectable]').on('mousedown click', '[data-selectable]', function() {
if (!clicking) {
return self.onOptionSelect.apply(self, arguments);
}
});
self.$control_input.off('blur').on('blur', function() {
if (!clicking) {
return self.onBlur.apply(self, arguments);
}
});
}
});
To use it, you need to pass the plugin option to the selectize call (.selectize({plugins:['option_click']})) and add the clickable class to links in your dropdown template. (This is fairly specific. If there are nested elements, make sure clickable is on the one that will first see the mousedown event.)
Note that this is a fairly hackish approach that may have edge cases and could break at any time if something about how Selectize dispatches events changes. It would be better if Selectize itself would make this exception, but until the project catches up to its backlog and becomes more receptive to requests and PRs this may be the most practical approach.
I have a table and I use select menu in each row for different actions for that specific row.
For example:
$(document).on('change', '.lead-action', function() {
// Do stuff
}
this method gets the value of the selected option. Based on the selected value, I display different popups. When the user leaves the page, the select menu retains the previously selected option.
Sometimes users click on the same option in the select menu. When they do, the above code doesn't work.
Is there a way to invoke the code block above if the same option in the select menu is selected?
I'm gathering that you just want the dropdown to fire anytime a selection is made. If so, check out the answer to Fire event each time a DropDownList item is selected with jQuery.
See my updated answer below:
You can use this small extension:
$.fn.selected = function(fn) {
return this.each(function() {
var clicknum = 0;
$(this).click(function() {
clicknum++;
if (clicknum == 2) {
clicknum = 0;
fn(this);
}
});
});
}
Then call like this:
$(".lead-action").selected(function(e) {
alert('You selected ' + $(e).val());
});
Update:
I'm actually rather unhappy with the original script. It will break in a lot of situations, and any solution that relies on checking the click count twice will be very fickle.
Some scenarios to consider:
If you click on, then off, then back on, it will count both clicks and fire.
In firefox, you can open the menu with a single mouse click and drag to the chosen option without ever lifting up your mouse.
If you use any combination of keyboard strokes you are likely to get the click counter out of sync or miss the change event altogether.
You can open the dropdown with Alt+↕ (or the Spacebar in Chrome and Opera).
When the dropdown has focus, any of the arrow keys will change the selection
When the dropdown menu is open, clicking Tab or Enter will make a selection
Here's a more comprehensive extension I just came up with:
The most robust way to see if an option was selected is to use the change event, which you can handle with jQuery's .change() handler.
The only remaining thing to do is determine if the original element was selected again.
This has been asked a lot (one, two, three) without a great answer in any situation.
The simplest thing to do would be to check to see if there was a click or keyup event on the option:selected element BUT Chrome, IE, and Safari don't seem to support events on option elements, even though they are referenced in the w3c recommendation
Inside the Select element is a black box. If you listen to events on it, you can't even tell on which element the event occurred or whether the list was open or not.
The next best thing is to handle the blur event. This will indicate that the user has focused on the dropdown (perhaps seen the list, perhaps not) and made a decision that they would like to stick with the original value. To continue handling changes right away we'll still subscribe to the change event. And to ensure we don't double count, we'll set a flag if the change event was raised so we don't fire back twice:
Updated example in jsFiddle
(function ($) {
$.fn.selected = function (fn) {
return this.each(function () {
var changed = false;
$(this).focus(function () {
changed = false;
}).change(function () {
changed = true;
fn(this);
}).blur(function (e) {
if (!changed) {
fn(this);
}
});
});
};
})(jQuery);
Instead of relying on change() for this use mouseup() -
$(document).on('mouseup', '.lead-action', function() {
// Do stuff
}
That way, if they re-select, you'll get an event you can handle.
http://jsfiddle.net/jayblanchard/Hgd5z/
I'm writing an app in phonegap with a specific 'zoom-in' effect when clicking on an input element (everything but the input hides and custom typeahead suggestions are shown). The view is written using backbone.js and i'm entering the 'zoomed-in' mode on focus:
events: {
'focus .search': 'startSearch',
}
In my startSearch method i'm doing all the logic to immitate the zoom-in effect.
_moveCursorToEnd: function(element) {
var val_len = element.value.length;
element.scrollLeft = val_len * 9;
setTimeout(function() {
element.selectionStart = val_len;
}, 1);
},
startSearch: function() {
window.navbar.hide();
this.$input.addClass('search-input-small');
this.$cancel.show();
var el = this.$input[0];
this._moveCursorToEnd(el);
},
The search-input-small makes the input smaller.
The setTimeout in _moveCursorToEnd is required because the effect doesn't work otherwise. The issue is that despite setTimeout having 1msec, it looks like a second cause inconvenient cursor move.
Is there any way to move the cursor to the end that would work on Safari Mobile 6 (iOS 6+) without the ugly delay?
I've ended up changing the event from focus to click and using similar code as mentioned above so that it works. Seems like the selection from a click that focused the text edit is applied after focus handler and before click handler.