Mootools - add variables to all draggable and droppable elements with .implement - javascript

Im making a drag and drop system with mootools drag.move class, but i need all the draggable and droppable elements to have some extra variables and maybe add in a function or two. Ive researched on how to do this using .implement but Ive got no idea how to fit that into my code:
window.addEvent('domready', function(){
$$('#draggables DIV').makeDraggable({
droppables: $$('#droppables DIV'),
onEnter: function(draggable, droppable){
droppable.setStyle('background', 'orange');
droppable.setStyle('opacity', '0.4');
snap_left = droppable.getStyle('left');
snap_top = droppable.getStyle('top');
document.getElementById("slot").innerHTML=droppable.id;
},
onLeave: function(draggable, droppable){
droppable.setStyle('background', null);
},
onDrop: function(draggable, droppable){
if (droppable){
droppable.setStyle('background', '');
draggable.setStyle('left', snap_left );
draggable.setStyle('top', snap_top );
} else {
draggable.setStyle('left', snap_left );
draggable.setStyle('top', snap_top );
}
}
});
});
Is what I want posible using .implement?
Can I add these things to all draggable and droppable elements?
ty in advance!
-Thaiscorpion
edit:
Ive tried adding options directly to the main class in the mootools library and tried accesing them from the onEnter event like this:
onEnter: function(draggable, droppable){
if (droppable.occupied){ //here is where im tryin to acces it, the default option is set to occupied: true
droppable.setStyle('background', 'red');
droppable.setStyle('opacity', '0.4');
document.getElementById("slot").innerHTML=droppable.id;
} else {
droppable.setStyle('background', 'orange');
droppable.setStyle('opacity', '0.4');
snap_left = droppable.getStyle('left');
snap_top = droppable.getStyle('top');
document.getElementById("slot").innerHTML=droppable.id;
}
},
but not getting anything to work.

you can use element storage.
draggable.store("occupied", true);
....
if (draggable.retrieve("occupied") === true) {
}
.... functions or anything can be stored per element
element.store("somekey", function() {
element.toggleClass("foo");
});
element.retrieve("somekey").call(element);
and so forth.
to use Implement:
Element.implement({
dragfoo: function() {
this.set("drag", { });
return this;
}
});
// allows you:
$("someid").dragfoo();
though if you need storage, use storage and dont store properties on the actual element. mootools storage really uses an object hash table that's behind a closure. having proprietary element attributes/properties in IE can slow things down considerably in element access.

Related

jQuery Plugin - How to add / bind events

