Call function without needing to instantiate the plugin again

Asked

Viewed 524 times

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 need to instantiate it again, which has generated bugs.

I urge 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 to instantiate the plugin again.

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

The plugin code follows below.

$(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;
    };
});
  • Can you change this jsFiddle to play the problem? -> http://jsfiddle.net/q5wjcuxv/

1 answer

3


The problem

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

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

An intermediate solution

Why dad.getPosition() function as proposed, you need to add the function in jQuery itself in the same way that .dad().

But this is not the recommended way!

Recommended solution

According to the jQuery’s guide to creating plugins, it is not recommended to add several functions to a given plugin. This leads to more risks of conflict with other plugins.

It is recommended to add only one method and to call plugin methods indirectly, like almost all libraries. Example:

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

I made a small refactoring in the plugin to work this way. See how it looked:

(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 little, like the example below:

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

I left one changed example in Jsfiddle.

Important remarks

Note that to not initialize the plugin several times, I added a if at the beginning that puts a flag dad-activated.

After the plugin startup, I added the code that calls the plugin 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 clean up the code. However, I recommend working in this way and, if possible, avoid using variables shared between methods, such as daddy as in function children_replace, which could receive the object as a parameter.

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

  • I use the attr() for the data-* are modified in the DOM and as the function data() takes the original value, forces me to use the function attr()

  • I could not use the plugin this way, the console shows no error, but it is not working

  • @Rodrigoborth It’s not 100% functional because I moved some methods and didn’t adjust global variables. Unfortunately I don’t have time to make all the necessary adjustments. The answer was just to give the solution way. :(

  • let’s try to tailor the code, had forgotten the global variables, but answered some other questions we had about how to make the plugin the right way too, thanks :D

  • @Rodrigoborth Ok, I’m really sorry to be a little out of time. If you keep having problems let me know that the other day I’ll take it easy. But anyway I think you can handle it ;)

  • 1

    https://github.com/williammustaffa/jquery.dad.js If you are interested, this is the link to the project

Show 1 more comment

Browser other questions tagged

You are not signed in. Login or sign up in order to post.