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>
Related
I've inherited some JS (that I can't change) that fires a bunch of events:
jQuery(document).trigger('section:' + section);
// where "section" changes dynamically
And I want to observe for ALL of these events, and parse out the value for section, and do something different depending on it's contents.
If it didn't change I could do this:
jQuery(document).on('section:top', doStuff );
But how do I observe an event if I only know the first part of that event name?
You cannot listen for all events in the style of $().on('section:*'), unfortunately. If you can change the code, I would do the following:
jQuery(document).trigger({
type: 'section',
section: section
});
Then you listen for it and don't need to parse anything out
jQuery(document).on('section', function(e){
if (e.section === 'top') {
// Something happened to the top section
}
});
If you want to minimize your code changes, leave the old event in there, that way existing code will be unaffected.
A different approach would be to use event namespaces.
jQuery(document).trigger('section.' + section);
jQuery(document).on('section', function(e){
if (e.namespace === 'top') {
// Something happened to the top section
}
});
I, however, prefer the first approach because event namespaces are most commonly used for a different purpose: to be able to remove events without being forced to keep a reference to the handler itself. See http://css-tricks.com/namespaced-events-jquery/ and http://ejohn.org/apps/workshop/adv-talk/#13. I prefer to use styles that other developers are used to, if they do the job.
I'm really not sure about your use case but you could overwrite $.fn.trigger method:
(function ($) {
var oldTrigger = $.fn.trigger;
$.fn.trigger = function () {
if (arguments[0].match(/^section:/)) {
doStuff(arguments[0].split(':')[1]);
}
return oldTrigger.apply(this, arguments);
};
})(jQuery);
var section = "top";
jQuery(document).trigger('section:' + section);
function doStuff(section) {
alert(section);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
Here's what I ended up doing.
It's a combination of Juan Mendes's solution, and using a method from the prototype library
Originally, there was a function that ran this code:
myObject.adjustSection(section) {
jQuery(document).trigger('section:' + section);
}
// I couldn't edit this function
So I extended the function with prototype's wrap method, since my project used prototype as well as jQuery.
// My custom function wrapper
// extend adjustSection to include new event trigger
myObject.prototype.adjustSection = myObject.prototype.adjustSection.wrap(
function(parentFunction, section) {
// call original function
parentFunction(section);
// fire event w/section info
jQuery(document).trigger({
type: 'adjustSection',
section: section
});
}
);
Then, it runs the original one, but also fires my custom event that includes the section info.
Now, I can do this to observe that event and get the section type:
jQuery(document).on('adjustSection', function(event) {
event.section; // contains the section I need
});
Of course, this means I have to utilize both prototype and jquery within the same scope, which isn't the best thing in the world. But it worked.
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/
I'm trying to create an algorithm for an event based editor like in StarCraft 2 Editor that can support:
Create UI
Play sounds
Handle keyboard/mouse inputs
Display messages
Button(or some referenced UI object) is pressed etc.
Pretty much the same thing as in StarCraft 2 Editor (of course not the 3D stuff too)
So far I'm thinking to use JSON , add every event in an object and then loop through them and create an event using the addEventListener() method.
The JSON Events Object(of course it will be created by the user in the editor with no programming):
var Events={
//your event's names here
onReady:{ //on page ready to manipulate
displayMessage:{//just a simple popup
text:"Hello user!",
title:"Welcome!",
type:"normal",
},
createButton:{ //creates a buton on the screen
text:"Click me!",
id:"myButton"
}
},
onClick:{
id:"myButton" ,//the id of the button we just created
actions:{ //the actions applied after we click the button
displayMessage:{//just a simple popup
text:"You pressed me!",
title:"Button",
type:"error",//show the message as an error
}
}
}
}
I found some softwares (GameMaker,Construct 2,GameDevelop) that have an event based editor if you would like to get an idea about what I'm talking about (if you don't already know about StarCraft 2 Editor)
My question is:
What is the best algorithm that I can use to achieve this?
Sounds like a job for jQuery UI.
When the user creates a custom area in your editor all it's attributes are stored inside an object (that you can save as JSON) that would then be applied to a div as param when loading the map (using html-attributes.
function create_areas(areas){
var map = $('#map_area');
for(var i=0;i<areas.length;i++){
map.append($('<div>', area[i].params));
}
}
whereas params would look something like this:
params = {
width: 100,
height: 200,
....
mousedown: function(){ play_music('hello'); },
keydown: function(e){ alert('you pressed ' + e.keyCode; }
}
also the jQuery UI tools like draggable and resizeable should ease up building your editor.
I'd model this more after backbone's event system:
events: {
'click selector': handler,
'mouseover selector': handler2,
...
}
Handlers can be any javascript function, this would allow you to create a bunch of pre-defined functions like displayMessage.
Then you could curry your own handlers, which would allow your users to specify configuration if they need it.
Example:
var events = {
'click element': displayMessage({
text:"Hello user!",
title:"Welcome!",
type:"normal",
}),
'mouseover pizza': createButton({...})
}
function displayMessage(options) {
var options = options;
return function() {
//display message logic
}
}
Then you can supply a compose function among other helpers (look up promises perhaps?) to combine your functions together:
var events = {
'click element': compose(
displayMessage({
text:"Hello user!",
title:"Welcome!",
type:"normal",
}),
createButton({})
),
'mouseover pizza': createButton({...})
}
This could work out?
Caveat: it might be better if events was an array that contained objects. That way you can have multiple click handlers on some selector without collisions.
The way I see this there are really severall choices you need to make. I would, although I prefer JSON as a data construct not limit myself to this subset of an actuall programming language. And engener this the other way around.
You have events, handlers and options. Where a option, or better a option list is the user inputed data, the handlers are the actual action, and the events are triggers to set some action off.
If you read this carefully you will notice this is the exact description of the basic structure of most jQuery-Scripts or Event-Driven Software in generall. Only the users options in jQuery are (since it is a DOM Framework) most often the context of a single DOM-Element. So, here we are and I would suggest to simply borrow the theorie behind this and make use of promisses wich make a very clear and great way to generate code!
So my call to any event chain would look like this.
...when(chainObject['event'])
.then(function(event) {
//call handler
handlers[chainObject[selectedHandler]].call(event.context, chainObject['options']);
//apply next element(s) in chain, this is the current promise
appendNextElement(chainObject['followingHandlers'], this);
})...
Notice how apply makes it easy for you to change the environement and in turn behaviour of any hanlder based on what the user and event did. And promisses make error handling very easy!
This of course applies to only one node in your chain. So what should a data structure look like to let you generate this kind of code?
One node in your structure would look like this:
{
event: 'click',
selectedHandler: 'sohwText',
options: {
'text': 'helloWorld'
},
followingChain: {...OTHER HANDLERS....}
}
The important thing to notice is that like a good structured functional programm you are looking at a tree and not at a simple list of events. So every actual DOM Element holds many of these
var eventTree = {
'.someButton': [..Handlers of this button...],'
'.someOtherButton': [..Handlers of the other button...],
}
And there we go. You have a context (the button), a event, user input and a handler.
The resulting app should not only work, but will be styled for any experienced JavaScript-Programmer to expand or mod.
I'm using JS and jQuery for the first time after a lot of experience with Java and C++. I'm loving jQuery's idea of $(document).on('click', 'btn-selector', react), but for more complex widgets I'm finding myself in the same rut over and over: in each react handler, I have to look up the widget as a whole and reconstruct all my knowledge about it.
For example, I'm making a simple widget out of <input>s with which the user can make a grading scale: 90 maps to an A, 80 maps to a B, etc. When one of the inputs changes, I want to check to make sure that the inputs are still in order (your scale can't go 90, 70, 80, for example).
So, I have something like
Actual
$(document).on('click', '.scale-input', function() {
var widget = $(this).closest('.scale-widget-container');
ensureLevelsAreInOrder(widget);
});
Almost every single handler has to have this first line to find its context. I'd much rather have code that looks like this:
Preferred
$(document).on('click', '.scale-input', ensureLevelsAreInOrder);
The problem is that in this form, ensureLevelsAreInOrder only has a reference to the input that changed, not the larger context.
In Java or C++, I would have called a constructor on the widget, and each input would have a handler with the context baked in via member variables. I could do something similar with
$(function() {
$('.scale-widget-container').scaleWidget();
});
with scaleWidget() setting up the contextualized handlers, but the page I'm working with loads a lot of its html with ajax and I don't have a reliable time to run that initialization.
Is this a common problem that we just have to deal with if we don't want JS in our HTML, or is there a solution I haven't come across yet?
Not sure what it is you're after exactly, but you don't seem to touch on two quite important concepts when it comes to JS: the event object, and closures. Both of these are open to you to get what you need:
event object:
The callback function is passed an argument, that describes the event itself, and references the elements affected by that event, This isn't exclusive to jQ (just google addEventListener), but it's quite handy:
$(document).on('click', '.scale-input', function(e)//<-- e is our event
{
console.log(e);//check console
});
Which, in vanilla JS would look like this:
document.addEventListener('click', function(e)
{
if (!e.className.test(/\bscale\-input\b/))
{
return e;
}
console.log(e);
}, false);
Another thing you might want to consider is enclosing references to whatever it is you need in an IIFE's scope:
(function()
{
var containers = $('.scale-widget-container'),
localBool = false,
asMany = 'varsAs you need',
previousScales = [],
inputs = $('.scale-input');//references to all DOM nodes you mention
$(document).on('click','.scale-input',function(e)
{
console.log($(this));
console.log(containers);
previousScales.push(this.value);//or something
console.log(previousScales);
//and so on.
});
}());
Hope this helped
Update:
If IE isn't a browser you don't care about that much, you could use one of the DOM-modified events, specifically DOMTreeModified:
(function()
{
var nodes = [];//<-- store current nodes here, if applicable
nodes.containsNode = function(node)
{
var i;
for (i=0;i<this.length;i++)
{
if (this[i] && this[i] === node)
{//node is set, return its index
return i;
}
}
//node not found, return -1
return -1;
};
document.body.addEventListener('DOMSubtreeModified',function(e)
{
var all = document.getElementsByClassName('scale-input'),
i;
for (i=0;i<all.length;i++)
{
if (nodes.containsNode(all[i]) === -1)
{
nodes.push(all[i]);//add new
}
}
},false);
}());
More on the mutation events, and their issues, on the DOM events wiki
According to the jQuery plugin development guides from the Internet, the common practice of developing a jQuery plugin would be:
(function($) {
$.fn.myplugin = function(options){
//...
//Plugin common characteristic
//e.g. default settings
//...
//Attach to each desired DOM element
return this.each(function(){
//Instantiation stuff...
});
}
})(jQuery);
$(document).ready(function(){
$(".someclass").myplugin();
})
It seems to me that, if the elements with class "someclass" have been attached to another plugin, once those elements are going to attach to "myplugin", they will lose the original relationship to the previously attached plugin. I'm not sure if my thinking is completely correct. Please advise if any mis-understood.
Thank you!
William Choi
An element isn't "attached" to a plug-in. A plug-in just adds further methods to the jQuery wrapper for a matched set of elements. So just as the jQuery wrapper has parent and find, it also has the plug-in's myplugin method. These can all coexist as long as there are no naming conflicts.
It's true that if two different plug-ins both try to change something about the elements that cannot be two things at once (a plug-in that changes the foreground color to "blue" and another changing the foreground color to "red"), then they'd collide if you called both of the two plug-ins methods on the same element set. But that's just like two calls to css.
In particular, remember that there can be multiple event handlers assigned to the same event on the same element, so plug-ins that hook events need not necessarily conflict with one another (unless one of them stops the event during handling).
Here's an example of two plug-ins that act on the matched set of elements, but in non-conflicting ways:
plugin1.js:
(function($) {
$.fn.foo = function() {
this.css("background-color", "#b00");
return this;
};
})(jQuery);
plugin2.js:
(function($) {
$.fn.bar = function() {
this.css("color", "white");
return this;
};
})(jQuery);
Usage:
$("#target").foo();
$("#target").bar();
or even
$("#target").foo().bar();
Live example
Now, if both the foo and bar plug-ins tried to set the foreground color, the one called later would win.
Here's an example of a pair of plug-ins that both want to handle the click event, but do so in a cooperative way:
plugin1.js:
(function($) {
$.fn.foo = function() {
this.click(function() {
$("<p>Click received by foo</p>").appendTo(document.body);
});
return this;
};
})(jQuery);
plugin2.js:
(function($) {
$.fn.bar = function() {
this.click(function() {
$("<p>Click received by bar</p>").appendTo(document.body);
});
return this;
};
})(jQuery);
Usage:
jQuery(function($) {
$("#target").foo().bar();
});
Live example
There's no magical relationship going on. There's no central registry or snap-ins that "belong" to any one element or to any one plug-in.
Javascript objects are just hacked-up functions; when you "attach a plugin" to an element, you're just calling some third-party library function that does something to that element, and possibly stores some internal data to assist with its animation throughout the session.
So there is nothing legally stopping you from "attaching" multiple plug-ins to the same element, though of course whether they'll be logically compatible is quite another question.