Ok this is my first stab at creating a jQuery plugin so I am going off tutorials currently.
This far I have
(function($)
{
$.tippedOff = function(selector, settings)
{
var config = {
'top':0,
'left':0,
'wait':3000
};
if(settings){$.extend(config, settings);}
var $elem = $(selector);
if($elem.length > 0)
{
$elem.each(function()
{
$(this).css({"color":"#F00"});
})
}
return this;
};
})(jQuery);
Which works for changing the text color of the provided elements. However. I want to add functionality to elements that the plugin takes effect on. Such as a hover or click event. But I can't wrap my head around that idea at the moment, seeing as the selector can be anything. So its not like I can hardcode something in per say thats specific like I would through normal jQuery methods.
So, with that, how do I go about adding that type of functionality to things after its been rendered?
When creating plugins, it is very easy to over-complicate things, so try to keep things nice and simple.
I have provided you with TWO examples of the tippedOff plugin. Here is also a jsfiddle demo of both plugins.
The first uses your original code as is (NO SIGNIFICANT CHANGES MADE):
$.tippedOff = function(selector, settings)
{
var config = {
'top':0,
'left':0,
'wait':3000
};
if(settings){$.extend(config, settings);}
var $elem = $(selector);
if($elem.length > 0)
{
$elem.each(function()
{
//bind mouseenter, mouseleave, click event
$(this).css({"color":"#F00"})
.mouseenter(function(){
$(this).css({"color":"green"});
})
.mouseleave(function(){
$(this).css({"color":"#F00"});
})
.click(function(){
$(this).html('clicked');
});
})
}
return this;
};
This one, however, is based on your original code. Basically, I have reconstructed your original code using these tips. This is how I would personally go about it. I have also provided you with a breakdown below of changes made. (SIGNIFICANT CHANGES MADE):
$.fn.tippedOff = function(settings) {
var config = $.extend( {
'top':0,
'left':0,
'wait':3000,
'color': 'orange',
'hoverColor': 'blue'
}, settings);
return this.each(function() {
$this = $(this);
$this.css({ 'color': config.color})
.mouseenter(function(){
$this.css({ 'color': config.hoverColor });
})
.mouseleave(function(){
$this.css({ 'color': config.color });
})
.click(function(){
$this.html('clicked');
});
});
}
----------------------------------------
Breakdown:
Original Code:
$.tippedOff = function(selector, settings) {
Changed:
$.fn.tippedOff = function( settings ) {
Comments:
The difference between $.tippedOff and $.fn.tippedOff is huge! Adding your plugin to the $.fn namespace rather than the $ namespace will prevent you from having to provide a selector and makes life simplier.
I personally like this answer, in which #Chad states:
My rule of thumb I follow is: use $. when it is not DOM related (like ajax), and use $.fn. when it operates on elements grabbed with a selector (like DOM/XML elements).
Original Code:
if(settings){$.extend(config, settings);}
Changed:
var config = $.extend( {
'top':0,
'left':0,
'wait':3000
}, settings);
Comments:
Having an if statement is redundant. .extend() does all the work for you.
Original Code:
var $elem = $(selector);
if($elem.length > 0)
{
$elem.each(function()
{
$(this).css({"color":"#F00"});
})
}
return this;
Changed:
return this.each(function() {
$this = $(this);
$this.css({ 'color': config.color});
});
Comments:
Using return this.each(function(){}) is good practice and maintains chainability. Not only that, you will no longer need to worry about the selector's length.
*NOTE: If you want to add additional events, then use different methods within your plugin: jQuery Doc Reference - Authoring Plugins.
I hope this helps and please let me know if you have any questions!
I have not enough reputation points to comment and fully agree with Dom, who is very knowledgeable. I only would like to add that within the changed code it would be better to create a local variable by using the var keyword:
var $this = $(this);
This will make the plugin better and allows you to apply the plugin to multiple elements one the page as for example:
$('#testX').tippedOff2();
$('#testY').tippedOff2();
$('#testZ').tippedOff2();

jQuery callbacks cumulation

I'm building a simple jQuery plugin called magicForm (How ridiculous is this?). Now face to a problem that I think I'm not figuring out properly.
My plugin is supposed to be applied on a container element, that will show each of its inputs one by one as user fills them. That's not the exact purpose of my problem.
Each time I initialize the container, I declare an event click callback. Let me show an example.
(function($){
var methods = {
init: function(options){
return this.each(function(){
var form, inputs;
var settings = {
debug: false
};
settings = $.extend(settings, options);
form = $(this);
$('a.submit', form).on('click', function(event){
if (settings.submitCallback) {
settings.submitCallback.call(form, inputs);
}
return false;
});
});
},
reset: function() {
}
}
$.fn.magicForm = function(method) {
if ( methods[method] ) {
return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || ! method ) {
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + method + ' does not exist.' );
}
};
})($);
I'm focusing on a specific part of this code :
$('a.submit', form).on('click', function(event){
if (settings.submitCallback) {
settings.submitCallback.call(form, inputs);
}
return false;
});
Because each time the init method is called, that poor callback is registered.
I was experiencing this painfully, when I invoked my plugin on an element nested in a twitter bootstrap 'tab', nested itself in a bootstrap modal :
I was calling init each time the event 'shown' of my bootstrap modal was triggered.
So, this is how I fixed it in my init method :
// Prevent callback cumulation
if (!$(this).data('form_initialized')) {
$('a.submit', form).on('click', function(event){
if (settings.submitCallback) {
settings.submitCallback.call(form, inputs);
}
return false;
});
$(this).data('form_initialized', true);
}
And I'm far from feeling sure about this.
Thank your for your time !
Many jquery plugins use data to know if their plugins were initialized. Most often, they use the name of their own plugin as a part (or in whole) as the data. For example:
$(this).data('magicForm')
So your approach of using that to signal is not a bad one.
However, you have two other options:
1) Pull the event handler out so the handler is a single instance. Above your methods, do var fnOnSubmit = function() { ... } Then you can simply ensure proper binding by calling $('a.submit', form).unbind('click', fnOnSubmit) before rebinding it the way you are already doing it.
2) Another option is to use event namespaces.
$('a.submit', form).unbind('click.magicForm'); then rebinding it with .on('click.magicForm') This namespace approach ensures that when you unbind it only unbinds in the context of your namespace magicForm, thus leaving all other click events (e.g. from other plugins) intact.
I hope this helps.
You could first explicitely remove the click-handler:
$('a.submit', form).off('click').on('click', function(event){ ... })
However, I would suggest you use event namespacing to prevent all click handlers (even those perhaps set by code not your own) from being removed:
$('a.submit', form).off('click.magicForm').on('click.magicForm', function(event){ ... })

