Onclick is triggered twice after changing the value manually in jquery - event-bubbling

I have changed the onclick(for li element) using the inspect element. In onClick I have called a function it was invoked twice.
Here is my sample code
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script>
function a(val)
{
alert(val);
}
</script>
</head>
<body>
<ul>
<li onClick="a(1)"> 1 </li>
</ul>
</body>
</html>

I assume you are using Google Chrome. This is a bug/problem with the Inspect Element feature.
If you use Inspect Element to change or remove the onclick handler of an element, it doesn't work as expected. The new Javascript code is ADDED as a new handler to the existing list of the onclick handlers, rather than replacing the existing handler of the old code.
This is incorrect and a bug, because Inspect Element allows user to replace the code, and it looks like it's replaced - but hidden from the user, it's actually appended as a new handler - so the result is not what it would seem like.
If the code or the onclick attribute is deleted, the JS code is still executed as it remains registered as a handler.
This is highly misleading and makes the Inspect Element nearly useless for debugging event handler JS bugs.
To try it out:
Right-click any element and click Inspect element (or Inspect).
Add or modify its onclick handler to alert('stack');.
Again, modify the onclick handler to alert('overflow');.
Now, click the element to fire the handler.
You may think that you will just get one alert with the word overflow. But you will actually get two alerts, one with stack and another with overflow. This means the code alert('stack'); is still in the handler, even though it's not visible on the DOM tree.
There is no permanent fix for this problem. The following workarounds may help. Reporting it to Google may encourage them to fix it in a future version of the browser.
Workarounds
Use Mozilla Firefox. The behavior of Inspect Element in Firefox is as expected - the browser really replaces the old handler instead of adding a new handler to the list.
If you need to use Google Chrome, do the following:
With the element selected in the Developer Tools window's Elements tab, click the Event Listeners tab (this is on the other pane which may appear below or on the right side).
Make sure that the Ancestors checkbox is unchecked.
If your element already has an onclick handler, you should be able to see a click event handler listed. If you don't see anything, try clicking Refresh button (next to Ancestors checkbox).
If you still don't see it, close the Developer Tools and right-click the element in the page and click Inspect to open it again.
Click the rightward arrow next to the click event handler. It will open and you will get a list of all the registered handlers (listed with the tag#id.class notation).
In the DOM tree (top pane or left pane), double-click and edit the onclick handler code and confirm it by pressing Enter.
In the Event Listeners tab, click Refresh button again. You will see that a new handler has been added.
Hover over the handlers and you will see a Remove button appearing on each of them. Click this button on each handler until only one remains.
The order doesn't matter - you can start at the bottom, the top or even go randomly - internally, the Remove button removes handlers in the order they were added.
If there's only one handler left, you can be sure that's your latest code - it doesn't matter which handler was left.
To change the onclick code again, repeat steps 5 to 7.

I notice this bug as well. While working on my project this week with ajax calls.
Having a button like this:
<button id="mybutton" type="button" onclick="sendMessage('12345')"></button>
If you go into the inspector and edit '12345' to 'abcde'. Then clicking the button will cause the button to fire twice. Once as '12345' and once as 'abcde'.
To solve the double sending you can attach the event listener with an ID rather than using html's onclick. So instead use something like:
$("#mybutton").click(function(){
sendMessage('12345');
});
This will not suffer the same double sending bug.
However, anything you edit on the inspector will not do anything now, it will be disregarded.

Related

Wicket AjaxLink javascript handler shows strange behaviour

I have a ListView that displays a list of Panels, one below the other. Every panel features a button (implemented via AjaxLink) that closes the panel and removes it from the list.
This is how the ListView is initalized and how the panels are created:
panelsList = new ArrayList<MyPanel>();
pnlContainer = new WebMarkupContainer("pnlContainer");
ListView<MyPanel> pnlItems = new ListView<MyPanel>("pnlItems", panelsList) {
#Override
protected void populateItem(final ListItem<MyPanel> item) {
item.add(item.getModelObject());
item.add(new AjaxLink<Void>("pnlClose") {
#Override
public void onClick(AjaxRequestTarget target) {
panelsList.remove(item.getModelObject());
target.add(pnlContainer); // repaint panel container
}
});
}
};
pnlContainer.setOutputMarkupId(true);
pnlContainer.add(pnlItems);
add(pnlContainer);
This works so far - the actions that trigger adding new panels (usually also AjaxLinks) do what they should and the new panel is added and displayed correctly. But I have problems getting the close button to fully work.
Please see the following steps:
1) I start the server and navigate to the main page. The ListView is initially populated with one panel.
Close-button-code of this panel:
<a wicket:id="pnlClose" id="pnlClose7" href="javascript:;">Close</a>
Searching the page code for pnlClose7 finds the following javascript code that makes the button work as expected:
Wicket.Ajax.ajax({"u":"./?0-1.IBehaviorListener.0-pnlContainer-pnlItems-0-pnlClose","e":"click","c":"pnlClose7"});;
Note: I do not press the button now, if i would, it would work as expected (thoroughly tested).
2) I trigger an action that opens a second panel. The panel is displayed below the first one as expected.
Close-button of the first panel:
<a wicket:id="pnlClose" id="pnlClosef" href="javascript:;">X</i></a>
Close-button of the second panel:
<a wicket:id="pnlClose" id="pnlClose10" href="javascript:;">X</i></a>
But now, neither searching for pnlClosef nor pnlClose10 finds some javascript code. The buttons (both!) do not work. I can still find the javascript code for pnlClose7.
3) I reload the page via pressing F5.
The button IDs change to pnlClose1a and pnlClose1b. Both IDs have javascript counterparts and work.
4) I press the first button (upper panel, ID pnlClose1a). The panel is closed as expected.
The remaining button's ID changes to pnlClose1c, again without a javascript counterpart. Javascript code for pnlClose1a and pnlClose1b is still present.
To make a long story short, the javascript handlers for my AjaxLinks seem to have shyness issues and only appear after I press F5 or reload the whole page in any other manner. I guess thats because repainting the pnlContainer changes the IDs of the current panels - but why is the linked javascript not updated at the same time? Is there anything I can change in my code to update the whole page without completely reloading it?
Wierd thing is that I am pretty sure this worked before... But I checked the whole class history and can't find any major change that would have triggered that. The ListView-code is mainly static since I added it.
I was had similiar problem. if you have any hardcoded javascript code in your page or panels html file (using <script> tag) remove it and set that js code in renderHead of your panel.

