I have a Dojo SubmitButton with jsId="saveParamButtonWidget". I overrided its onClick method by putting:
saveParamButtonWidget.onClick = editParam
I defined the editParam() function like this:
function editParam(eventObj) {
dojo.stopEvent(eventObj);
// ...
}
dojo.stopEvent() is supposed to stop event bubbling and default processing. However, the browser will submit the form anyway. I also tried with the following:
function editParam(eventObj) {
eventObj.stopPropagation();
eventObj.preventDefault();
// ...
}
Same thing. The only way I've managed to prevent form submission is by returning "false" from the event handler:
function editParam(eventObj) {
// ...
return false;
}
Can someone tell me why the first two ways did not work? Thanks.
Okay, after doing some digging through the source, I believe I can answer your question definitively.
The reason dojo.stopEvent() doesn't work, but return false does, is entirely due to how dijit.form.Button is coded. If you're interested, it's time for a little field trip. Keep your hard hats on.
When a dijit.form.Button is clicked...
The button's _onButtonClick method is invoked. (This is hooked up in the template, to the special ondijitclick event which captures not only mouse click but also certain keypresses, for a11y purposes.)
The _onButtonClick method first invokes the _onClick method, which, presuming the button is not disabled (which it's not in this case), invokes and returns the result of the onClick method. This is of particular interest since it's the method you're overriding!
Coming back to _onButtonClick, if _onClick returned precisely false (e.g. if your onClick handler returned false), _onButtonClick immediately bails out. This is why returning false makes your code work as desired. But what happens if it doesn't bail out there? Let's follow the trail further...
Next, _onButtonClick checks whether this button not a descendant of an actual HTML form, but is a descendant of a widget with an _onSubmit method (duck-typing). I'm assuming that in your case it is inside a real form (dijit.form.Form counts), so we'll skip over this. (I am under the impression that this code path wouldn't actually end up submitting, whereas yours apparently does.)
One final condition is checked: if the button has a valueNode defined (it does), the click method of this node is invoked. Unfortunately, this produces an entirely new event object on an invisible input type="submit" node under your form, and thus anything you tried to tell the original event is rendered immaterial, and the form goes on to submit! This is why dojo.stopEvent did not work - this code in dijit.form.Button pays it absolutely no heed.
I cooked this up as a somewhat-limited proof of concept (be sure to open firebug/etc. to get the logs): http://jsfiddle.net/Bf5H8/
Perhaps this is something that should be logged as a bug, but I suppose the initial thought may have been that supporting the well-known return false mechanism would be enough.
All this being said, it's quite possible that overriding onSubmit of the form is more in-line with your interests than overriding the button's onClick anyway (as S.Jones suggested), but at least this should solve the mystery.
Interesting question. +1
I believe you have to use dojo.connect to connect your function to a DOM event to get access to those methods with an event object.
See: The Event Object (DojoTollkit.org Reference Guide)
The Event Object
When you connect a function to a DOM
event with dojo.connect,
Dojo passes your function a normalized
event object. This means that,
regardless of the client's browser,
you can count on a set of standard
attributes about the event and a set
of methods to manipulate the event.
Assume that your function has been
called by dojo.connect and takes an
argument named event, like:
dojo.connect(dojo.byId("node"), "onclick", function(event){
// the var 'event' is available, and is the normalized object
});
...
Dojo normalizes the following methods with an event object:
event.preventDefault — prevent an event's default behavior (e.g., a link from loading a new page)
event.stopPropagation — prevent an event from triggering a parent node's event
Additionally, dojo.stopEvent(event)
will prevent both default behavior any
any propagation (bubbling) of an
event.
That said, placing a function like the one below in your form to perform some logic before submitting it, is a fairly clean, easily understood & maintainable approach.
<script type="dojo/method" event="onSubmit">
if (!this.validate()) { // or whatever else you'd like to evaluate
// insert calls here...
return false;
}
return true;
<script>
Cheers.
I had the same issue for using dojo.stopEvent
This issue is solved the form submission issue like this - here it is a simple form used to connect through dojo:
this.formId = dojo.byId("formId");
dojo.connect(this.formId, 'onsubmit', function(evt) {
var val_main = validate_this_form(0);
if(val_main == false)
dojo.stopEvent(evt);
});
Related
I have the following buttons:
<button id="abcd" onclick="something()">click</button>
and the following functions are attached to this button apart from the one in its html definition.
$('#abcd').on('click',function(){alert("abcd");});
$('#abcd').on('click',function(){
someAjaxCallWithCallback;
});
Now I want a new function with another ajax call to execute on this button's click, before the above mentioned functions. This new function determines whether the remaining functions would be called or not based on what data is recieved by the ajax call. That is, this pre function should complete its execution before giving control over to the rest of the functions and also determine whether they would run or not.
As an example, without changing the existing validation logics and button code, I have to add a new pre-validation function and similarly and post validation function.
I have a bindFirst method using which I can at least bring my new function to the beginning of the call stack but I have not been able to contain its execution and control further delegation because of callbacks.
If I understand correctly, you are looking for the way to do this, without modifying html and already existing js, only by adding new js-code.
First of all, if onclick handler is set and you want to control it, you should disable it on page load (maybe, saving it to some variable):
$(document).ready(function() {
var onclick = $("#abcd").attr("onclick").split("(")[0];
//to run it in future: window[onclick]();
$("#abcd").attr("onclick", "");
});
Edit: I changed my answer a little, previous approach didn't work.
Now you need to remove all already existing handlers. If number of handlers you want to control is limited, constant and known to you, you can simply call them in if-else after pre-validation inside your pre-function. If you want something more flexible, you are able to get all the handlers before removing, save them and then call them in a loop.
For that "flexible" solution in the end of $(document).ready(); you save all already existing handlers to an array and disable them. Then you write your pre-function and leave it as the only handler.
var handlers = ($._data($("#abcd")[0], "events")["click"]).slice();
$("#abcd").off("click");
$("#abcd").click(function() {
//this is your pre-func
//some code
handlers[1].handler.call();
});
Try console.log($._data($("#abcd")[0], "events")) to see, what it is.
Finally just run your post-function and do whatever you need, using conditions.
So, the general algorithm is as follows:
Disable onclick
Save all handlers
Disable all handlers
Run pre-func first
Run handlers you want to be executed
Run post-func
In fact, you just make your pre-func the only handler, which can run all other handlers you may need.
Although Alex was spot on, I just wanted to add more details to cover certain cases that were left open.
class preClass{
constructor(name,id){
if($(id) && $(id)[0] && $(id)[0]['on'+name])
{
var existing = $(id)[0]['on'+name]
$(id).bindFirst(name,existing);
$(id).removeAttr('on'+name)
alert("here");
}
if($._data($(id)[0],"events")){
this.handlers = $._data($(id)[0],"events")[name].slice();
}
else
{
this.handlers = null;
}
this.id = id;
this.name = name;
}
generatePreMethod(fn,data)
{
$(this.id).off(this.name);
$(this.id).bindFirst(this.name,function(){
$.when(fn()).then(execAll(data));
});
}
}
function exec(item,index){
item.handler.call()
}
function execAll(handlers){
return function(){ handlers.forEach(exec);}
}
This more or less takes care of all the cases.
Please let me know if there is something I missed!
Commuity,
I have a problem with a specific piece of code I am writing currently. The problem is that i want to delegate the animations. (E.g. I have 3 objects, the first is animated, after that the second and after that the third)
The point is I am trying to make it modular so i can't hard code anything in there.
On start we have following situation:
parentDiv is supposed to contain the objects you want to "fadeIn"
prevDiv is the object which was "fadedIn" recently
onclickDisabled is a class to remove "click" events temporarily
fadeInAnim is the class to trigger needed animation
function prepareChildrenForFadeInAnimation(parentDiv, prevId){
var object = 0;
parentDiv.children().each(function(){
$(this).addClass('onclickDisabled');
addAnimationAfterAnimationEnd($(this)[object].id, prevId);
prevId = $(this)[object].id;
});
}
function addAnimationAfterAnimationEnd(currentSelection, prevSelection){
document.getElementById(prevSelection).addEventListener("webkitAnimationEnd", triggerNextAnimation(currentSelection, prevSelection));
document.getElementById(prevSelection).addEventListener("animationend", triggerNextAnimation(currentSelection, prevSelection));
}
function triggerNextAnimation(selection, prevSelection){
selection = $('#'+selection);
selection.addClass('fadeInAnim');
prevSelection = $('#'+prevSelection);
prevSelection.removeClass('onclickDisabled');
}
I have a feeling that in addAnimationAfterAnimationEnd() the function triggerNextAnimation() is fired, but I don't have a clue how to prevent this since I need to submit parameters (or do I?).
If one of you guys could help me out I would be overjoyed!
PS: I am using "Chrome" and "Firefox" for testing.
The JavaScript at Question is calling the function triggerNextAnimation(currentSelection, prevSelection) immediately instead of referencing the function to be called when event is dispatche at the object.
You can use Function.prototype.bind() or $.proxy() to reference the function which should be called when the event is dispatched and with this set to a specific object and pass parameters.
Also, it is not necessary to use vendor prefix if you are already using jQuery. You can use .on("animationend")
document.getElementById(prevSelection).addEventListener("animationend",
triggerNextAnimation.bind(null, currentSelection, prevSelection))
The problem was that the function, which was supposed to trigger after certain animation, was simply executed.
document.getElementById(prevSelection).addEventListener("animationend",
triggerNextAnimation.bind(null, currentSelection, prevSelection));
The code above solves this problem. "Function.bind()" enables me to specificaly call the function after an event
I am trying to programmatically update a currency field to run the value changed event which holds a numeric calculation. I want the value to set to zero using something like.
$('.tester').igCurrencyEditor("setFocus");
$('.tester').igCurrencyEditor('option','value', 0);
Then when I blur out, or not sure what to do here, the valueChanged event should trigger as per the API docs (It can be raised on lost focus or on spin events).
But I can't seem to trigger the value changed event, it only works when I manually click into the input and change the number.
The valueChanging and valueChanged events would trigger when a user interaction changes the displayInput value of the editor, and the corresponding valueInput value is different from the display input one. The editors have adopted the same approach as all other Ignite UI controls where events do not trigger on API calls, because when an API call is performed, the developer can choose whether to invoke their event handler after the API call, or not.
There's two things that you can do to invoke your event handler. First one is to cache the event handler method and invoke it manually:
$('.tester').igCurrencyEditor({
...
valueChanged: valueChanged,
...
});
function valueChanged(event, ui) {
// event handler
};
$('.tester').igCurrencyEditor("setFocus");
$('.tester').igCurrencyEditor('option','value', 0);
valueChanged(null, { /* if you need arguments */ });
The second one is to extend the currency editor and override the method that performs the check whether these events should be triggered, and make it always trigger the events:
$.widget("ui.igCurrencyEditorExtension", $.ui.igCurrencyEditor, {
_processValueChanging: function (value) {
this._triggerInternalValueChange(value);
}
}
The second approach requires you to switch to using the igCurrencyEditorExtension and may cause side effects, as the method performs other checks as well.
Anyways, what Alex Marinov has suggested should work, but it depends on your editor configuration, depending on whether you've set nullValue, allow null values in the editor, etc.
you need a function like this:
function clearValue() {
$('.tester').igCurrencyEditor('option','value', "");
$('.tester').igCurrencyEditor('field').blur();
}
The result will be that the displayed value inside the currency editor is "$0.00" and the valueChanged event is fired.
I use last version free-jqGrig by Oleg.
I know that in versions, free-jqGrid, many other events are added in difference from jqGrid.
http://www.trirand.com/jqgridwiki/doku.php?id=wiki:events#list_of_events
Has re-read many similar answers, but events don't work for me.
jqGrid 'clearToolbar' without grid reload
Here something similar, but in an example an event when pressing the custom button.
It is necessary for me that when pressing on to ClearToolbar to add the custom check on event "jqGridToolbarBeforeClear" or "jqGridToolbarAfterClear".
The main reason of your problem is the usage of wrong event. The event jqGridToolbarBeforeClear will be triggered inside of the method clearToolbar, but you want to prevent processing of reloading of the grid inside of triggerToolbar. Thus you should use jqGridToolbarBeforeSearch event instead.
The mostly correct implementation of event handler jqGridToolbarBeforeSearch looks like the following:
$("#grid").on("jqGridToolbarBeforeSearch", function (e) {
var filters = $(this).jqGrid("getGridParam", "postData").filters;
if (typeof filters === "string") {
filters = $.parseJSON(filters);
}
if (filters) {
/* add here you custom tests */
return "stop";
}
return e.result; // forward the result of the last event handler
});
The main advantage of the usage events comparing to callback is the following: one can define multiple event handlers, but only one callback. If one event returns "stop" to prevent processing then the next event could overwrite the value with another value. To allow to stop processing in case of any event handler return "stop" one should use event.result in every event handler.
In ArcGIS JS API I need to trigger a method of my class when the processing after an extent change is complete. I didn't find any special event for this. extent-change triggers too early, when the data are not loaded yet. update-end is triggered also by the function I want to call, making an endless loop. So what I need is to link the two events together, the second one just once. But how?
Most of my events look like this:
this.events.push(on(this.map, "extent-change", lang.hitch(this, this._foo)));
This is not suitable for event linking, so I need to make the linked event some other way. What I've tried:
_foo: function () {
function fooEvent() {
this._bar();
dojo.disconnect(updateHandle);
}
var updateHandle = dojo.connect(map, "onUpdateEnd", fooEvent());
}
_bar is the method I want to run on the end of the extent change. However, this in the event handler means something else, not the class containing the function. I also tried the classname I declared in the declare statement, but with no luck.
_foo() and _bar() are in the same class (let's call it "foobar/baz"). However, inside of the fooEvent() is not its subclass as I hoped - when I try to use this.inherited within it, it's undefined. Another way I try is to add event argument to the handler, but it's undefined as well. So unless there is some better way, I need to understand how to get the object of "foobar/baz" class.
Another way I tried was to use lang.hitch once more, in one of the following ways:
//through the cluster event
var updateHandle = dojo.connect(map, "onUpdateEnd", lang.hitch(this, clusterEvent));
//or directly calling _bar()
var updateHandle = dojo.connect(map, "onUpdateEnd", { langh:lang.hitch(this, this._bar), disc:dojo.disconnect(updateHandle)});
//or through on, leaving a rouge event listener
dojo.on(map, "onUpdateEnd", lang.hitch(this, this._bar));
None of them returns any clear error and though the _bar() method seemed to work for some time, it doesn't work now - this is true for all three of the previous. I don't understand what these listeners do.
I solved this issue by flattening the event listeners. First, I made a flag _reloadFlag, initialized in the constructor as false and then changed to true whenever I want to call _bar, like in _foo. _bar now starts with a test of _reloadFlag either setting it to false or returning nothing. The event listeners now look like this:
this.events.push(on(this.map, "extent-change", lang.hitch(this, this._foo)));
this.events.push(on(this.map, "update-end", lang.hitch(this, this._bar)));