Script.aculo.us Drag 'n' Drop - Revert onEnd condition

I'm trying to revert a draggable if a condition returns false. So for instance, I'd like to do the following:
new Draggable('myelement', {
onStart: function() {
// do something
},
onEnd: function() {
var condition = getConditionVal();
if (!condition) revert to original position
else {
// do something else
}
}
});
Would this be possible? Not sure if "droppables" would work in this case since the droppable area changes dynamically.
Scriptaculous drag/drop is designed to have all kinds of fancy stuff easily added.
Of course you can edit the revert option any time.
To change the value of the revert-option of an draggable, just reset the revert-option:
var myDraggable = new Draggable('myelement', {
onStart: function() {
// do something
},
onEnd: function() {
var condition = getConditionVal();
if (!condition){
myDraggable.options.revert = true;
}
else {
myDraggable.options.revert = false;
// do something else
}
};
});
Scriptaculous does the revert right after the onEnd event call,
which gives us the possibility of changing it before it will be executed.
Scriptaculous's drag/drop code wasn't designed to have conditional revert. You can have revert or no revert. That's all, sadly.
This feature has been requested many times but scripty/prototype has waned in popularity over the years, so it's doubtful this feature will ever be added.

use custom function configuration option of a jquery script

I am using this jQuery plugin: http://demo.awkwardgroup.com/showcase/
I am trying to show a counter on the first slide of Awkward Showcase and have been unable to nest the call into the script without breaking it to some degree or other. I tried using current_id, but realized that the value of current_id was inconsistent, at least, when I was calling for it. I am now trying to rely on the "active" state of the number 1, which corresponds to the first slide and is consistent.
I realize now that it has a custom_function feature, but don't know how to plug this in there
var awNavButtonID = document.getElementById('showcase-navigation-button-1');
function myHack() {
var awNavButtonClass = awNavButtonID.className;
if(awNavButton == 'active'){
document.getElementById('defaultCountdown').style.left = "250px";
} else {
document.getElementById('defaultCountdown').style.left = "-9999px";
}
}
$(document).ready(function(){
$("#showcase").awShowcase(
{
content_width: 700,
content_height: 470,
custom_function: null //how do i plug it in here?
});
});
If you want to bind myHack function to the custom_function callback, try this :
$("#showcase").awShowcase(
{
content_width: 700,
content_height: 470,
custom_function: myhack
});
You can also attach your function to a local variable.
var myHackFunction = function myHack() { ... }
...
custom_function: myHackFunction
Finally, you can implement an anonymous function :
custom_function: function() { /* do stuff here */ }

jQuery event to trigger action when a div is made visible

