Is it possible to "hold" an event for later firing? - javascript

What I'm trying to do is: when the user presses a cancel button, or navigates away of the page through a link or a menu option, I check if there are unsaved changes. If so, I ask the user if he/she wants to save. I can't do this with a javascript confirm window because I sometimes have more than two options, so I can't "hold" everything until the user makes a selection like confirm would. So I though to "save" the event, cancel it's current execution, whait until user makes up his/her mind, then take the action needed according to their answer, then raise back the original event.
So, as a code example of what I thought:
I have this piece of code:
var executingEvent;
function someFunction() {
...
if(existUnsavedChanges) {
showConfirmMessage();
executingEvent = window.event;
if (executingEvent.stopPropagation) { executingEvent.stopPropagation(); } else { executingEvent.cancelBubble = true; }
...
}
}
Is there a way to later on do something like this?
raise (executingEvent);
Sounds a bit complex, I'd also welcome other options :)

to fire an event use
elem.dispatchEvent(event)
Where elem is either the element you bound to or below it in the DOM (so it bubbles up).
Of course if you already stopped propagation the event wont bubble up, so you may want to create a new event object instead.
var ev = document.createEvent("Event");
ev.initEvent(type, true, true);
ev.origEv = originalEvent;
elem.dispatchEvent(ev);

It sounds to me like you're overthinking it - you could just raise the same type of event as the original event (which you would have cancelled) once the user has taken the action that you prompted for.
You can work out what the original event raised was by inspecting properties on the event object e.g. the type of event, the original target, etc.

Related

Know status of windows.beforeunload event by user

I am using windows.beforeunload event to prompt user whether they would like to unsaved changes or not.
Inside windows.beforeunload event, I want to know status whether user clicked on "Leave" button or "Cancel" button (In Chrome). Is it possible? If not then any alternate solution?
Per MDN beforeunload
This event enables a web page to trigger a confirmation dialog asking the user if they really want to leave the page. If the user confirms, the browser navigates to the new page, otherwise it cancels the navigation.
The above implies that the event is cancelable, but...
According to the specification, to show the confirmation dialog an event handler should call preventDefault() on the event.
And even then, if you are indeed doing that...
To combat unwanted pop-ups, browsers may not display prompts created in beforeunload event handlers unless the page has been interacted with, or may even not display them at all.
Proper Use Case
However, unlike the unload event, there is a legitimate use case for the beforeunload event: the scenario where the user has entered unsaved data that will be lost if the page is unloaded.
It is recommended that developers listen for beforeunload only in this scenario, and only when they actually have unsaved changes, so as to minimize the effect on performance. See the Examples section below for an example of this.
See the Page Lifecycle API guide for more information about the problems associated with the beforeunload event.
Example
In this example a page listens for changes to a text input. If the element contains a value, it adds a listener for beforeunload. If the element is empty, it removes the listener:
const beforeUnloadListener = (event) => {
event.preventDefault();
return event.returnValue = "Are you sure you want to exit?";
};
const nameInput = document.querySelector("#name");
nameInput.addEventListener("input", (event) => {
if (event.target.value !== "") {
addEventListener("beforeunload", beforeUnloadListener, {capture: true});
} else {
removeEventListener("beforeunload", beforeUnloadListener, {capture: true});
}
});

Javascript click event listener fires only once

I have a Chrome extension that intercepts and checks tweets before they get posted. To do this, I've add an event listener to the Tweet button. Sine the content is dynamic, I use the solution proposed in this thread:
initialize : function() {
let that = this;
let jsInitChecktimer = setInterval(checkForJsFinished, 111);
function checkForJsFinished () {
if (document.querySelector("div[data-testid='tweetButtonInline']")) {
clearInterval (jsInitChecktimer);
console.log("Button found");
that.addSubmitNewTweetClickHandler();
}
}
},
addSubmitNewTweetClickHandler : function() {
let that = this;
let buttonSubmitTweet = document.querySelector("div[data-testid='tweetButtonInline']");
buttonSubmitTweet.addEventListener('click', function(e) {
console.log("CLICK");
// Stop default event from happening
e.preventDefault();
e.stopImmediatePropagation();
// Do stuff
});
},
If the tweet passed the checks alright, it gets submitted by programmatically triggering the event using .trigger('click').
This works fine, but only once. After a tweet has been submitted and posted, the event listener on the Tweet button is gone, and I cannot intercept the next tweet to check it. I've tried calling initialize() after submitted again -- maybe the button gets removed and newly added to the DOM (it actually disappears fire a moment when submitting a tweet) -- but the querySelector finds the button immediately. But even after calling initialize() again, no click even on the Tweet button fires.
What could be the issue here? My problem is that I don't even know where to look for and how to debug this.
After many more hours, I've finally figured it out. The problem was essentially the highly dynamic content of the new Twitter website. After submitting a tweet, the Tweet button gets indeed removed and added again. In needed to do a serious of changes:
Use a MutationObserver to keep track of any changes. Every time there's a change, call the initialize() function. To avoid too many calls, I do this in case of certain changes (unnecessary detail here)
Change the addSubmitNewTweetClickHandler() method so that the event listener first gets removed in order to avoid duplicate listeners (please note that I use objects hence the use of this compared to my original question)
addSubmitNewTweetClickHandler : function() {
let that = this;
let buttonSubmitTweet = document.querySelector("div[data-testid='tweetButtonInline']");
buttonSubmitTweet.removeEventListener('click', this.handleSubmitNewTweetClick );
this.handleSubmitNewTweetClick = this.handleSubmitNewTweetClick.bind(this)
buttonSubmitTweet.addEventListener('click', this.handleSubmitNewTweetClick );
},
This change required to create the reference function handleSubmitNewTweetClick
Overall, it's still not a perfect solution since I call initialize() many unnecessary time. However, I failed to reliably identify when the Tweet button was added to the document. When I used the MutationObserver none of the added nodes had the attribute data-testid which I need to identify the correct button. I have node idea why this attribute was not there. Maybe the attribute is added some times after added to button, but even with an additional MutationObserver looking for attribute changes I could detect this.
Anyway, it works now and it's only for a prototype.

