Call function without having to instantiate the plugin again

3

I'm doing a drag and drop plugin with a colleague, but we're having a problem. When we instantiate the plugin it runs normally, but if we want to call some function of this plugin in the variable in which it was instantiated we always have to instantiate it again, which has generated bugs.

I instantiate the plugin:

var dad = $('.dad').dad();

And then I want to call the function to getPosition to know the final order of the items.

var ordem = dad.getPosition();

But it says getPosition is not a function on the console. The only way to make it work is by instantiating the plugin again.

var ordem = dad.dad().getPosition();

The code for the plugin follows.

$(function(){
    function O_dad(){
        var self=this;
        this.x=0;
        this.y=0;
        this.target=false;
        this.clone=false;
        this.placeholder=false;
        this.cloneoffset={x:0,y:0};
        this.move=function(e){
            self.x=e.pageX;
            self.y=e.pageY;
            if (self.clone!=false && self.target!=false){
                self.clone.css({top:self.y-self.cloneoffset.y,left:self.x-self.cloneoffset.x});
            }else{

            }
        };
        $(window).on('mousemove',function(e){
            self.move(e)
        });

    }
    $.prototype.dad=function(opts){
        var me=this;
        $(this).each(function(){
            var mouse;
            mouse=new O_dad();
            var target,active,callback,daddy,childrenClass,jQclass,cloneClass;
            childrenClass='dads-children';
            cloneClass='dads-children-clone';
            jQclass='.dads-children';
            daddy=$(this);
            daddy.addClass('dad-container');
            if ( typeof opts != "undefined" && typeof opts.target !== 'undefined'){
                target=daddy.find(opts.target);
            }else{
                target=daddy.children();
            }
            if ( typeof opts != "undefined" && typeof opts.callback !== 'undefined'){
                callback=opts.callback;
            }else{
                callback=false;
            }
            me.addDropzone=function(selector,func){
                $(selector).on('mouseenter',function(){
                    if (mouse.target!=false) {
                        mouse.placeholder.css({display: 'none'});
                        mouse.target.css({display: 'none'});

                        $(this).addClass('active');
                    }
                }).on('mouseup',function(){
                    if (mouse.target!=false) {
                        mouse.placeholder.css({display: 'block'});
                        mouse.target.css({display: 'block'});
                        func(mouse.target);
                        children_replace();
                    }
                    $(this).removeClass('active');
                }).on('mouseleave',function(){
                    if (mouse.target!=false){
                        mouse.placeholder.css({display: 'block'});
                        mouse.target.css({display: 'block'});
                    }
                    $(this).removeClass('active');
                });
            };
            me.getPosition=function(){
                var positionArray = [];
                $(this).find(jQclass).each(function(){
                    positionArray[$(this).attr('data-dad-id')]=parseInt($(this).attr('data-dad-position'));
                });
                return positionArray;
            };
            $(document).on('mouseup',function(){
                children_replace();
            });
            function children_replace(){
                if (mouse.target!=false &&  mouse.clone!=false){
                    if (callback!=false){
                        callback(mouse.target);
                    }
                    var appear=mouse.target;
                    var desapear=mouse.clone;
                    var holder=mouse.placeholder;
                    var bLeft =0;Math.floor(parseFloat(daddy.css('border-left-width')));
                    var bTop =0;Math.floor(parseFloat(daddy.css('border-top-width')));
                    if ($.contains(daddy[0],mouse.target[0])){
                        mouse.clone.animate({top:mouse.target.offset().top-daddy.offset().top-bTop,left:mouse.target.offset().left-daddy.offset().left-bLeft},300,function(){
                            appear.css({visibility:'visible'}).removeClass('active');
                            desapear.remove();
                        });
                    }else{
                        mouse.clone.fadeOut(300,function(){
                            desapear.remove();
                        })
                    }
                    holder.remove();
                    mouse.clone=false;
                    mouse.placeholder=false;
                    mouse.target=false;
                    update_position(daddy);
                }
                $("html,body").removeClass('dad-noSelect');
            }
            function children_update(obj){
                if (mouse.target!=false && mouse.clone!=false) {
                    var newplace, origin;
                    origin = $('<span style="display:none"></span>');
                    newplace = $('<span style="display:none"></span>');
                    if (obj.prevAll().hasClass('active')){
                        obj.after(newplace);
                    }else{
                        obj.before(newplace);
                    }
                    mouse.target.before(origin);
                    newplace.before(mouse.target);
                    //update placeholder
                    mouse.placeholder.css({
                        top:mouse.target.offset().top-daddy.offset().top,
                        left:mouse.target.offset().left-daddy.offset().left,
                        width: mouse.target.outerWidth()-10,
                        height: mouse.target.outerHeight()-10
                    });
                    //origin.before(obj);
                    origin.remove();
                    newplace.remove();
                }
            }
            var order = 1;
            target.addClass(childrenClass).each(function(){
                if($(this).data('dad-id')==undefined){
                    $(this).attr('data-dad-id',order);
                }
                $(this).attr('data-dad-position',order);
                order++;
            });
            function update_position(e){
                var order = 1;
                e.find(jQclass).each(function(){
                    $(this).attr('data-dad-position',order);
                    order++;
                });
            }
            daddy.find(jQclass).on('mousedown touchstart',function(e){
                if (mouse.target==false && e.which==1 && active==true){
                    // GET TARGET
                    mouse.target=$(this);

                    // ADD CLONE
                    mouse.clone=mouse.target.clone();
                    mouse.target.css({visibility:'hidden'}).addClass('active');
                    mouse.clone.addClass(cloneClass);
                    daddy.append(mouse.clone);

                    // ADD PLACEHOLDER
                    mouse.placeholder=$('<div></div>');
                    mouse.placeholder.addClass('dads-children-placeholder');
                    mouse.placeholder.css({
                        top:mouse.target.offset().top-daddy.offset().top,
                        left:mouse.target.offset().left-daddy.offset().left,
                        width: mouse.target.outerWidth()-10,
                        height: mouse.target.outerHeight()-10,
                        lineHeight: mouse.target.height()-18+'px'
                    }).text('drop here');
                    daddy.append(mouse.placeholder);

                    // GET OFFSET FOR CLONE
                    var difx,dify;
                    var bLeft =Math.floor(parseFloat(daddy.css('border-left-width')));
                    var bTop =Math.floor(parseFloat(daddy.css('border-top-width')));
                    difx=mouse.x-mouse.target.offset().left+daddy.offset().left+bLeft;
                    dify=mouse.y-mouse.target.offset().top+daddy.offset().top+bTop;
                    mouse.cloneoffset.x=difx;
                    mouse.cloneoffset.y=dify;

                    // REMOVE THE CHILDREN DAD CLASS AND SET THE POSITION ON SCREEN
                    mouse.clone.removeClass(childrenClass).css({
                        position:'absolute',
                        top:mouse.y-mouse.cloneoffset.y,
                        left:mouse.x-mouse.cloneoffset.x
                    });
                    // UNABLE THE TEXT SELECTION AND SET THE GRAB CURSOR
                    $("html,body").addClass('dad-noSelect');
                }
            }).on('mouseenter',function(){
                children_update($(this));
            });

        });

        return this;
    };
});
    
