I created a simple javascript that uses jquery.tools.min.js library.
My javascript looks like this:
function testFunction() {
$("img").click(function() {
alert("Handler for .click() called.");
});
console.log("what's up?");
}
$(testFunction);
So when I try it on a simple HTML page, I get my alert message when I click on an Image.
I added the exact same javascript in my GWT application.
When the application loads I see the console.log message, but nothing ever happens when I click on any image on my app.
Why is that so ? Is it because the testFunction() doesn't apply to the dynamically created images ? Or is it because the event was overriden by GWT ?
Thank you.
EDIT
I tried recalling my function after I create my content:
public native final void recallFunction() /*-{
$wnd.console.log('again1');
$wnd.testFunction();
$wnd.console.log('again2');
}-*/;
I can see my log messages but no click event is fired.
EDIT 2:
When I run :
$("img").click(function() {
alert("Handler for .click().");
});
or
testFunction();
directly in Firebug's console, the event is correctly attached to my images!!
I tried also calling testFunction() in the window's onload event but with no better luck.
I assume you include jQuery library itself and it is 1.7 or later.
If your images do not exist in the DOM when at the time testFunction is defined use "on()".
function testFunction() {
$("img").on('click',function() {
alert("Handler for .click() called.");
});
console.log("what's up?");
}
When using jQuery in JSNI code, you must use $wnd.jQuery or $wnd.$ instead of $. See also this question on Google Groups.
Instead of Writing JSNI over Jquery you can easily start of with gwtquery http://code.google.com/p/gwtquery/
You will benefit from actually from both Jquery approach and also GWT performance optimization .
GwtQuery benefits over Jquery -
http://manolocarrasco.blogspot.in/2011/01/gwtquery-css-selectors-performance.html
jQuery vs GQuery Benchmark
Also if it is only nice scroll you are trying to achieve you can take a look -
GWT CustomScrollPanel example
and
Custom Scrollbar in GWT
Are you adding any other event listeners (in gwt) to the image in question?
There can only be one on-click handler, and if you are setting them in both enviros, you will run into hurt.
Gwt-Query, as mentioned in another comment, is kind enough to delegate back to gwt internal events (since it knows the syntax for event handlers).
You may want to use firebug object inspection on your image.
Try console.log($("img")) and see what you get.
Look for __listener variable on your images; that's the gwt listener. Check out DOMImplStandard ~line 200 to see what happens when you sink an event in gwt.
I'm not sure how jquery attaches events, but I did see this in the mozilla spec:
"If multiple identical EventListeners are registered on the same EventTarget with the same parameters, the duplicate instances are discarded. They do not cause the EventListener to be called twice, "
https://developer.mozilla.org/en/docs/DOM/element.addEventListener
Related
...yes I know... Blazor Server is served side, and I can handle DOM events with my C# code at server side... still it makes sense to add a pure client side javascript event handler and surprisingly it seems it is not possible, because _framework/blazor.server.js swallows them...
Here are the simplest isolated steps to reproduce what am I doing.
Create a Blazor App using VS 2019 template, choose Blazor Server App in the next step.
In Index.razor add a line anywhere <div id="test">Hello, click me!</div>
In _Host.cshtml add the following script after or before the already existing <script src="_framework/blazor.server.js"></script> line:
... pulling my hair out, I can not insert code block, here, see after the step 4...
Run the app, and press F12 to see the console output.
here is the script in step 3:
<script>
console.log("script is executing...");
console.log("element found, its innerText is: " + document.getElementById("test").innerText);
document.getElementById("test").addEventListener("click",
function() {
console.log("click handler called");
});
</script>
Question
Am I missing something? Is it possible to make this work, without a server roundtrip? If it is not possible, then many of the existing javascript widgets, and jQuery plugins how will initialize themself?
What I've tried so far?
Use other event for example mouseup: does not work.
Change the order of my custom script and the <script src="_framework/blazor.server.js"></script> : neither order is working...
Comment out the line <script src="_framework/blazor.server.js"></script> for diagnostic reasons: The event handler now called and I see the console output in the browser: "click handler called"
To answer in advance "why I would like to do this?":
I would like for example implement a DOM behavior where clicking on an element it changes something in its attributes. I do know, I can attach a C# handler, then call a back javascript function but I would not like this roundtrip. Besides of that, maybe there are many existing javascript plugin and lib which relies on that its initialization script attaches event handler on DOM.
...based on the documentation what agua from mars pointed to in his comment:
(If anyone has better idea then please answer or comment)
If I run my client side script which is attaching the event handlers after the server OnAfterRender event, then it works. If I run it any way before (for example in OnInitialized event) it will not work:
So here is a working solution:
Index.razor
#code
{
protected override void OnAfterRender(bool firstRender)
{
_jsRuntime.InvokeVoidAsync("attachHandlers");
}
}
_Host.cshtml:
window.attachHandlers = () => {
document.getElementById("test").addEventListener("click", function()
{
console.log("click handler called");
});
};
Is it considered bad practice to use jQuery's .on() event handler for every event?
Previously, my code contained script like this:
$('#cartButton').click(function(){
openCart();
});
However I've recently started using InstantClick (a pjax jQuery plugin).
Now none of my scripts work. I understand why this is happening, but I cannot wrap my code with the InstantClick.on('change', function(){ tag as this means my code starts to repeat itself. For example, clicking on the cart button will run the openCart() function many times. So to get around this, I'm changing all my functions to something like this:
$(document).on('click', '#cartButton', function(){
openCart();
});
I'm curious as to whether this will increase loading times and cause excess strain. Is it bad practice to use the on() event handler for every event in my code?
It's not bad practice at all..
.on is the preferred method for handling all events, and using .click is just a shortcut that gets passed to the .on method anyway..
If you check out here (unminified source for jquery 2.1.0): https://code.jquery.com/jquery-2.1.0.js
Here are a couple notes:
search for this line: on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
This is the function definition for the on method and just shows you what the code is doing..
also search for this line: jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick "
Th code below this line is mapping all the directly callable shortcuts (like click) and shows you that they are just mapping to the 'on' method.
Hope this helps!!!
No it is not a bad practice to use .on(), actually if you check the source of the .click() function, you'll see that it actually calls .on().
But... Instead of creating an anonymous function, you should simply do this, which would be cleaner, and slightly faster:
$(document).on('click', '#cartButton', openCart);
and
$('#cartButton').click(openCart);
Consider an element <img id='hitMe' /> which is referenced in a jQuery function with either $('#hitme').click() or possibly $(document).on('click', '#hitme'). How can I find these functions in the Javascript source code, other than grepping for the string hitme? Consider that grepping is not feasible as the string may have an arbitrarily large amount of references, and there may be a very large number of imported Javascript files.
Perhaps there might be a way to have Firebug step into Javascript functions that are run, after page load, on a line-by-line basis? Consider that I don't know where to set a breakpoint because I don't know which function is run.
Here's a solution that catches the moment in which the event is trigger (dispatched) rather than the moment in which the listener is being added (on/click).
It can be done by overriding jQuery.event.dispatch.
for example:
(function($){
var old = $.event.dispatch;
$.event.dispatch = function(event){
if(event.type=='click'){
var handlers = ( $._data( this, "events" ) || {} )[ event.type ] || [];
console.log('handlers', handlers[0]);
}
old.apply(this, arguments);
};
})(jQuery);
In this example I chose to print the content of the function to the console, but it could also be useful to debug it like Omri Sivan suggested, with debugger;. With Chrome you should be able to right click on the function to "Show function definition".
Chrome or Chromium can help you there. Make a right click on the element you want to explore, chose "Inspect element", have a look into the right panel which starts with "Computed Style", "Style"... and at the bottom of it you will find the section "Event Listeners", which gives you information about what events are listened and where in the code it was appended.
I recommend Visual Event bookmarklet. What follows, is a shameless copy-paste from the site:
Visual Event is an open source Javascript bookmarklet which provides debugging information about events that have been attached to DOM elements.
Visual Event shows:
Which elements have events attached to them
The type of events attached to an element
The code that will be run with the event is triggered
The source file and line number for where the attached function was defined (Webkit browsers and Opera only)
http://www.sprymedia.co.uk/article/Visual+Event+2
Try the following (I'm using Chrome/Chrome Dev Tools):
Override jQuery's on/click function with something like:
var old = $.fn.on;
$.fn.on = function() { console.log('called on'); debugger; old.apply(this, arguments);}
Every call to .on() should now pass through you (synchronously) and pause at the 'debugger;' statement.
Once paused, look under 'Call Stack' and locate the function you are looking for (e.g. the function that invoked .on()). You can also add some condition to the overriding function above to make sure not every call to .on() pauses.
I am trying to set an event in JavaScript but it is not really working. I am pretty sure I am doing it correctly too.
// in index.htm:
function foo() {
// Load gets code from a php file via ajax
document.getElementById('div').innerHTML = load('phppage.php');
document.getElementById('element').onClick = func;
}
// in lib.js:
function func() { alert('Working'); }
Unfortunately... it never alerts 'Working'. I have Google Chrome and I even inspected the element in the developer tools and found that the onClick property was infact func()... I don't understand why this wont work.
I do have extensive use of ajax. The element 'element' is actually loaded with ajax
Try changing it to "onclick"
HTML attributes are not, but javascript properties are case-sensitive. You need to use onclick to set the event handler.
I think the onclick property is lowercase.
However, you should use event attaching methods.
element.addEventListener('click',func);
(and attachEvent for Internet Explorer)
Of course, there are plenty of frameworks out there to handle the cross browser issues for doing this, such as:
ExtJS
JQuery
This is actually a bigger question because I know there are several ways to solve this problem but I will try to sum it up.
What I try to do: I am using this jQuery plugin to upload files via Flash http://www.uploadify.com/. However, the element #fileInput that I supposed to bind this function to is a live element which is generated after the page loaded: $('#fileInput').uploadify(). The reason #fileInput is a live element is because I use FancyBox to popup a DIV and this FancyBox basically just "cloned" the inner html of the DIV.
What happened: When I clicked "BROWSE" to upload a file, there is no progress bar for upload. The reason is because the Uploadify could not bind to live elements.
Questions:
1. I tried to replace bind() with live() in uploadify code but that did not work because bind() allows to pass [data]. The LiveQuery plugin http://docs.jquery.com/Plugins/livequery does not have the same syntax as bind() either. Is there anything similar to bind but works for live elements?
If I don't try to replace bind() function and keep uploadify code the same. Does anyone know how to change code in FancyBox so that it WILL NOT make a clone to generate live elements? I know this is a hard question too.
Note: FancyBox site seems dead --> http://www.visual-blast.com/javascript/fancybox-jquery-image-zooming-plugin/
Thank you very much!
You might consider changing the FancyBox code to support calling a callback function after it clones the HTML. Then, put the uploadify() call in the callback function.
You could overload the live method, making it support data as the second parameter:
jQuery.fn.live = (function(_live){
return function( type, data, fn ) {
var _fn;
if ( jQuery.isFunction(fn) ) {
_fn = function(e) {
e.data = data;
return fn.call( this, e );
};
}
return _live.call( this, type, _fn || fn || data );
};
})(jQuery.fn.live);
Replacing all instances of bind(...) with live(...) should now work.
Note: you'll have to put the overloaded method above everything else.
From my experience , the only way I have found to do this is by using livequery
It has a similar syntax, and in your case to bind uploadify on a live element, you would use
$('#fileInput').livequery(function(){
$(this).uploadify();
})
Livequery accepts functions without events, and executes them everytime there is a change in the DOM
How is the element generated? If its fetched from the server using jQuery you can use a more hackish way of fixing it, simply put jQuery runs eval() on any script tags it runs into so you could just put:
<script type='text/javascript'>
$(function(){
$('#fileInput').uploadify();
});
</script>
In the fetched html and it'll bind it on load instead of trying to watch over it live. Bonus points, if you fetch the html again it'll be unbound.