Return Functions using prototype's Event.observe - return

I'm trying to migrate from using inline event triggers to using event listeners using Prototype's Event.observe function. There are a few inline commands that I don't know how to handle using the function call.
I want to move from:
<form id='formFoo' action='whatever.php' onsubmit="return Foo.verify(this);">
To an event call:
Event.observe('formFoo', 'submit', Foo.verify);
This of course will not work, as I need a return value from the function I call to determine whether the form gets submitted or not.
How do I do this using event handlers?

The easiest way to do this is probably Event.Stop from prototype. This works for me (put this in any script block):
Foo = { verify: function(){ return false } };
Event.observe(window, 'load', function() {
Event.observe('formFoo', 'submit', function(e){
if(! Foo.verify($('formFoo'))){
e.stop();
}
});
});
It stops every form submission; you will just have to change Foo.verify to do what you wanted.
Explanation: When the submit event is triggered, prototype passes the handler a prototype Event object representing the event, and the stop method on that object prevents the submit. The rest is just setting up the event.
Minor note: Among other things, passing Foo.verify directly as a handler will cause verify to be called as a function, not a method (this will be the global object within the call, rather than Foo). That situation might be okay - if verify doesn't use this, you're fine. Be aware of the difference, though.

Related

Have Stimulus wait for Dom changes using MutationObserver custom event listener before continuing

