Bonjour,

Je viens vers vous suite à plusieurs recherche qui ne mon pas permis de trouver la solution.

Je suis actuellement entrain de développer un plugin jquery suite au tuto de Grafikart sur le menu collant pour un projet perso.

Mon plugin permet de sélectionner une div et de la rendre collante comme le tuto. Pour cela tous fonctionne mais je souhaiterais laisser la possibilité à l'utilisateur d'ajouter sa fonction lorsque le menu commence à descendre mais également lorsqu'il s'arrête.

Pour cela j'ai ajouter une variable animStart qui permet de savoir si l'animation de départ à été lancer ou non. pour cela également tous fonctionne mais lorsque je rajoute mon animation perso je tombe sur l'erreur :

TypeError: $(...).offset(...) is undefined

J'ai utiliser un template de plugin jquery pour bien le concevoir, vous trouverez en premier la déclaration du plugin et en second le plugin en question et à la ligne 118 l'appel de la fonction personnaliser.

Si quelqu'un peut m'expliquer d’où viens mon erreur je le remercie d'avance.

jQuery(function($){
             $('#filters').sticky({
                moreTop : 20,
                slideDuration : 200,
                animStart : function(){
                  posLeft = $(this).offset().left;
                  $('#filters').parent().animate({left: posLeft-100 }, 500);
                }
             }); 
});

/*
 * Project: 
 * Description: 
 * Author: 
 * License: 
 */