asked by anonymous 04.05.2015 / 17:58

1 answer

3

The problem

The problem occurs because you are trying to add the new functions to objects returned by jQuery, which will soon be discarded.

It's important to remember that jQuery plugins return jQuery objects and not DOM objects. A new jQuery selector will create a new jQuery object with the selected elements.

An intermediate solution

In order for dad.getPosition() to work as proposed, you need to add the function in jQuery itself in the same way as .dad() .

But this is not the recommended way!

Recommended solution

Depending on the jQuery plug-in creation guide , it is not recommended to add multiple functions to a certain plugin. This leads to more risk of conflict with other plugins.

We recommend adding only one method and invoking plugin methods indirectly, like almost any library. Example:

(function( $ ) {
    $.fn.popup = function( action ) {
        if ( action === "open") {
            // Open popup code.
        }
        if ( action === "close" ) {
            // Close popup code.
        }
    };
}( jQuery ));

I did a small refactoring in the plugin to work that way. Here's how it went:

(function( $ ) {
    var jQclass='.dads-children';

    function O_dad(){
        var self=this;
        this.x=0;
        this.y=0;
        this.target=false;
        this.clone=false;
        this.placeholder=false;
        this.cloneoffset={x:0,y:0};
        this.move=function(e){
            self.x=e.pageX;
            self.y=e.pageY;
            if (self.clone!=false && self.target!=false){
                self.clone.css({top:self.y-self.cloneoffset.y,left:self.x-self.cloneoffset.x});
            }else{

            }
        };
        $(window).on('mousemove',function(e){
            self.move(e)
        });
    }

    function addDropzone(selector,func){
        $(selector).on('mouseenter',function(){
            if (mouse.target!=false) {
                mouse.placeholder.css({display: 'none'});
                mouse.target.css({display: 'none'});

                $(this).addClass('active');
            }
        }).on('mouseup',function(){
            if (mouse.target!=false) {
                mouse.placeholder.css({display: 'block'});
                mouse.target.css({display: 'block'});
                func(mouse.target);
                children_replace();
            }
            $(this).removeClass('active');
        }).on('mouseleave',function(){
            if (mouse.target!=false){
                mouse.placeholder.css({display: 'block'});
                mouse.target.css({display: 'block'});
            }
            $(this).removeClass('active');
        });
    };
    function getPosition(){
        var positionArray = [];
        $(this).find(jQclass).each(function(){
            positionArray[$(this).attr('data-dad-id')]=parseInt($(this).attr('data-dad-position'));
        });
        return positionArray;
    };

    function children_replace(){
        if (mouse.target!=false &&  mouse.clone!=false){
            if (callback!=false){
                callback(mouse.target);
            }
            var appear=mouse.target;
            var desapear=mouse.clone;
            var holder=mouse.placeholder;
            var bLeft =0;Math.floor(parseFloat(daddy.css('border-left-width')));
            var bTop =0;Math.floor(parseFloat(daddy.css('border-top-width')));
            if ($.contains(daddy[0],mouse.target[0])){
                mouse.clone.animate({top:mouse.target.offset().top-daddy.offset().top-bTop,left:mouse.target.offset().left-daddy.offset().left-bLeft},300,function(){
                    appear.css({visibility:'visible'}).removeClass('active');
                    desapear.remove();
                });
            }else{
                mouse.clone.fadeOut(300,function(){
                    desapear.remove();
                })
            }
            holder.remove();
            mouse.clone=false;
            mouse.placeholder=false;
            mouse.target=false;
            update_position(daddy);
        }
        $("html,body").removeClass('dad-noSelect');
    }

    function children_update(obj){
        if (mouse.target!=false && mouse.clone!=false) {
            var newplace, origin;
            origin = $('<span style="display:none"></span>');
            newplace = $('<span style="display:none"></span>');
            if (obj.prevAll().hasClass('active')){
                obj.after(newplace);
            }else{
                obj.before(newplace);
            }
            mouse.target.before(origin);
            newplace.before(mouse.target);
            //update placeholder
            mouse.placeholder.css({
                top:mouse.target.offset().top-daddy.offset().top,
                left:mouse.target.offset().left-daddy.offset().left,
                width: mouse.target.outerWidth()-10,
                height: mouse.target.outerHeight()-10
            });
            //origin.before(obj);
            origin.remove();
            newplace.remove();
        }
    }

    function update_position(e){
        var order = 1;
        e.find(jQclass).each(function(){
            $(this).attr('data-dad-position',order);
            order++;
        });
    }


    $.fn.dad = function(method) {

        $(this).each(function(){

            if ($(this).data('dad-activated')) {
                return;
            } else {
                $(this).data('dad-activated', true);
            }

            var mouse;
            mouse=new O_dad();
            var target,active,callback,daddy,childrenClass,jQclass,cloneClass;
            childrenClass='dads-children';
            cloneClass='dads-children-clone';
            daddy=$(this);
            daddy.addClass('dad-container');
            if ( typeof opts != "undefined" && typeof opts.target !== 'undefined'){
                target=daddy.find(opts.target);
            }else{
                target=daddy.children();
            }
            if ( typeof opts != "undefined" && typeof opts.callback !== 'undefined'){
                callback=opts.callback;
            }else{
                callback=false;
            }

            $(document).on('mouseup',function(){
                children_replace();
            });

            var order = 1;
            target.addClass(childrenClass).each(function(){
                if($(this).data('dad-id')==undefined){
                    $(this).attr('data-dad-id',order);
                }
                $(this).attr('data-dad-position',order);
                order++;
            });

            daddy.find(jQclass).on('mousedown touchstart',function(e){
                if (mouse.target==false && e.which==1 && active==true){
                    // GET TARGET
                    mouse.target=$(this);

                    // ADD CLONE
                    mouse.clone=mouse.target.clone();
                    mouse.target.css({visibility:'hidden'}).addClass('active');
                    mouse.clone.addClass(cloneClass);
                    daddy.append(mouse.clone);

                    // ADD PLACEHOLDER
                    mouse.placeholder=$('<div></div>');
                    mouse.placeholder.addClass('dads-children-placeholder');
                    mouse.placeholder.css({
                        top:mouse.target.offset().top-daddy.offset().top,
                        left:mouse.target.offset().left-daddy.offset().left,
                        width: mouse.target.outerWidth()-10,
                        height: mouse.target.outerHeight()-10,
                        lineHeight: mouse.target.height()-18+'px'
                    }).text('drop here');
                    daddy.append(mouse.placeholder);

                    // GET OFFSET FOR CLONE
                    var difx,dify;
                    var bLeft =Math.floor(parseFloat(daddy.css('border-left-width')));
                    var bTop =Math.floor(parseFloat(daddy.css('border-top-width')));
                    difx=mouse.x-mouse.target.offset().left+daddy.offset().left+bLeft;
                    dify=mouse.y-mouse.target.offset().top+daddy.offset().top+bTop;
                    mouse.cloneoffset.x=difx;
                    mouse.cloneoffset.y=dify;

                    // REMOVE THE CHILDREN DAD CLASS AND SET THE POSITION ON SCREEN
                    mouse.clone.removeClass(childrenClass).css({
                        position:'absolute',
                        top:mouse.y-mouse.cloneoffset.y,
                        left:mouse.x-mouse.cloneoffset.x
                    });
                    // UNABLE THE TEXT SELECTION AND SET THE GRAB CURSOR
                    $("html,body").addClass('dad-noSelect');
                }
            }).on('mouseenter',function(){
                children_update($(this));
            });

        });

        if (method == 'getPosition') {
            return getPosition.apply( this );
        } else if (method == 'addDropzone') {
            return addDropzone.apply( this, arguments );
        } 

        return this;

    };

}( jQuery ));

The call changes a bit, like the example below:

var myDad = $('.dad').dad();
var ordem = myDad.dad('getPosition');

I left a changed example in JsFiddle .

Important remarks

Note that in order to not initialize the plugin multiple times, I've added a if to the start that places a dad-activated flag.

After the plugin was started, I added the code that calls the plugin's methods indirectly:

if (method == 'getPosition') {
    return getPosition.apply( this );
} else if (method == 'addDropzone') {
    return addDropzone.apply( this, arguments );
} 

Note that I removed the various internal functions that were inside the block and put them in the scope of the plugin. That was just to make the code cleaner. However, I recommend that you work this way and, if possible, avoid using shared variables between methods, such as daddy and children_replace function, which could receive the object as a parameter.

One last remark: Instead of accessing attributes of type data-* with attr() , you can use the function data() .

    
04.05.2015 / 19:31