I am writing a Stimulus controller that has a function which needs wot wait for DOM changes before it can continue (As a Turbo Stream response needs to update a frame). Before the DOM change, it can't find the target, but it should be able to AFTER the DOM change.
I have successfully enabled MutationObserver, which is able to tell me that the child element was added--but this happens after the function/action is looking for it.
Therefore I found this article on how to use Mutation Observer to add a custom Event Listener.
I've gotten it to work as far as until the last step, where we code the Promise.
When I run the code, I get
undefined is not an object (evaluating "el.innerText.includes(text)
I tried changing it to
document.getElementByTagName(el).innerText.includes(text)
But that returned that it's not a function.
The instantiation of the MutationObserver is inside my connect() method:
connect() {
const observer = new MutationObserver( list => {
const evt = new CustomEvent('dom-changed', {detail: list});
document.body.dispatchEvent(evt)
});
observer.observe(document.body, {attributes: true, childList: true, subtree: true});
}
the waitforText(el, text, maxWait=5000) function is a controller action currently.
I then run this.waitForText("h1", "Settings for") inside the function I need it to wait. I'm merely using a h1 for testing before I put it on an element that is within the same frame as that h1.

jQuery UI dialog binding keydown doesn't always work

I'm writing my own ESC handler because I need to do other actions when ESC is pressed, specifically I need to manage where focus goes for keyboard-only users. I have it working for all menus and some dialogs (both of which are using jQueryUI) but I'm having problems with dialogs that open on top of other dialogs (confirmation dialogs).
I'm using a Backbone View and adding my keydown handler on dialogcreate. this.$el.on('dialogcreate', this.bindKeydownEvent);
My handler:
bindKeydownEvent: function(ev, ui) {
var self = this;
this.$el.dialog().on('keydown', function(evt) {
if(evt.keyCode === $.ui.keyCode.ESCAPE) {
self.$el.dialog("close");
if(self.options.closeFocusEl) {
$(self.options.closeFocusEl).focus();
}
evt.stopPropagation();
}
});
}
I've checked and this.$el.dialog() is the correct dialog when the second dialog calls this.bindKeydownEvent but for some reason the keydown handler is not being triggered no matter what I press in the dialog (Tab, Space, Enter, random letters, etc).
Any idea what I'm doing wrong or have a better way I could bind the keydown event?
EDIT:
I just noticed that this is also happening in some first-level dialogs. It looks like the only difference is the way we get the template and therefore create the interior of the dialog. In our Alert and Confirmation dialog classes, we define the template as an attribute on the object like this: template: _.template("<div><%= o.content %></div>"). In other views (in which the keydown binding works) we build the child elements and add them to the DOM of the dialog, set the template in the initialize function
this.options.template = 'navigation/CreateNewDialog.template';
or set it when we call the dialog
var closeConv = new views.CloseConversationDialogView({
confirm: this.closeConversationConfirmed,
content: i18n.t("closeConversationInput"),
template: "conversation/CloseConversationDialog.template"
});
closeConv.render();
Is there a reason that creating the template inline as an attribute on the view would not bind keydown correctly?
To understand why your event handler isn't being triggered you need first understand how event delegation works.
The key to event delegation in that events bubble up the DOM. So when you bind your event using this.$el.dialog().on('keydown',..., what you basically doing is listening to any keydown event that is triggered on your $el or it's descendants. In this case being that your second dialog isn't a descendant of your $el it's events won't bubble up to it and therefore don't trigger your handler.
To work around this you can either bind directly to your second dialog, or instead bind to a exisitng higher level element like the document. For example
$(document).on('keydown', '.myDialog', function() {...
The only thing my original attempt was missing was "widget". The widget method, according to api.jqueryui.com,
Returns a jQuery object containing the generated wrapper.
I don't see any documentation on what exactly $('.selector').dialog() returns but apparently it is not the same as $('.selector').dialog("widget"). I also changed on('keydown'... to just use the jQuery keydown instead.
bindKeydownEvent: function(ev, ui) {
var self = this;
this.$el.dialog("widget").keydown(function(evt) {
if(evt.keyCode === $.ui.keyCode.ESCAPE) {
self.$el.dialog("close");
if(self.options.closeFocusEl) {
$(self.options.closeFocusEl).focus();
}
evt.stopPropagation();
}
});
}

JQuery passing arguments for On Change for an item added to DOM via AJAX

I have multiple HTML fragments that are inserted into my DOM as the result of AJAX call-backs.
Each of these fragments will contain a text box whose class is "quantity".
What I want to do is to create an "on change" event handler that fires whenever one of these textbox's text value is changed. However, when that event is fired/handled, I need to know WHICH specific textbox was updated.
Okay, using jQuery, I have the following that fires in my "Lists.initHandlers" method:
$(document).on('change', $('#divABC').find(".quantity"), List.quantityChanged);
And my "List.quantityChanged" event handler happily fires when I update the quanity.
The problem is that when I reference "this" within the event handler, I get the whole document, and not the element that triggered the event.
I have tried to capture the element using syntax similar to:
$(document).on('change', $('#divABC').find(".quantity"), {ctrl: this}, List.quantityChanged);
but when I attempt this, the handler is never fired (even when I change the signature to expect an argument).
Any guidance here would be most appreciated.
Thanks
Griff
Try this:
$('.quantity').live('change', function(){
alert('New value: ' + $(this).val());
});
Pass this to your function:
$(document).on('change', $('#divABC').find(".quantity"), function () {
List.quantityChanged(this);
});

Event listener for multiple elements - jQuery

In the ASP MVC page I'm currently working on, the values of three input fields determine the value of a fourth. Zip code, state code, and something else called a Chanel Code will determine what the value of the fourth field, called the Territory Code, will be.
I just started learning jQuery a couple weeks ago, so I would first think you could put a .change event that checks for values in the other two fields and, if they exists, call a separate method that compares the three and determines the Territory code. However, I'm wondering if there is a more elegant way to approach this since it seems like writing a lot of the same code in different places.
You can bind a callback to multiple elements by specifying multiple selectors:
$(".field1, .field2, .field3").click(function() {
return field1 +
field2 +
field3;
});
If you need to perform specific actions depending on which element was clicked, another option would be to create a function which performs the actual computation and then invoke that from each callback.
var calculate = function() {
return field1 +
field2 +
field3;
};
And then invoke this function when on each click:
$(".field1").click(function() {
// Perform field1-specific logic
calculate();
});
$(".field2").click(function() {
// Perform field2-specific logic
calculate();
});
// etc..
This means that you do not repeat yourself.
This works for me
jQuery(document).on('scroll', ['body', window, 'html', document],
function(){
console.log('multiple')
}
);
Adding another possibility, just in cased this may help someone. This version should work on dynamically created fields.
$("#form").on('change', '#Field1, #Field2, #Field3', function (e) {
e.preventDefault();
console.log('something changed');
});

Cannot simply force focus on text input in stand-alone Google App using HtmlService?

I want to script a click on an input box.
Here is an example where the focus() should do just that, but it doesn't! Why?
Code.gs:
function doGet(e) {
return HtmlService.createHtmlOutputFromFile('myFile');
}
myFile.html:
<input type="text" id="new" onchange="adding(this)"/>
<div id="data"></div>
<script>
document.getElementById('new').focus();
function adding(a){
document.getElementById('data').innerHTML += a.value;
a.value = '';
}
</script>
I have also tried without success putting the focus() in its own function and having a body element whose onload calls that function.
What DOES work is having a button whose onclick calls that function, so focus() does eventually become active. Is there some other event I can use to trigger it?
I am using a Chromebook. Could that be the problem?
This is an intentional security decision in Caja. Certain functions that are prone (across the web) to serious malicious misuse, such as submit() and focus(), can only be executed while in the context of a user-initiated event (such as a button click).

Resources