Not able to understand how the domEvent works

Scenario:
I have a RadCombobox and I have attached functions to most of the events.
One event of the combobox is OnClientBlur and I am using this to check whether value in Combo is "Unassigned" or not. If it is "Unassigned" I need to cancel the onblur event and keep the focus on to the same combo.
This is the javascript which I has been used to cancel the event.
if (sender.get_text() === "Unassigned") {
eventArgs.get_domEvent().preventDefault();
return false;
}
Problem:
When the user tabs out first time of the ComboBox the event gets cancelled and the focus stays on the same combo box (in this case it is the 3rd Combo).
But when the user hits the tab button again the focus moves to the next control.
When I debugged the code I found that when the user first hits the tab button, following line works
eventArgs.get_domEvent().preventDefault();
I can see the preventDefault function, see following snapshot.
but when the user hits the tab button again I get an error and cannot see preventDefault function, see following snapshot
I am not able to understand what is going wrong here. Anyhelp would be appreciated.
Your problem, revolves around the difference between MouseEvents and KeyEvents. And also the way Telerik implement the OnClientBlur event. As far as it doesn't point to a specific type of browser event, each time it gets triggered
As you see in the first snapshot you got clientX and clientY, which means your OnClientBlur derived from a MouseEvent.
Whereas in the second one you got altKey, altLeft, and also there is no button property, which means that this one is a KeyEvent.
The other point here is as you have these fields in the output:
e.bookmarks
e.behaviorPart
e.behaviorCookie
Means you are using one of the old versions of IE4+ to IE7 or IE8, which they have cancelBubble instead of preventDefault.
Sometimes events are not cancelable, and using event.cancelable you can make sure if the current event is cancelable or not.
At the end to fix you code you can simply do this:
if (sender.get_text() === "Unassigned") {
var domEvent = eventArgs.get_domEvent();
if(domEvent.cancelable){
if(typeof(domEvent.preventDefault)==="function")
domEvent.preventDefault();
else
domEvent.cancelBubble = true;
return false;
}
else{
//you can not cancel the event, do something else to make it manageable
}
}

Dispatch click event and keep keystroke modifiers

I would like to listen to a click event on a particular element and then dispatch it to a link element while keeping in mind if the meta key was hold or not.
For instance:
Let's say I have a table of elements. When the user clicks on a row I would like to follow a link, but I would like the link to be opened in a new tab if the user had his ⌘ /ctrl key pressed.
It seems simple but I found out this was tricky... (As I didn't succeed to do it!)
The DOM Event API already provides all you need, assuming you need only to remember the modifier keys from a legitimate user action.
When using addEventListener to catch an event, simply take the parameter, which is a DOMEvent instance, that is passed to your handler function, and resend it to your target element with dispatchEvent :)
Indeed, a DOMEvent instance encapsulates its source environment. More specifically here, a MouseEvent knows which keys were pressed when it was fired.
Demo: try clicking the link in this JSfiddle while holding down ⌘ (or ctrl if not on a Mac), for example.
For a complete reference, here is the used code:
var button = document.getElementById('source'),
target = document.getElementById('target');
function handler(evt) {
target.dispatchEvent(evt); // that's all the magic it takes
}
button.addEventListener(
'click', // listen to a click event
handler,
false // no capture, i.e. do not catch events before children
);​
You may also find this complete reference on DOM events useful :)