Multiple event handlers created when reopening fancyBox

On the calling page, I bind my fancyBox using an href, like so:
<a id="myId" href="myContent.cfm">Click me</a>
<script>
$(document).ready(function(){
$('a#myId').fancybox({
// my initialization params
});
});
</script>
In myContent.cfm, a default "filter" is built, which has add and delete buttons. Something like this:
<div id="fd_0" class="eachFilter blank">
<select name="filterBy" class="fl filterBy">
<option selected="selected">-- Add a Filter --</option>
<!--- add more options --->
</select>
<button type="button" class="addFilter default" title="Add a filter to the current filter set.">+</button>
<button type="button" class="deleteThisFilter default" title="Delete this filter from the current filter set.">-</button>
</div>
When the addFilter button is clicked, a new "default" filter is added to the dom after the filter that was clicked, using consecutive ids. Conversely, clicking the deleteFilter button causes that filter to be deleted and all remaining filters to have their ids renumbered; with the exception that there must be one filter remaining. My original code used .live() to attach event handlers to the newly created elements, like so:
$('.addFilter).live('click', function(){
// get number of existing filters
// create new blank filter
// add to the dom after the filter whose button was just clicked
});
$('.deleteThisFilter).live('click', function(){
// if there is more than one existing filter, use .remove() to remove the parent .eachFilter div
// renumber the existing filter ids consecutively
});
After the user has created all the "filters" they need, they may either "apply" them, which closes the fancybox and reloads a grid with the new, filtered parameters, or simply cancel and close the fancybox.
This all works fine the first time, and on reopening the fancybox, the initial blank filter's add button works as expected. However, after adding a second filter, any filter that was added to the dom has multiple event handlers added to the addFilter and deleteFilter buttons. If I added one filter the first time, then return to the fancybox the second time, then add a filter by clicking on the default filter's add button, then click on the newly created filters add button, two more filters are added. If I close, reopen the fancybox a second time, add a filter, and click on that filters add button, three more filters are added.
So here's what I've tried so far:
1) Changing the .live() calls to
$(document).on('click', 'addFilter', function(){ // add my filter code});
2) Putting the code to create the filters into a function, which at the end uses .bind() to add the event handlers to the newly created filters; followed by using
$('.addFilter').unbind('click', fnCreateMyFilter())
on closing the fancybox.
3) Using .live() ONLY on the newly created filter elements, and a regular click handler on the default element
4) Upgrading jQuery to 1.8.3 from our current version
5) Calling .remove() on all elements inside the fancybox .onClosed function (although I was under the impression that closing fancybox does actually remove the elements from the dom).
Any thoughts?
As always, it's the most obvious thing which isn't readily apparent. Moving the .js code out of the popup into its own file fixed the problem, which is something that I had intended to do after getting all the code to work.
I was using a combination of Fancybox2 http://fancyapps.com/fancybox/ and Noty popups http://needim.github.com/noty/ and having a similar problem.
I loaded a product edit form into a fancybox via ajax using class='fancybox.ajax' in the href link.
Everything saved fine when I clicked my save button until I reloaded another (or the same) product in fancybox.
I was using this code to trigger my save buttons:
$(document).on("click",".save_product_button",function(){
... post to ajax file to save info
});
Using that triggered multiple noty popups and saves (once for each time I'd loaded a fancybox since refreshing), because the save button was already loaded in the document model that many times. (I guess??)
But when I changed my on() to the save button's immediate parent, all my problems went away.
$("#productBox").on("click",".save_product_button",function(){
... post to ajax file to save info
});
Everything saved once from then on.
Plus, that should make the code a tad quicker.
Hope this helps someone not waste half a day like I just did.

mousedown event on options in select with jquery .on

I was reading the documentation of the .on event handler of jQuery and I started playing around with it.
I have a simple <select> element with the multiple attribute set to true.
However, I want the user to be able to select multiple items without having to press the ctrl or shift key(s)
According to the .on documentation, if you specify a selector it will automatically add those event handlers to any new items added inside that container that match the selector specified.
So in my case, I could for example decide to replace the <option> elements available in the listbox, but I still want to have the same functionality for those options without having to rebind those events etc.
One might think that the following snippet should do the trick (atleast, I did):
$('#dropdownlist').on('mousedown', 'option', function(e){
e.preventDefault();
$(this).prop('selected', $(this).prop('selected') ? false : true);
return false;
});
So when a users clicks on an option in that listbox, the default action will be cancelled and depending wether the item is already selected or not, it will invert that selection.
I have created a small fiddle demonstrating this behaviour: fiddle
In that fiddle, the first dropdownlist is behaving as expected, but the functionality is lost when replacing the items. This only works in FF & Chrome.
The second dropdownlist is how I thought it should've been (event handling wise), but that doesn't seem to work besides in Chrome -.-
The functionality is kept when replacing items, because the console will still log '2' when clicking on an item after replacing them.
Can someone explain me why this is happening? Am I doing something wrong?
Please point me in the right direction!
Thanks!
~ Dirk
IE doesn't respect the mousedown event on the option tag itself. You've got to use the OnChange event of the select tag, which is not what you (nor I) want. I was trying to do the exact same thing as you and was stopped at every attempt. I finally gave up and made it a list of checkboxes because I can get the same behavior out of mine that you are trying to do here.
You can see this question for some options, but I never did get it to work the way you are describing.

JavaScript: How to force a page reload on reset?

I have a page with some generated HTML that survives the form's reset button. It is a problem because that HTML is inconsistent with the values in the cached default form.
In principle I guess it could be solved easily if I could force a hard reload from the server when the user presses the reset. However I see that the Chrome browser does not support the onReset event (in fact it is deprecated in HTML5).
But perhaps I could work around the missing onReload event. Can I re-define what happens when the reset button is pressed? In my case the apply and reset buttons are located in general HTML templates which I cannot change. Can I attach a function to the button from JavaScript?
You can replace the "reset" button , by a regular button.
And use the "onClick" event, to trigger a page reload.
EDIT
oops I missed the template part,
You can add a function to a button from Javascript.
First you need to "get" the button, with something like document.getElementbyId('resetButton');
If the button doesn't have a ID, you still can to retrieve it by doing javascript dom traversal
then you can add a function like :
var resetButton = document.getElementbyId('resetButton');
resetButton.onclick= reloadPage;
function reloadPage(){
window.location.reload();
}

Unable to call getValues() on an Ext Js FormPanel on initialization of a container Panel

I have an Ext Js panel that I am adding to my main TabPanel. The panel I am adding contains a FormPanel as one of it's items and inside the FormPanel I have a Name field. What I want to do is change the name of the Tab based on the name in the form field.
The problem is that if I call the FormPanel's getForm().getValues() inside of the panel's initComponent, I get the following javascript error:
Uncaught TypeError: Cannot read property 'dom' of undefined
If I do this outside of initComponent (e.g. on a button press) everything works fine. After doing some testing, I think the issue is that the FormPanel isn't actually rendered yet (and thus the dom doesn't exist), getValues() fails. However, I can't seem to figure out a way to get my FormPanel's values from the Panel on load.
I tried to listen for events. I tried:
this.detailForm.on('afterrender', function () { alert('test'); });
but doing this showed that AfterRender is called prior to the form actually being rendered (it's not visible on the screen). Changing the alert to my custom function handler produces the previous dom exception. I attempted to use the activate and enable events instead of afterrender, but even though the API says that FormPanel fires those events, the alert('test') never gets called.
I can't seem to find any way for my panel to get the inner FormPanel's values upon loading my panel. Does anyone have any ideas?
Using getFieldValues() in place of getValues() will collect values by calling each field instance's getValue() method instead of by reading from the DOM. This should allow you to get your values regardless of the form's rendered state.
I've got the same problems on one of my projects, I managed to fix it using the afterlayout event.
I'd give setting .deferredRender:false a try.
Ext.TabPanel.deferredRender
Probably best to roll out of your afterlayout changes, then test with just a straight deferredRender:false config item.
I believe the problem is caused because the inactive tabs are not rendered until they become active. In your scenario, you cannot get the values, because they don't exist until the tab is activated/shown.
Setting deferredRender:false will render the items within all tabs. There could be a performance hit by setting deferredRender:false, so testing you must do.
Hope this helps.