How can I add a onclick method using js_of_ocaml? - javascript

Hi suppose I have a button with id form-submit and I have the following function in my OCaml file:
let () =
let form_submit = Dom_html.getElementById "form-submit" in
...
What is the right way to add a on_click method to the button? Should I just do something like this?
form_submit##onclick <- ...
But whats the type of the handler function? I cannot find it in the documentation.

You can use the dom stuff manually, but it's not very convenient. The right way is to use the Lwt_js_events modules that builds up abstraction over event handlers using Lwt.
Here is a minimal example:
Lwt.async (fun () ->
Lwt_js_events.clicks form_submit
(fun _ev _thread -> Lwt.return () )
)
First parameter is the event object, second is the loop thread. You can tweak the behavior of the handler quite finely using the general construct at the beginning.
Lwt.async here ensure that the handler will be put in place "when it's good" (in this case, when the lwt scheduler is launched). It's better to use that than to ignore the result, as it ensures proper scheduling.

Related

Access parent scope inside child element's onclick callback (Atom Package)

I'm not sure how you're normally supposed to handle this within Atom Packages, but this is the situation:
I created a beautiful button
playButton = document.createElement('button');
playButton.onclick = #funkyFreshCallback;
This will call my callback defined as so
funkyFreshCallback: (event, element) ->
console.log(#);
Now, what I want is to be able to use the element's parent scope.
See the # symbol? Right now it returns the button element (because that's the current this, I know)
Now how do I access the scope instead? The same one that defined funkyFreshCallback?
Also, it really doesn't feel like this is how I should be doing things but I haven't found much documentation... at all.
You can bind the parent context onto the function before setting it up as the event handler.
playButton.onclick = #funkyFreshCallback.bind(#);
Another way to avoid the context issue is to setup a function that calls funkyFreshCallback. The event handler should only handle the event and then pass along the minimum set of required data to the callback, like so:
playButton.onclick = (event, element) =>
timePressed = Date.now();
#funkyFreshCallback(timePressed);
With the code above, funkyFreshCallback will be called with the context that you expect, and won't receive a really complex event object as an argument. This helps to make writing unit tests for funkyFreshCallback easier, and it should simplify your callbacks, because they won't have to deal with events.
More on that in Chapter 7 of Maintainable JavaScript

Initializing jQuery.load'ed elements

I'm loading part of my webpage using AJAX, in particular jQuery.load(). With this the usual jQuery pattern
$('.classname').click(...) // Handler
// or, working with bootstrap
$("a[rel=tooltip]").tooltip() // Function
or similar obviously don't work any more, because they are called only when the page is loaded. I realize there is jQuery.on for the first example, but how would I implement the second?
Is there a simple (builtin) way to also apply these to jQuery.loaded stuff, or do I have to work around it myself? Seems like a problem a lot of people should be having.
You have to work around it yourself; but you can easily to this by calling $("a[rel=tooltip]").tooltip() in the callback for load():
$('#blah').load('/somewhere.html', function () {
$('#blah').find('a[rel="tooltip"]').tooltip();
});
Be sure to restrict the tooltip() call to only newly loaded elements, or you'll end up initializing tooltip multiple times per element (which may result in weird behaviour).
To avoid duplicating code you just need to define a helper function;
function initTooltip(root) {
return $(root || document).find('a[rel="tooltip"]').tooltip();
}
Which allows you to init tooltip. The parameter is optional, and lets you restrict initialization to only descendants of the provided element, e.g.:
jQuery(document).ready(function ($) {
initTooltip(); // equivilant to initTooltip(document);
$('#blah').load('/somewhere.html', function () {
initTooltip('#blah');
});
});

Get value of current event handler using jQuery

I can set the onclick handler using jQuery by calling
$('#id').click(function(){
console.log('click!');
});
Also using jQuery, how can I get a reference to the function which is currently handling the click() event?
The reason is that I have another object and want to set its click handler to the same one as #id.
Update
Thank you for all the suggestions. The problem is that I do not know which function is currently handling the clicks. Keeping track of it would add state to an already complicated template-editing system.
jQuery's .click(function) method adds the function to a queue that is executed on the click event~
So actually pulling out a reference to the given function would probably be hairy-er than you expect.
As noted by others, it would be better to pass in a reference to the function; and then you already have the reference you need.
var clicky = function () { /* do stuff */ };
$('#id').click(clicky);
// Do other stuff with clicky
Update
If you really really need to get it out, try this:
jQuery._data(document.getElementById('id')).events.click[0].handler
Depending on your version of jQuery that may or may not work~ Try playing around with
jQuery._data(document.getElementById('id'))
and see what you get.
Got the idea from this section of the source:
https://github.com/jquery/jquery/blob/master/src/event.js#LC36
if you dont know the name of the function you can use
args.callee
https://developer.mozilla.org/en/JavaScript/Reference/Functions_and_function_scope/arguments/callee
function clickHandle(e){
if($(e.target) == $('#id')) {
$(newTarget).bind('click', clickHandle);
}
}
$('#id').bind('click',clickHandle);
I think this would be the most symantic way of going about it

Can you bind to jQuery events with plain JavaScript?

I'm working on a project where a number of different companies are working on the same site.
The main developer have set up an event - let's call it init - which indicates the page is ready for our code to execute.
They're basically calling it like this:
$(window).trigger('init');
For a number of reasons I won't go into here, we prefer to avoid using jQuery in our own code wherever possible. I tried to bind to it like this:
window.addEventListener('init', function (event) {
alert('hehehehe');
});
But that doesn't seem to work. This works perfectly, though:
$(window).bind('init', function (event) {
alert('hehehehe');
});
Does jQuery use special event objects by default that you can't bind to with plain JS? Am I just doing something stupid?
The docs for bind seem to contain the answer:
Any string is legal for eventType; if the string is not the name of a native DOM event, then the handler is bound to a custom event. These events are never called by the browser, but may be triggered manually from other JavaScript code using .trigger() or .triggerHandler().
There's no native DOM event called 'init':
http://en.wikipedia.org/wiki/DOM_events
Hence "These events are never called by the browser, but may be triggered manually from other JavaScript code using .trigger() or .triggerHandler()"

dojo how to override dijit class method

I need to override onClick method in the Tree.js. Is there any common way to override dojo/dijit classes methods?
I'm a bit confused, since you were already doing this in the last question you posted.
You've got a few choices, depending on what you want to do.
Clobbering method stubs
In the case of true stubs like onClick, it's potentially as easy as clobbering that method on your widget instance.
Programmatically:
var myWidget = new dijit.Tree({
...,
onClick: function(item, node, evt) {
/* handler code here */
}
};
Or declaratively (this is exactly the same as what you were doing in your last question):
<div dojoType="dijit.Tree" ...>
<script type="dojo/method" event="onClick" args="item,node,evt">
/* handler code here */
</script>
</div>
Connecting to method invocations
In other cases, perhaps you need to do something whenever a given method gets called, in which case you could use the widget's connect method (which is a nicer version of dojo.connect that will automatically clear itself when the widget is destroyed). In that case you could do something like this:
Programmatically:
//execute the given function whenever myWidget's onClick method is called
myWidget.connect(myWidget, 'onClick', function(item, node, evt) {
/* handler code here */
});
Declaratively, this can be done very similarly to the above, except instead of type="dojo/method", make sure you use type="dojo/connect"
<div dojoType="dijit.Tree" ...>
<script type="dojo/connect" event="onClick" args="item,node,evt">
/* handler code here */
</script>
</div>
Note that when you connect like this, your code will execute after the method whose invocation you are connecting to. If you need to do something before, your best option is probably to dojo.declare your own extension to the widget. If you need to go that far, I'll elaborate, but I think you'll likely be set with one of the above options.
Edit #1: Connecting the dots (no pun intended...oh heck, yes it was)
Since it seems that my comment appended to my answer to the original question was somehow not clear enough, here's a code block modifying the original code in that question based on the simple two steps exactly as I explained in that comment. The only tiny wrinkle is that the arguments passed to _onClick are slightly different.
<div dojoType="dijit.Tree" ...>
<script type="dojo/connect" event="_onClick" args="node,evt">
/* handler code here. In this case, item is accessible via node.item */
</script>
</div>
This solution may feel a bit sub-optimal since it involves connecting to a method that's suggested to be private. However, it's a way that should work regardless of whether openOnClick is true or not. If you are certain you're going to have openOnClick set to true, you could potentially write a single function, then connect it to both onClick and onOpen instead (both get passed the item, then the node).
Edit #2: Common functions, connecting programmatically
To answer your follow-up questions, I'd like to actually address them in reverse order - since if you are interested in connecting programmatically, it will actually make the other question easier to answer.
So first, to answer your connect question: you definitely don't want to be using dojo.byId, as that's not giving you the Tree widget, that's giving you some DOM node (probably the topmost) associated with the widget. As a general rule, dojo methods don't know anything about dijit stuff.
What you do want to be doing, is what I suggested above. Here it is applied as per the code you attempted. Also note that onClick has a capital C - another general rule: widget events use camel case notation, as a way to distinguish them from plain DOM events which don't.
var mytree = dijit.byId("mytree");
mytree.connect(mytree, "onClick", function(item) {
/* ... */
});
Now, to take that a step further and resolve your other inquiry, if you want to bind some common functionality to onClick and onOpen and onClose, you could define a function first, then connect it to both. This is one of the many things that makes JavaScript awesome - the availability of functions as first-class objects that can be easily passed around.
function handleClick(item) {
/* do stuff here.
Inside this function you can assume 'this' is the tree,
since connect will ensure it runs in-context.
*/
}
var mytree = dijit.byId("mytree");
mytree.connect(mytree, "onClick", handleClick);
mytree.connect(mytree, "onOpen", handleClick);
mytree.connect(mytree, "onClose", handleClick);
Now there's one important remaining question: where should we do this? The best place is likely inside a function passed to dojo.ready (or dojo.addOnLoad, same thing, ready was added as a synonym in 1.4) so that it will run only after:
The DOM is parsed by the browser
All dojo.required modules have loaded
If you set parseOnLoad: true in djConfig, all widgets defined in the document's HTML will already be instantiated
So, sometime after your dojo.requires, in script, you'd do this:
dojo.ready(function() {
/* code from previous example goes here */
});
Give it a shot.
Also, if you're interested in a bit of reading, I've actually written about a couple of topics I touched on in this edit:
Of Dijits and DOM Nodes
dojo.required Reading
You could use:
dojo.connect(tree, 'onClick', function(item) {
/** Your Action **/
});

Categories