onChange event firing incorrectly after being loaded programmatically - javascript

I have an APEX application where there are many drop down items. I've bound change event handlers to them using the bind function of jQuery.
Now when I load the content of a drop-down programmatically using $('#ELEMENT').trigger('apexrefresh'), the drop-down reloads but the change event handler fires automatically.
How do I prevent this from happening? I tried avoiding binding the event handler using bind and instead adding the onChange attribute to the element. The incorrect behaviour was still present.
Here is the skeletal code:
$(document).ready(function()
{
$('#P7021_MSG_DEF').bind('change', function(e)
{
console.log('bound function onChange() msg_def');
updateStartWord();
}
);
});
function updateMsgDef()
{
console.log('function updateMsgDef() ');
$('#P7021_MSG_DEF').one('apexafterrefresh', function()
{
if( $x('P7021_RESTORE_CHK').value == 'Y')
{
setdefault('P7021_MSG_DEF', vJson.msg_def);
}
updateStartWord();
}
).trigger('apexrefresh');
}
In the above code, when the updateMsgDef is called from another function the function updateStartWord() gets called twice - once by updateMsgDef() itself and again by the onChange handler that was bound to P7021_MSG_DEF item.
If anyone could help on this?

Calling $('#ELEMENT').trigger('apexrefresh') is going to trigger the change event. Short of going back to the drawing board altogether, the solution is going to be a hack whatever you do. You could poke about in (and quite possibly break) Oracle's javascript. You could write your own AJAX to populate the select list.
The easiest way might be to check in your onChange event which element currently has focus, eg:
onChange = "if($( document.activeElement).attr('id')=='YOUR_PAGE_ELEMENT')
{ $( document.activeElement).trigger('apexrefresh'); };"
If the user has changed the select list, it should still have focus. There's no guarantee that will work in all browsers, but I think it should be ok in current Chrome and IE versions.
I've been in a similar situation to yours, and have come to accept that if the page logic is too complicated to implement using DAs, maintaining it is likely going to be a nightmare whatever happens. Much as I like "proper" programming, Apex is really all about the declarative controls.

Related

Method for triggering certain click event using .trigger() but suppressing all others

My situation is that I am trying to trigger a single event using the jQuery .trigger() method. However the element I am triggering has multiple click event listeners.
Actually finding what these listeners are and what they trigger from the source code is probably not viable as its included in the sites main JS file and its all minified and pretty much unreadable.
At the moment I know that the element when clicked performs some kind of ajax call and loads more data into the DOM of the page (which is what i want to trigger), however it also displays an overlay (which is what I want to suppress temporarily).
As its just an overlay there are workaround I can make; using a display:none on it straight after click etc. However it would be much more elegant if i could somehow suppress all click events on this element except the desired event.
Any ideas if this is actually possible? And if so how I would go about it?
You need to register your own event at the top of the event chain. And cancel the event chain in your event. Here is a solution with writing a custom jquery extention.
$.fn.bindFirst = function (which, handler) {
var $elm = $(this);
$elm.unbind(which, handler);
$elm.bind(which, handler);
var events = $._data($elm[0]).events;
var registered = events[which];
registered.unshift(registered.pop());
events[which] = registered;
}
$("#elm").bindFirst("click", function(e) {
// edit: seems like preventing event does not work
// But your event triggers first anyway.
e.preventDefault();
e.stopPropagation();
});
Reference:
https://gist.github.com/infostreams/6540654
EDIT:
https://jsfiddle.net/8nb9obc0/2/
I made a jsFiddle and it seems like event preventing does not work in this example. There might be another solution.

Wicket - IE8 - Javascript event listeners arent executed, when tags are dynamically added over ajax

