So lets say I have a button #button. I want it to toggle visibility of some element #element. So with plain jQuery I would do
$("#button").on("click", function() {$("#element").toggle();})
Or with an explicit side-effect:
var visible = true;
$("#button").on("click", function() {visible = !visible; $("#element").show(visible);})
What would be the equivalent of this in Bacon.js. I would assume it's possible to do without any side-effects, but I can't figure it out how.
EDIT: Let me clarify: without any side-effect which aren't a part of Bacon.js-objects.
The docs give an almost literal example on how to do that with .assign and $.fn.asEventStream:
$("#button").asEventStream("click").assign($("#element"), "toggle");
Caveats: with an event stream you can't use the Property::assign method, yet onValue works the same way. Also we want to ensure that toggle isn't invoked with the event as an argument, so you'd rather use
$("#button").asEventStream("click").onValue($("#element"), "toggle", null, null);
For explicit state, we use the scan method:
$("#button").asEventStream("click") // a stream of click events
.scan(true, // iterate it, starting with true
function(visible, e) {
// ignore the event parameter
return !visible; // just toggle the boolean
}) // now we've got a property of a changing boolean value
.assign($("#element"), "show");
I'm not entirely positive about this, but it appears that Bacon.js relies on either jQuery or Zepto to function properly. From the API Documentation on GitHub:
API Creating streams
$.asEventStream(eventName) creates an EventStream from events on a
jQuery or Zepto.js object. You can pass optional arguments to add a
jQuery live selector and/or a function that processes the jQuery event
and its parameters, if given, like this:
$("#my-div").asEventStream("click", ".more-specific-selector")
$("#my-div").asEventStream("click", ".more-specific-selector", function(event, args) { return args[0] })
$("#my-div").asEventStream("click", function(event, args) { return args[0] })
For what its worth, here's a fiddle that shows how you would do this in vanilla JavaScript:
http://jsfiddle.net/cs01rm3v/
Related
Is there any way to get the list of all event listeners of an element on the HTML page using JavaScript on that page.
Note: I know we can see them with Chrome dev tools event listeners but I want to log/access see list using the JavaScript of the page.
Also, I know we can get them through jQuery but for that, we also have to apply the events using jQuery, but I want something that would be generic so I could also access the event listeners applied to other elements such as web components or react components.
If you really had to, a general way to do this would be to patch EventTarget.prototype.addEventListener:
const listeners = [];
const orig = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(...args) {
if (this instanceof HTMLElement) {
listeners.push({
type: args[0],
fn: args[1],
target: this,
});
}
return orig.apply(this, args);
};
document.body.addEventListener('click', () => console.log('body clicked'));
console.log(listeners[0].fn);
click this body
To find listeners attached to an element, iterate through the listeners array and look for targets which match the element you're looking for.
To be complete, also patch removeEventListener so that items can be removed from the array when removed.
If you need to watch for listeners attached via on, then you'll have to do something similar to the above to patch the HTMLElement.prototype.onclick getter/setter, and for each listener you want to be able to detect.
That said, although you said you want a generic solution, rather than patching built-in prototypes, it'd be better to add the listeners through jQuery or through your own function.
What I did when I had a similar problem is add a data attribute when the listener was set, so I could identify it later.
At the end of the function that adds the listener:
elm.setAttribute('data-has_mask', true);
At the beginning of that same function:
if("true" == elm.getAttribute('data-has_mask')) {
return;
}
Maybe not exactly what the OP is looking for, but I was having a lot of trouble with this, and this is an obvious solution for a particular use case, and I guess it might help someone out.
I am trying to get access and modify this function (second one) in jqueryUI. I have tried everything. What I want to do is to add something in the function. I know it is possible and I need to do something like this :
var snapIt = $.ui.draggable.prototype.drag;
$.ui.draggable.prototype.drag = function() {
console.log("hello"); // exemple of a thing I want to add
// Now go back to jQuery's original function()
return snapIt.apply(this, arguments);
};
On top it will get the function add in console "hello" and then continue normally with the rest of the jQuery function. But I just can't find this function. I know this doesn't work: $.ui.draggable.prototype.start and dozens of others I tried.
$.ui.plugin.add("draggable", "snap", {
start: function( event, ui, i ) {
click.x2 = event.clientX;
click.y2 = event.clientY;
var o = i.options;
i.snapElements = [];
$(o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap).each(function() {
var $t = $(this),
$o = $t.offset();
if (this !== i.element[0]) {
//...........
I don't want the drag: function(event, ui) {..... I need to modify the function because I use ui.position = {left..... and it make the snap method not work. The only way was to change the drag method. I know it work because I tried manualy. But changing the library might be problematic for futur dev.
Don't know if I am clear but basically I want the path to $.ui.plugin.add("draggable", "snap", {//stuff}); in jqueryUI library
Thx in advance
There are 3 different sources of behaviors that are called on the different events in jquery-ui, each with its own structure.
First you have the "private" functions, that are defined on the prototype and that are called directly on native events. These are on $.ui.draggable.prototype and begin with a _ character. For example you have $.ui.draggable.prototype._mouseDrag function.
These are called directly and are the ones triggering the events. They are not directly accessible from the options.
Then you have the plugins functions. These are the ones that are added using add. Basically what add does is that it sets functions to be called on the events that are accessible via the options. And these plugins callbacks are called if their corresponding option is true. The structure goes like this:
Each plugin is an object that defines a callback for different
events. The events available are the same that are accessible in the options. For draggable, you have start, drag and stop.
These callbacks are pushed in arrays contained by
$.ui.draggable.plugins object, in which each property is one of the available event.
A function goes through the event array and validates if the plugin
should be ran based on the option set.
Once the plugins are done, the options callbacks are called. These are the ones that you set in the options.
So depending what ou want to modify, you can either change the prototype:
$.ui.draggable.prototype._mouseDrag
Or you can add a plugin. Like this:
$.ui.plugin.add( "draggable", "customPlugin", {
drag: function(event, ui, draggable){
console.log("I'm the custom plugin");
});
Or you can modify snap plugin. This one is a bit more complicated, and much less reliable since the functions are stored in arrays and not in an object, and they are added. The structure goes like this:
Each property key is an event, and every property is an array of
arrays.
Each of the array first element is the name of the option associated
with the callback, that is the second element of the array.
So the drag callback associated to snap is $.ui.draggable.prototype.plugins.drag[2], because it's the third callback that's been added to drag event. $.ui.draggable.prototype.plugins.drag[2][0] is the string "snap", which is used to check if the option was set to true. And the callback is $.ui.draggable.prototype.plugins.drag[2][1]. So you can modify it like this:
$.ui.draggable.prototype.plugins.drag[2][1] = function(){
console.log("I'm the modified plugin");
}
If you want a better control, you can iterate through $.ui.draggable.prototype.plugins.drag array and check the first element to make sure you modify the proper plugin.
Obviously, as you tried, you need to store the original callback if you want the behavior to work.
See here how this goes:
$.ui.plugin.add("draggable", "customPlugin", {
drag: function() {
console.log("%c I'm a custom plugin", 'color: blue');
}
});
var _temp = $.ui.draggable.prototype.plugins.drag[2][1];
$.ui.draggable.prototype.plugins.drag[2][1] = function() {
console.log("%c I'm the modified snap plugin drag callback", 'color: red');
_temp.apply(this, arguments);
}
$('div').draggable({
snap: true,
customPlugin: true,
drag: function() {
console.log("%c I'm the options callback", 'color: green');
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script type="text/javascript" src="//code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
<div>Drag me</div>
<div>Or me</div>
Is there a way in Javascript to have a delegate like the ones in c# ?
Example in c#
Object.onFunctionCall = delegate (vars v) {
Console.WriteLine("I can do something in this private delegate function");
};
I would like with my Javascript to have my main object do something over a long time, and shot a delegate once in a while to give a little update. All that without having to change the code itself of my class to adjust for the webpage.
function mainObject() {
this.onUpdate = function() { //Potentially the delegate function here
}
}
var a = new mainObject();
a.onUpdate = Delegate {
$(".myText").text("Just got a delegate update");
}
I dunno if it's clear enough.. havent found ressources on this so I suppose there is just no way to do so ?
NOTE: I am not looking into jquery Click delegates event here, but into delegating a function call like how it works in c#
Let me know
Although the original question was ansered by solving the root problem (observer - pattern) there is a way to implement delegates in JavaScript.
The C# delegate pattern is available in native JavaScript using context binding. Context binding in JavaScript is done with the .call method. The function will be called in the context given by the first argument.
Example:
function calledFunc() {
console.log(this.someProp);
}
var myObject = {
someProp : 42,
doSomething : function() {
calledFunc.call(this);
}
}
myObject.doSomething();
// will write 42 to console;
What you are looking for is an "Observer Pattern", as described eg. here.
But as you are interested in jQuery, you don't need to go the trouble of writing an observer pattern for yourself. jQuery already implements an observer in the guise of its .on() method, which can be invoked on a jQuery collection to cause callback function(s) to fire every time a native or custom event is dispatched.
Here's an example :
$(function() {
//attach a custom event handler to the document
$(document).on('valueChange', function (evt) {
$(this).find("#s0").text(evt.type);
$(this).find("#s1").text(evt.value);
$(this).find("#s2").text(evt.change);
$(this).find("#s3").text(evt.timestamp).toLocaleString();
});
//customEvent(): a utility function that returns a jQuery Event, with custom type and data properties
//This is necessary for the dispatch an event with data
function customEvent(type, data) {
return $.extend($.Event(type||''), data||{});
};
//randomUpdate(): fetches data and broadcasts it in the form of a 'changeValue' custom event
//(for demo purposes, the data is randomly generated)
function randomUpdate() {
var event = customEvent('valueChange', {
value: (10 + Math.random() * 20).toFixed(2),
change: (-3 + Math.random() * 6).toFixed(2),
timestamp: new Date()
});
$(document).trigger(event);//broadcast the event to the document
}
});
Here's a demo, complete with "start" and "stop" buttons for a regular "interval" dispatch of the custom event.
Notes
Under some circumstances, it might be more appropriate to broadcast the event to the four data spans individually.
On the web, you will find mention of a more convenient jQuery.event.trigger({...}) syntax. Unfortunately this was an undocumented feature of jQuery, which disappeared at v1.9 or thereabouts.
JQuery has great support for custom events - .bind("foo", function(e).... However what if the mechanic of triggering the event is not ready yet and has to be constructed only on those elements that have the event bound on?
For example I want a scrollin event that gets fired when an element is scrolled into a viewport. To do this, I would onscroll have to check all the elements and trigger scrollin on those that were outside the viewport and now are inside. This is not acceptable.
There are some tricks to speed it up. For example one of the plugins for this checks all the elements in "private" $.cache and does the checking only on those that have scrollin event bound.
But that's also ugly. What I need is an additional callback for the binding of the event (additional to the callback for handling) that would take care of the scroll management, that is to put the element(s) into some elementsCheckOnScrol cache array.
I'm looking for something like:
$.delegateBind("scrollin", function(jqSelection) { ... });
element.bind("scrollin", function(e) {..}); //Calls ^ after internal bind management
Edit: This would be nice api!
$.bind("bind", function(onWhat) { ... })
:-)
If I'm not misunderstanding you, you could patch the bind method like this:
(function($) {
var oldBind = $.fn.bind;
$.fn.bind = function(name) {
if(name === "scrollin") {
delegateFunction(this);
}
oldBind.apply(this, arguments);
};
})(jQuery);
What it does is checking whether a scrollin is being bound, and if so, calls your delegate function. After that it simply calls the original bind function which does all jQuery things like it does regularly.
After having added this code, you could use it like this: http://jsfiddle.net/pimvdb/g4k2G/.
function delegateFunction(selection) {
alert(selection.length);
}
$('a').bind('scrollin', function() {});
Note that this does not support object literals being passed to .bind (only (name, func)), but you could implement that as well.
I found an $.event.special API, but I don't know "how much" public it is. It is not in the docs and has been changed at least once before. http://benalman.com/news/2010/03/jquery-special-events/
I have a jQuery plugin that needs to register a click event handler:
$.fn.myPlugin = function (options) {
var settings = {
// snipped
};
$.extend(settings, options || {});
$("body").click(function () {
// Do Something
});
// Rest of the plugin
});
The problem is that multiple invocations register the function more than once. Since the function needs to stay attached, I can't use .one().
Is there a way if a function is already attached? Can I give it a name or so? Or do I have to set some boolean flag using closure magic?
Namespace your events.
$('body').unbind('click.myPlugin').bind('click.myPlugin', function() {
..code...
});
More on Namespaced Events.
A very easy method with good performance would be to set a data element on the body element:
if (!$.data(document.body, 'myPluginRegistered') {
$.data(document.body, 'myPluginRegistered', true);
// do your plugin code here
}
Easiest might be the boolean plus closure magic you suggested. Alternatively, you could get the list of all functions bound to "click" object, and see if the function you're attaching is there already.
This question shows how to get the list.
List all javascript events wired up on a page using jquery
Though, the namespace suggestion that came in after I first responded is probably simpler.