// the semi-colon before function invocation is a safety net against concatenated
// scripts and/or other plugins which may not be closed properly.
;(function ( $, window, document, undefined ) {
    // undefined is used here as the undefined global variable in ECMAScript 3 is
    // mutable (ie. it can be changed by someone else). undefined isn't really being
    // passed in so we can ensure the value of it is truly undefined. In ES5, undefined
    // can no longer be modified.
    // window is passed through as local variable rather than global
    // as this (slightly) quickens the resolution process and can be more efficiently
    // minified (especially when both are regularly referenced in your plugin).
    // Create the defaults once
    var pluginName = 'sticky',
        defaults = {
            moreTop : 0,
            endScroll : 0,
            slideDuration : 500,
            childElem : 'li',
            debug : {
                startAnim : false, // permet d'afficher dans la console si l'animation personnaliser c'est lancer
                endAnim : false, // permet d'afficher dans la console si l'animation personnaliser c'est terminer
                startScroll : false, // permet d'afficher une ligne à l'endroit ou doit commencer le scroll du menu
                endScroll : false, // permet d'afficher une ligne à l'endroit ou doit commencer le scroll du menu
            },
            animStart : function(){},
            animEnd : function(){},
        };
    // The actual plugin constructor
    function Plugin( element, options ) {
        this.element = element;
        // jQuery has an extend method which merges the contents of two or
        // more objects, storing the result in the first object. The first object
        // is generally empty as we don't want to alter the default options for
        // future instances of the plugin
        this.options = $.extend( {}, defaults, options) ;
        this._defaults = defaults;
        this._name = pluginName;
        this._infos = {};
        this._infos.origine = {};
        this.$element = $(this.element);
        this.init();
        return this;
    }
    Plugin.prototype = {
        / ***********************************************
        * Public methods don't start with underscore
        ************************************************ /
        init : function () {
            this._initInfos();
            this._startEvents();
            // Place initialization logic here
            // You already have access to the DOM element and the options via the instance,
            // e.g., this.element and this.options
        },
        getInfos : function(){
          return this._infos;
        },
        moveTop : function(posY){
            // modification de la position par rapport au top
            this.$element.stop().animate({top: posY}, this.options.slideDuration);
        },
        / ********************************************
        * Private methods start with underscore
        ********************************************* /
        /**
        * Permet d'initialiser les variables 
        * de créer le conteneur 
        * d'ajouter les propriétées css à l'élément et au conteneur de l'élément
        **/
        _initInfos : function(){
            this._infos.origine = {
                posTop : this.$element.offset().top,
                posLeft : this.$element.offset().left,
                startAnim : false,
                endAnim : false,
            };

            var containerSticky = document.createElement('div');
            $(containerSticky).addClass('containerSticky')
                              .css({
                                  position :'absolute',
                                  width : '100%'
            });
            this.$element.addClass('sticky')
                    .wrap($(containerSticky))
                    .css('position','relative');
        },
        _startEvents : function(){
            var parentOfY, scrollY, newPosElem,
            _self = this
            infos = _self._infos;

            // évènement scroll
            $(window).scroll(function(){
                parentOfY = _self.$element.parent().offset().top,
                scrollY = _self._getScrollY(),
                newPosElem = scrollY - parentOfY;

                if (scrollY > infos.origine.posTop){
                    _self.moveTop(newPosElem + _self.options.moreTop );
                    /**
                    * ICI ON DÉCLENCHE L'ANIMATION PERSONNALISER QU'UNE SEUL FOIS
                    **/
                    if(!infos.startAnim){
                        _self.options.animStart();
                    }
                    _self._infos.origine.startAnim = true;
                }
                else if(scrollY < infos.origine.posTop) {
                  _self.moveTop( infos.origine.posTop - parentOfY);
                };
                // set Position Element
                _self._infos.posY = newPosElem + _self.options.moreTop;
                _self._infos.posX = _self.$element.offset().left;
            });  
        },
        _getScrollY : function(){
          var scrOfY = 0;
          if(typeof(window.pageYOffset) == 'number') {
            scrOfY = window.pageYOffset;
          }else if( document.body && (document.body.scrollTop) ) {
            scrOfY = document.body.scrollTop;
          }else if( document.documentElement && (document.documentElement.scrollTop) ) {
            scrOfY = document.documentElement.scrollTop;
          }
          return scrOfY;
        },
       /* _debug : function (){
            var _self = this,
                _debug = _self.defaults.debug;

            if (_debug.startAnim) {
            }else if(_debug.endAnim){

            }else if(_debug.startScroll){

            }else if(_debug.endScroll){
            }
        }*/

    }
    // You don't need to change something below:
    // A really lightweight plugin wrapper around the constructor,
    // preventing against multiple instantiations and allowing any
    // public function (ie. a function whose name doesn't start
    // with an underscore) to be called via the jQuery plugin,
    // e.g. $(element).defaultPluginName('functionName', arg1, arg2)
    $.fn[pluginName] = function ( options ) {
        var args = arguments;
        // Is the first parameter an object (options), or was omitted,
        // instantiate a new instance of the plugin.
        if (options === undefined || typeof options === 'object') {
            return this.each(function () {
                // Only allow the plugin to be instantiated once,
                // so we check that the element has no plugin instantiation yet
                if (!$.data(this, 'plugin_' + pluginName)) {
                    // if it has no instance, create a new one,
                    // pass options to our plugin constructor,
                    // and store the plugin instance
                    // in the elements jQuery data object.
                    $.data(this, 'plugin_' + pluginName, new Plugin( this, options ));
                }
            });
        // If the first parameter is a string and it doesn't start
        // with an underscore or "contains" the `init`-function,
        // treat this as a call to a public method.
        } else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {
            // Cache the method call
            // to make it possible
            // to return a value
            var returns;
            this.each(function () {
                var instance = $.data(this, 'plugin_' + pluginName);
                // Tests that there's already a plugin-instance
                // and checks that the requested public method exists
                if (instance instanceof Plugin && typeof instance[options] === 'function') {
                    // Call the method of our plugin instance,
                    // and pass it the supplied arguments.
                    returns = instance[options].apply( instance, Array.prototype.slice.call( args, 1 ) );
                }
                // Allow instances to be destroyed via the 'destroy' method
                if (options === 'destroy') {
                  $.data(this, 'plugin_' + pluginName, null);
                }
            });
            // If the earlier cached method
            // gives a value back return the value,
            // otherwise return this to preserve chainability.
            return returns !== undefined ? returns : this;
        }
    };
}(jQuery, window, document));

1 réponse


Au cas ou tu as tjs ton problème, je pense que tu as un problème de scope.

animStart : function(){
    posLeft = $(this).offset().left;
    $('#filters').parent().animate({left: posLeft-100 }, 500);
}

A mon avis this dans ta function animStart n'est pas ton element (div),pour resoudre ton problème tu dois lui passer ton element

animStart : function(element){
    posLeft = $(element).offset().left;
    $('#filters').parent().animate({left: posLeft-100 }, 500);
}

et donc dans ton plugin

_self.options.animStart(_self.$element);

quelques choses comme ça

je pense que graphikart parle des scopes en javascript dans ce tutoriel, Programmation orientée Objet en Javascript