Javascript onChange arrow keys

Ok, so we all know that onChange is used to execute javascript code on a select statement when the option changes. However, if you change a select statement using the arrow keys, the onChange event is not called. Is there a way around this? Please help! I'm OCD I know.
--EDIT 1--
Just tested this in IE and arrow keys do work. Apparently it's just Chrome. ** Goes to check firefox
-- Edit 2 --
Tested in Firefox and realized just before an answer below talked about the onBlur action being required for the change. So the answer here is:
Internet Explorer recognizes onChange events from the keyboard as well as clicking on them.
Firefox and Chrome both require key events to be followed by blur event in order to call onChange.
Now normally, I don't like Internet Explorer, because it's a piece of garbage... But I think I... unfortunately, have to say they got that one right.
My understanding as to the reasoning for the blur event on chrome and firefox is to save resources, but I disagree with that. I feel it should follow the literal interpretation of the command onChange... Sigh... I suppose I'm probably wrong somehow, though.
I would suggest you to write the required code in Key Up event to capture the Key press and and also check for Key Code. Hope this helps
Scrolling through a select box is not considered a change. The change happens when you blur() the select and the new option value is applied to the select element.
Coming back to this, it appears that since the asking of this question, Chrome now fires onChange after key events. Firefox appears to still wait for onblur. http://jsfiddle.net/2aQBN/
$(document).ready(function() {
$("#test").on("change", function() {
console.log("Changed.");
});
});
W3C Specification appears to suggest using an input event instead.
When the input event applies, any time the user causes the element's
value to change, the user agent must queue a task to fire a simple
event that bubbles named input at the input element.
However, no input event appears to fire in Chrome or Firefox for the select element. (Just input elements.)
Test demonstrating the current value vs the last onchange value.
http://jsfiddle.net/teynon/MpyHK/5/
Firefox will change the value onmouseover. The key change will change the value as well. However, the onchange hasn't fired. If the form submits while the user has the select menu open, the currently highlighted option is submitted.
From W3C:
If the multiple attribute is absent, and the element is not disabled,
then the user agent should allow the user to pick an option element in
its list of options that is itself not disabled. Upon this option
element being picked (either through a click, or through unfocusing
the element after changing its value, or through a menu command, or
through any other mechanism), and before the relevant user interaction
event is queued (e.g. before the click event), the user agent must set
the selectedness of the picked option element to true and then queue a
task to fire a simple event that bubbles named change at the select
element, using the user interaction task source as the task source.
There is a LONG discussion at https://bugzilla.mozilla.org/show_bug.cgi?id=126379 about this with many people asking for the arrow keys to work. (And some defending the onchange approach.)
Some users have suggested that the W3C is flat out wrong in the specification for the select element's change event. Instead suggesting we propose changes to the specification for how we expect the select's onchange functionality to work.
The current functionality is clearly not intuitive to a large number of people based solely on the number of bug reports. (Mozilla has 40 marked as duplicates.)
This is a pretty dirty hack, but you can force the the change event to fire by doing this:
element.addEventListener('keyup', function(evt){
evt.target.blur();
evt.target.focus();
}, false);
So you'd register an event listener for change as well, and that function would get called when the user presses a key on the <select> via the code above.
You may want to scope this only to Firefox, but AFAIK you'd have to use UA sniffing for that so it's up to you if that's acceptable.
Source
I'm thinking about something like this (to not trigger event if value wasn't changed):
select.keydown(function(){
var _this = $(this);
var _val = $(this).val();
setTimeout(function(){
if(_this.val() !== _val){
_this.trigger("change");
}
}, 1);
});
Here's a realization of this request. For brevity only showing the code. See https://github.com/ida/skriptz/blob/master/js/fields/select/selection_changed.js for long explanations in comments.
function addSelectionChangedListener(selectionEle, onChangeDoWithEle) {
var selectedIndex = null
function onChangeEvent(eve) {
// If selection-change was caused of an option's click-event:
if(eve.explicitOriginalTarget.tagName.toLowerCase() == 'option') {
// We want to trigger passed event-handler:
onChangeDoWithEle(eve.target)
}
}
function onKeyEvent(eve) {
// Key-event is keydown, remember current selectedIndex:
if(eve.type == 'keydown') {
selectedIndex = eve.target.selectedIndex
}
// Key-event is keyup, if selection changed, trigger passed handler:
else if(selectedIndex != eve.target.selectedIndex) {
onChangeDoWithEle(eve.target)
}
}
selectionEle.onchange = function(eve) onChangeEvent(eve)
selectionEle.onkeydown = function(eve) onKeyEvent(eve)
selectionEle.onkeyup = function(eve) onKeyEvent(eve)
}

Categories

Resources