I'm using jQuery in my site and I would like to trigger certain actions when a certain div is made visible.
Is it possible to attach some sort of "isvisible" event handler to arbitrary divs and have certain code run when they the div is made visible?
I would like something like the following pseudocode:
$(function() {
$('#contentDiv').isvisible(function() {
alert("do something");
});
});
The alert("do something") code should not fire until the contentDiv is actually made visible.
Thanks.
You could always add to the original .show() method so you don't have to trigger events every time you show something or if you need it to work with legacy code:
Jquery extension:
jQuery(function($) {
var _oldShow = $.fn.show;
$.fn.show = function(speed, oldCallback) {
return $(this).each(function() {
var obj = $(this),
newCallback = function() {
if ($.isFunction(oldCallback)) {
oldCallback.apply(obj);
}
obj.trigger('afterShow');
};
// you can trigger a before show if you want
obj.trigger('beforeShow');
// now use the old function to show the element passing the new callback
_oldShow.apply(obj, [speed, newCallback]);
});
}
});
Usage example:
jQuery(function($) {
$('#test')
.bind('beforeShow', function() {
alert('beforeShow');
})
.bind('afterShow', function() {
alert('afterShow');
})
.show(1000, function() {
alert('in show callback');
})
.show();
});
This effectively lets you do something beforeShow and afterShow while still executing the normal behavior of the original .show() method.
You could also create another method so you don't have to override the original .show() method.
The problem is being addressed by DOM mutation observers. They allow you to bind an observer (a function) to events of changing content, text or attributes of dom elements.
With the release of IE11, all major browsers support this feature, check http://caniuse.com/mutationobserver
The example code is a follows:
$(function() {
$('#show').click(function() {
$('#testdiv').show();
});
var observer = new MutationObserver(function(mutations) {
alert('Attributes changed!');
});
var target = document.querySelector('#testdiv');
observer.observe(target, {
attributes: true
});
});
<div id="testdiv" style="display:none;">hidden</div>
<button id="show">Show hidden div</button>
<script type="text/javascript" src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
There is no native event you can hook into for this however you can trigger an event from your script after you have made the div visible using the .trigger function
e.g
//declare event to run when div is visible
function isVisible(){
//do something
}
//hookup the event
$('#someDivId').bind('isVisible', isVisible);
//show div and trigger custom event in callback when div is visible
$('#someDivId').show('slow', function(){
$(this).trigger('isVisible');
});
You can use jQuery's Live Query plugin.
And write code as follows:
$('#contentDiv:visible').livequery(function() {
alert("do something");
});
Then everytime the contentDiv is visible, "do something" will be alerted!
redsquare's solution is the right answer.
But as an IN-THEORY solution you can write a function which is selecting the elements classed by .visibilityCheck (not all visible elements) and check their visibility property value; if true then do something.
Afterward, the function should be performed periodically using the setInterval() function. You can stop the timer using the clearInterval() upon successful call-out.
Here's an example:
function foo() {
$('.visibilityCheck').each(function() {
if ($(this).is(':visible')){
// do something
}
});
}
window.setInterval(foo, 100);
You can also perform some performance improvements on it, however, the solution is basically absurd to be used in action. So...
The following code (pulled from http://maximeparmentier.com/2012/11/06/bind-show-hide-events-with-jquery/) will enable you to use $('#someDiv').on('show', someFunc);.
(function ($) {
$.each(['show', 'hide'], function (i, ev) {
var el = $.fn[ev];
$.fn[ev] = function () {
this.trigger(ev);
return el.apply(this, arguments);
};
});
})(jQuery);
If you want to trigger the event on all elements (and child elements) that are actually made visible, by $.show, toggle, toggleClass, addClass, or removeClass:
$.each(["show", "toggle", "toggleClass", "addClass", "removeClass"], function(){
var _oldFn = $.fn[this];
$.fn[this] = function(){
var hidden = this.find(":hidden").add(this.filter(":hidden"));
var result = _oldFn.apply(this, arguments);
hidden.filter(":visible").each(function(){
$(this).triggerHandler("show"); //No bubbling
});
return result;
}
});
And now your element:
$("#myLazyUl").bind("show", function(){
alert(this);
});
You could add overrides to additional jQuery functions by adding them to the array at the top (like "attr")
a hide/show event trigger based on Glenns ideea:
removed toggle because it fires show/hide and we don't want 2fires for one event
$(function(){
$.each(["show","hide", "toggleClass", "addClass", "removeClass"], function(){
var _oldFn = $.fn[this];
$.fn[this] = function(){
var hidden = this.find(":hidden").add(this.filter(":hidden"));
var visible = this.find(":visible").add(this.filter(":visible"));
var result = _oldFn.apply(this, arguments);
hidden.filter(":visible").each(function(){
$(this).triggerHandler("show");
});
visible.filter(":hidden").each(function(){
$(this).triggerHandler("hide");
});
return result;
}
});
});
I had this same problem and created a jQuery plugin to solve it for our site.
https://github.com/shaunbowe/jquery.visibilityChanged
Here is how you would use it based on your example:
$('#contentDiv').visibilityChanged(function(element, visible) {
alert("do something");
});
What helped me here is recent ResizeObserver spec polyfill:
const divEl = $('#section60');
const ro = new ResizeObserver(() => {
if (divEl.is(':visible')) {
console.log("it's visible now!");
}
});
ro.observe(divEl[0]);
Note that it's crossbrowser and performant (no polling).
Just bind a trigger with the selector and put the code into the trigger event:
jQuery(function() {
jQuery("#contentDiv:hidden").show().trigger('show');
jQuery('#contentDiv').on('show', function() {
console.log('#contentDiv is now visible');
// your code here
});
});
Use jQuery Waypoints :
$('#contentDiv').waypoint(function() {
alert('do something');
});
Other examples on the site of jQuery Waypoints.
I did a simple setinterval function to achieve this. If element with class div1 is visible, it sets div2 to be visible. I know not a good method, but a simple fix.
setInterval(function(){
if($('.div1').is(':visible')){
$('.div2').show();
}
else {
$('.div2').hide();
}
}, 100);
You can also try jQuery appear plugin as mentioned in parallel thread https://stackoverflow.com/a/3535028/741782
This support easing and trigger event after animation done! [tested on jQuery 2.2.4]
(function ($) {
$.each(['show', 'hide', 'fadeOut', 'fadeIn'], function (i, ev) {
var el = $.fn[ev];
$.fn[ev] = function () {
var result = el.apply(this, arguments);
var _self=this;
result.promise().done(function () {
_self.triggerHandler(ev, [result]);
//console.log(_self);
});
return result;
};
});
})(jQuery);
Inspired By http://viralpatel.net/blogs/jquery-trigger-custom-event-show-hide-element/
There is a jQuery plugin available for watching change in DOM attributes,
https://github.com/darcyclarke/jQuery-Watch-Plugin
The plugin wraps All you need do is bind MutationObserver
You can then use it to watch the div using:
$("#selector").watch('css', function() {
console.log("Visibility: " + this.style.display == 'none'?'hidden':'shown'));
//or any random events
});
Hope this will do the job in simplest manner:
$("#myID").on('show').trigger('displayShow');
$('#myID').off('displayShow').on('displayShow', function(e) {
console.log('This event will be triggered when myID will be visible');
});
I changed the hide/show event trigger from Catalint based on Glenns idea.
My problem was that I have a modular application. I change between modules showing and hiding divs parents. Then when I hide a module and show another one, with his method I have a visible delay when I change between modules. I only need sometimes to liten this event, and in some special childs. So I decided to notify only the childs with the class "displayObserver"
$.each(["show", "hide", "toggleClass", "addClass", "removeClass"], function () {
var _oldFn = $.fn[this];
$.fn[this] = function () {
var hidden = this.find(".displayObserver:hidden").add(this.filter(":hidden"));
var visible = this.find(".displayObserver:visible").add(this.filter(":visible"));
var result = _oldFn.apply(this, arguments);
hidden.filter(":visible").each(function () {
$(this).triggerHandler("show");
});
visible.filter(":hidden").each(function () {
$(this).triggerHandler("hide");
});
return result;
}
});
Then when a child wants to listen for "show" or "hide" event I have to add him the class "displayObserver", and when It does not want to continue listen it, I remove him the class
bindDisplayEvent: function () {
$("#child1").addClass("displayObserver");
$("#child1").off("show", this.onParentShow);
$("#child1").on("show", this.onParentShow);
},
bindDisplayEvent: function () {
$("#child1").removeClass("displayObserver");
$("#child1").off("show", this.onParentShow);
},
I wish help
One way to do this.
Works only on visibility changes that are made by css class change, but can be extended to watch for attribute changes too.
var observer = new MutationObserver(function(mutations) {
var clone = $(mutations[0].target).clone();
clone.removeClass();
for(var i = 0; i < mutations.length; i++){
clone.addClass(mutations[i].oldValue);
}
$(document.body).append(clone);
var cloneVisibility = $(clone).is(":visible");
$(clone).remove();
if (cloneVisibility != $(mutations[0].target).is(":visible")){
var visibilityChangedEvent = document.createEvent('Event');
visibilityChangedEvent.initEvent('visibilityChanged', true, true);
mutations[0].target.dispatchEvent(visibilityChangedEvent);
}
});
var targets = $('.ui-collapsible-content');
$.each(targets, function(i,target){
target.addEventListener('visibilityChanged',VisbilityChanedEventHandler});
target.addEventListener('DOMNodeRemovedFromDocument',VisbilityChanedEventHandler });
observer.observe(target, { attributes: true, attributeFilter : ['class'], childList: false, attributeOldValue: true });
});
function VisbilityChanedEventHandler(e){console.log('Kaboom babe'); console.log(e.target); }
my solution:
; (function ($) {
$.each([ "toggle", "show", "hide" ], function( i, name ) {
var cssFn = $.fn[ name ];
$.fn[ name ] = function( speed, easing, callback ) {
if(speed == null || typeof speed === "boolean"){
var ret=cssFn.apply( this, arguments )
$.fn.triggerVisibleEvent.apply(this,arguments)
return ret
}else{
var that=this
var new_callback=function(){
callback.call(this)
$.fn.triggerVisibleEvent.apply(that,arguments)
}
var ret=this.animate( genFx( name, true ), speed, easing, new_callback )
return ret
}
};
});
$.fn.triggerVisibleEvent=function(){
this.each(function(){
if($(this).is(':visible')){
$(this).trigger('visible')
$(this).find('[data-trigger-visible-event]').triggerVisibleEvent()
}
})
}
})(jQuery);
example usage:
if(!$info_center.is(':visible')){
$info_center.attr('data-trigger-visible-event','true').one('visible',processMoreLessButton)
}else{
processMoreLessButton()
}
function processMoreLessButton(){
//some logic
}
$( window ).scroll(function(e,i) {
win_top = $( window ).scrollTop();
win_bottom = $( window ).height() + win_top;
//console.log( win_top,win_bottom );
$('.onvisible').each(function()
{
t = $(this).offset().top;
b = t + $(this).height();
if( t > win_top && b < win_bottom )
alert("do something");
});
});
$(function() {
$(document).click(function (){
if ($('#contentDiv').is(':visible')) {
alert("Visible");
} else {
alert("Hidden");
}
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="contentDiv">Test I'm here</div>
<button onclick="$('#contentDiv').toggle();">Toggle the div</button>
<div id="welcometo">Özhan</div>
<input type="button" name="ooo"
onclick="JavaScript:
if(document.all.welcometo.style.display=='none') {
document.all.welcometo.style.display='';
} else {
document.all.welcometo.style.display='none';
}">
This code auto control not required query visible or unvisible control

Categories

Resources