I have spent several hours, maybe days stucked on a very weird problem :(
I am creating an application that is based on the Wicket solution. It works perfectly in IE9,IE10, Chrome and FF. Strange is, that i have tested it in IE8 too and it works in 99% of cases (IE instances on different computers + totally identical version of IE8) too. But now the PROBLEM.
PROBLEM:
I am creating dynamic content over AjaxLink button. After clicking the button the WebMarkupContainer model is changed and WebMarkupContainer is refreshed (based on Ajax, so the page isnt reloaded complete, but only the container is).
Every item in the container has added AjaxFormComponentUpdatingBehavior. In onComponentTag method, i add tag.put("onchange","some jsFunctionCalling....");. The problem is, that after clicking on the item, no event is invoked. I have tried add the onchange listener over .add(new AttributeModifier.....), but the result is still same. As i have said, i tried the same code in the same version of IE on another PC and it works perfectly. Interesting is, that after refreh of the page everything work perfect, until new item to WebMarkupContainer is added. After that no item listeners work until the page is refreshed again.
One of the latest idea, that i got is, that problem isn't in the code, but in the settings of IE (maybe security). Have anybody any idea? What setting could be set different and cause these problems? Is there any settings on Wicket site, that can solved this? Is there some setting that can blocked registration of these listeners to DOM, if they are added dynamically over ajax?
I didn't tried it but IMHO there are three options you can try:
Instead of adding "onchange" by yourself, add OnChangeAjaxBehavior and make all work in wicket. Downside is server roundtrip on every event.
Add data-attributes (AttributeModifier.append("data-param1", "foobar")) to push your parameters into html and call ajaxRequestTarget.appendJavaScript("attachOnChangeHandler()"); after the click event on the AjaxLink. attachOnChangeHandler() should be your js function to add onchange handler to every item which needs it. And over data-attributes you can access your parameters.
Since Wicket 6: To avoid mixing to much js with Wicket, you could subscribe to one of the global AJAX events.
The solution in your case would be almost the same as in 2. Just add a listener in js for "/ajax/call/success"
(see if the call relates to your component by checking the id) and add the onchange handler there.
This is IMHO the best solution without mixing custom js with Wicket.
The solution provided by #peterchon (attaching event handlers higher in the DOM than the elements which are going to be replaced by wicket) would work in every other case, but you have "onchange" which applies only to input, textarea and select elements.
BTW the page is "working" after refresh, since the whole page is rendered and browser can properly attach the handlers.
You can try this method:
/* this will catpure the target that triggered the event */
function getEventTarget( e ) {
e = e || window.event;
return e.target || e.srcElement;
}
function doSomething( e ) {
var that = getEventTarget( e );
if( that.tagName.toLowerCase() === 'a' ) { // specify the target, in this cas <a>
// Do something
}
}
parentElement.onclick = doSomething;
This script basically will capture any event, then will pass the variable of target to the function that will perform something.
Hopefully this will work for you.
You try to achieve something using a non-wicket JavaScript/Ajax way. This is fine, but also makes it very messy.
Please check this fine article about passing parameters from JavaScript to wicket and vice versa. I think it will suit your needs.
http://wickedsource.org/2013/01/07/rolling-your-own-ajax-behavior-with-wicket/

jquery event added multiple times

I have a fairly large javascript class that generates an complete ajax-generated application. In one version of the ajax page there are a number of dropdown menus. These menus can get created and destroyed at various points during the life cycle of the application.
This is the behaviour I see:
User opens page version 1: no dropdowns
User goes to page version 2: dropdowns added with jQuery onchange event. Work as intended.
User returns to version 1 of page, dropdowns removed.
User returns to version 2 of page, dropdowns added again (using same element IDs)
dropdowns will now have 'double' event handling, triggering the event for each onchange.
The behaviour I'm struggling with is as follows.
On the initial page load, I add an onchange event:
function myClass(){
//Initiate once for current and future elements.
jQuery(document).on('change',".mydropdowns",
function(e){
self.submitDescriptionChange(this);
}
);
}
myClass.prototype.submitDescriptionChange = function (el){
doSomeAjaxStuff();
}
This works fine, except that each time the user goes to pages version 1 and returns to page version 2, the event gets multiplied. Very quickly you can end up with the event firing 20 times per change event, which in this case creates 20 ajax calls.
Logically, by using jQuery.off() I should be able to avoid this. But what happens instead is that the event is removed from both past and future elements, which means that when I recreate page version 2, the dropdowns won't work.
Every way I have tried this (and I've tried LOADS), I either end up with no event firing, or multiple events firing. I cannot seem to find a way to add/replace the elements whereby the event is only ever fired once.
Any ideas how I can solve this?
UPDATED
Yeah, so it turns out I misdiagnosed the problem. It actually came from repeatedly rebinding a 'hashchange' event, rather than rebinding the onchange event. Apologies for misdirection. Moving to bind() function to somewhere where it only executed once fixed the issue.
Since you do not want .off() to remove your events from other pages, I would suggest using namespaces for your events. For example, something like this:
function myClass(pageno) {
var pref_ev = 'mypage' + pageno + '.' + 'change';
$(document).off(pref_ev).on(pref_ev, ".mydropdowns", function(e) {
self.submitDescriptionChange(this);
});
}
This way, each page will have its own "change" event such as "mypage1.change". The event is still registered normally as a change event; the prefix namespace "mypage1" is used to only perform the .off() call on the right events.
I am not sure what plugin you are using for your dropdown menus but there should be a "destroy" method on that plugin. If you call that when removing the dropdowns that should work. Also, if you are only hiding the second page and not actually removing it from the DOM you dont have to re-invoke the plugin as the plugin will still be saved on the element.

how to prevent mutiple registration of a single event handler in jquery

I have a <div> box displaying search message and some radio button for recent message. There is link option for slide toggle.
When you click on that link it will show some input field and check box and radio button. And at the same time the text of link change to hide option. If you click on that it will hide all the input and checkbox option.
When I refreash the whole page its working properly but when that paticular box or part is refreashing then the box is hiding and imediately hides. If you refresh that portion n number of times the box is going on toggling continously. I think the problem is in registration of event handler. So please give me some solution.
CODE :
$(document).ready(function() {
$(".SideBar-blockheader1").click(function() {
e.preventDefault();
$(".SideBar-blockcontent1").slideToggle("fast");
});
$(".SideBar-optionheader").click(function() {
$(".SideBar-optioncontent").slideToggle("fast");
$(this).text($(this).text() == $("#hideopt").attr('value') ? $("#showopt").attr('value') : $("#hideopt").attr('value'));
});
$(".SideBar-optionheader").text($("#showopt").attr('value'));
$(".SideBar-optioncontent").hide();
});
jQuery has a method, called data() which can be used to extract the attached handler information of an HTML element. You can see if the element has already a click handler, and if it has, then stop re-attaching another handler to it.
if(typeof $('#id').data('events').click == 'object')
{
// A click handler is already attached
}
else
{
// No click handler; Attach one;
}
Although you haven't provided code, I suspect you are using .click(). For jQuery 1.7+ you should be using .on() in delegate mode (the element you bind to is an ancestor, not the clickable element itself) or .delegate() if pre jQ 1.7.
For example:
$('someAncestor').on('click', 'a.specialLink', function(event) {
event.preventDefault();
// the rest of your code for the click handler
})
"someAncestor" is any valid selector that is an ancestor of your link that will not be destroyed, rebuilt, or otherwise manipulated after the DOM is built. It doesn't have to be the direct ancestor.
[updated below after seeing code sample and comments]
There are a few things going on. First, .on() will only work if you're using jQuery 1.7+. Next, .on() can be invoked a few different ways (I wrote about it here: http://gregpettit.ca/2011/jquery-events-its-on/) and you need to be invoking it while delegating an ancestor listener, not simply as a substitute for click. Next, you haven't provided code for your attempted update, only for the original code; it's hard to tell what "didn't work" about trying to use .on(). Moving along, I'm not actually sure what this line is meant to do:
$(this).text($(this).text() == $("#hideopt")...etc...
I can't think of why you would want to try to treat a jQuery object as a variable. I'm not saying the code is wrong, I'm just saying I don't get it. Also, I hate ternary operators... which is part of the reason I don't get it. I much prefer readable conditionals. ;-)
Next, you're calling preventDefault() on "e" but you're not passing "e" into your functions. You might just be getting a JavaScript error, period. (e is undefined)
Then there's attr("value") which I believe should actually work. But why not use .val() if it is indeed a node that HAS a value attribute?
Finally, there is tonnes of room for caching your objects. Every time you see that an object is being used more than once, you can benefit (to varying degrees of performance and legibility) from caching it. I have not updated the code with any caching, though-- that's really something for a whole other "How can I best cache my objects?" question.
Anyhow... to solve the problem, you first have to choose a valid ancestor. This can be any ancestor that isn't destroyed during the process of loading in your new data. This could be anything, but the closest ancestor is the best. It might be a section wrapper, but if you're truly desperate it could be a page wrapper or even the body tag. If you bind to document, you're reproducing the deprecated .live() function, which I definitely recommend against. I have used a placeholder selector, ".section" but you need to figure out what an appropriate ancestor is on your page.
$(document).ready(function()
{
$(".section").on("click", ".SideBar-blockheader1", function(e)
{
e.preventDefault(); // probably not necessary if there's no default click behaviour
$(".SideBar-blockcontent1").slideToggle("fast");
});
$(".section").on("click", ".SideBar-optionheader", function(e)
{
e.preventDefault(); // probably not necessary if there's no default click behaviour
$(".SideBar-optioncontent").slideToggle("fast");
$(this).text($(this).text() == $("#hideopt").val() ?$("#showopt").val() : $("#hideopt").val());
});
$(".SideBar-optionheader").text($("#showopt").val());
$(".SideBar-optioncontent").hide();
});

Html Select List: why would onchange be called twice?

I have a page with a select list (ASP.NET MVC Page)
The select list and onchange event specified like this:
<%=Html.DropDownList("CompanyID", Model.CompanySelectList, "(select company)", new { #class = "data-entry-field", #onchange = "companySelectListChanged()" })%>
The companySelectListChanged function is getting called twice?
I am using the nifty code in this question to get the caller.
both times the caller is the onchange event, however if i look at the callers caller using:
arguments.callee.caller.caller
the first call returns some system code as the caller (i presume) and the second call returns undefined.
I am checking for undefined to only react once to onchange, but this doesnt seem ideal, whcy would onchange be called twice?
UPDATE:
ok, found the culprit! ...apart from me :-) but the issue of calling the companySelectListChanged function twice still stands.
The onchange event is set directly on the select as mentioned. This calls the companySelectListChanged function.
..notice the 'data-entry-field' class, now in a separate linked javascript file a change event on all fields with this class is bound to a function that changes the colour of the save button. This means there is two events on the onchange, but the companySelectListChanged is called twice?
The additional binding is set as follows:
$('.data-entry-field').bind('keypress keyup change', function (e) { highLightSaveButtons(); });
Assuming its possible to have 2 change events on the select list, its would assume that setting keypress & keyup events may be breaking something?
Any ideas?
ANOTHER UPDATE:
The select element looks fine and all works if I remove the additional 'change' binding. when the 'change' binding is added that event fires once and the hard-wired 'onchange' is fired twice.
If both events are bound via jQuery all works ok, it seems that mixing hard-wired onchange and jquery bound change events cannot be mixed? I guess this answers my question but seems like an issue with IE and binding these events with jquery.
I agree with your assessment. I've updated my small example at http://gutfullofbeer.net/onchange.html to include a jQuery handler in addition to the DOM 0 handler (the one set with the "onchange" attribute). It appears to be a jQuery bug, or at least a failure of jQuery to deal with the IE weirdness.
I logged jQuery ticket 6593.
I also encountered this issue when using IE8 and made some changes to fix this.
Instead of specifying the onchange event on the dropdownlist, I used the jquery.change() event.
Markup:
#Html.DropDownList("drpList", myList, new { #class = "myClass", id = "drpList" })
Script:
$(function () {
$('#drpList').change(function () {
.... your functionality
});
});
This works for me.. Hopes this help.

Categories