簡體   English   中英

JS:為我的插件創建新的唯一實例

[英]JS: Create new unique instance of my plugin

我已經創建了一個JavaScript插件,我希望有可能多次使用它,但是,像下面這樣兩次將它稱為頁面兩次似乎並不能創建新的實例:

var lightbox = new MarvLightbox({ 
    imagesContainer: 'detail-hdr__detail-images',
    limit: 11
  });
  var lightbox2 = new MarvLightbox({ 
    imagesContainer: 'image-grid-2',
    galleryFolder: 2,
    mobileFolder: 0
  });

以下代碼似乎返回相同的值,因此,例如,如果我單擊圖像以在state對象內設置c值,則它會更改,例如lightbox2 AND lightbox

MarvLightbox.prototype.current = function() {
    console.log(state);
  };

我嘗試從插件中刪除所有不需要的代碼。 但是我基本上要做的是:1.編譯容器中的圖像列表。 2.從JSON字符串生成HTML。 3.為圖像分配點擊事件,然后在單擊它們時打開燈箱並更改圖像等。

(function() {

  var instance = null,
      container;

  // Constructor
  this.MarvLightbox = function() {

    instance = this;

    // Create global variables
    this.images_count = -1;
    this.active = false;
    this.error = false;
    this.debug = false;

    // Define option defaults
    var defaults = {
      activeClass: 'marvLightbox',
      appendTo: '#wrapper',
      imagesContainer: null,
      thumbClass: null,
      lightboxId: 'marvLightbox',
      galleryFolder: null,
      mobileFolder: null,
      showAlt: true,
      showMax: true,
      limit: null,
      html: '{ "div": { "id": "{lightboxId}", "0": { "div": { "class": "{lightboxId}__container", "data-click": "EventClose", "0": { "div": { "class": "{lightboxId}__controls {lightboxId}__controls--top", "0": { "div": { "class": "{lightboxId}__eschint", "content": "Press <span>ESC</span> to close" } }, "1": { "div": { "class": "{lightboxId}__close", "data-click": "EventClose" } } } }, "1": { "div": { "class": "{lightboxId}__image", "0": { "img": { "src": "", "class": "responsive-img image", "data-click": "EventRight" } } } }, "2": { "div": { "class": "{lightboxId}__controls {lightboxId}__controls--bottom", "3": { "div": { "class": "{lightboxId}__left", "data-click": "EventLeft", "data-hover": "EventClass(#{lightboxId}, toggle: left)" } }, "4": { "div": { "class": "{lightboxId}__right", "data-click": "EventRight", "data-hover": "EventClass(#{lightboxId}, toggle: right)" } }, "5": { "div": { "class": "{lightboxId}__alt" } }, "6": { "div": { "class": "{lightboxId}__num" } } } } } } } }'
    };

    // Create options by extending defaults with the passed in arugments
    if (arguments[0] && typeof arguments[0] === "object") {
      this.options = extendDefaults(defaults, arguments[0]);
    }

    this.options.html = this.options.html.replace(/\{(lightboxId)}/g, this.options.lightboxId);
    // this.options.limit = this.options.limit + 1;

    // Check if debugging is enabled
    (function() {
      var args = document.querySelectorAll('[data-external-arg]');
      if(args.length > 0 && args[0].src.indexOf('marv.lightbox') !== -1) {
        if (args[0].dataset.externalArg === 'debug') instance.debug = true;
      }
    }());

    // Initialise plugin
    this.init();
  };

  // Debugging messages
  var debug = function(code, arg) {

    var args = function(n) {
      if (arg === undefined) {
        return '[arg undefined]';
      }

      if (arg[n] === undefined) {
        return '[arg undefined]';
      } else {
        return arg[n];
      }
    };

    var messages = function(code, argument) {
      return [
        'marv.lightbox(debug): Debugging mode is on, make sure you turn this off before you launch',
        'marv.lightbox(debug): HTMLElement(' + args(0) + ') with the name of ' + args(1) + ' has been detected',
        'marv.lightbox(debug): Found '+ arg + ' image/s within your Lightbox container ',
        'marv.lightbox(debug): ' + args(0) + ' key pressed, changing current (' + args(1) + '), number of images (' + args(2) + ')',
        'marv.lightbox(debug): Current is set to null, closing lightbox',
        'marv.lightbox(debug): Inserting Lightbox within HTMLElement(' + args(1) + '), using ' + args(0),
        'marv.lightbox(debug): 1 image found, don\'t add previous/next arrows, don\'t show numbers either',
        'marv.lightbox(debug): showAlt set to false, don\'t display alt text',
        'marv.lightbox(debug): showMax set to false, don\'t display numbers below alt text',
        'marv.lightbox(debug): Reverting to mobile version (' + instance.options.mobileFolder + ') of images',
        'marv.lightbox(debug): Over-riding to ' + instance.options.galleryFolder + ' version of images'
      ][code];
    };
    if (instance.debug === true)
      console.log(messages(code, arg));

  };

  // Error messages
  var error = function(code, arg) {

    var args = function(n) {
      if (arg === undefined) {
        return '[arg undefined]';
      }

      if (arg[n] === undefined) {
        return '[arg undefined]';
      } else {
        return arg[n];
      }
    };

    var messages = function(code, argument) {
      return [
        'marv.lightbox(error): I need to know which images to use... add { imagesContainer: "id/class" } to the plugin initialization',
        'marv.lightbox(error): The HTML structure provided appears to have an error: ' + arg,
        'marv.lightbox(error): Issue compiling list of images, speak to Dev, error: ' + arg,
        'marv.lightbox(error): Your going to need some images for this to work... make sure they have the class: ' + instance.options.thumbClass,
        'marv.lightbox(error): I was unable to find an element which matches ' + instance.options.imagesContainer + ', please double check this',
        'marv.lightbox(error): I was unable to find a container with the name of ' + args[0],
        'marv.lightbox(error): EventListener with arguments passed, had an issue seperating arguments, check formatting',
        'marv.lightbox(error): Unable to apply class event to element, please check your attribute',
        'marv.lightbox(error): You have attempted to over-ride the images folder with a size that doesn\'t exist, please choose a size between 0 and ' + arg
      ][code];
    };
    console.log(messages(code, arg));
  };

  var imageSizes = function(e) {
    var sizes = [ '344x258', 'full', 'large1' ];
    if (sizes[e] !== undefined) {
      return sizes[e];
    }
    error(8, sizes.length);
  };

  // Initilise the plugin
  MarvLightbox.prototype.init = function() {

    if (this.options.imagesContainer === null) {
      error(0);
      return;
    }

    container = (instance.options.imagesContainer).objectType();
    if (container === null || container === undefined) {
      error(4);
      return;
    }

    debug(0);
    setupLightbox();
  };

  // Generate HTML from JSON
  function buildHTML(json) {
    "use strict";

    var handleAttribute = function(element, attribute, value) {
      if (value instanceof HTMLElement) {
        return element.appendChild(value);
      }
      switch (attribute) {
        case 'class':
        case 'src':
        case 'id':
        case 'data-click':
        case 'data-hover':
          return element.setAttribute(attribute, value);
        case 'content':
          element.innerHTML = value;
          return;
          // other keys...
        default:
          console.log(element.tagName, attribute, value);
                       }
    };
    var htmlReviver = function(key, value) {
      // parse as element
      if (isNaN(key) && typeof value === 'object') {
        var element = document.createElement(key);
        var subValue;

        for (var attribute in value) {
          handleAttribute(element, attribute, value[attribute]);
        }

        return element;
        // move element from { index: { tagName: Element } } to { index: Element }
      } else if (!isNaN(key)) {
        return value[Object.keys(value)[0]];
        // leave property alone
      } else {
        return value;
      }
    };
    try {
      var htmlObject = JSON.parse(json, htmlReviver);
      return htmlObject;
    } catch (e) {
      error(1, e);
    }
  }

  // Manage item change
  var images_compiled;
  var state = {
    c: null,
    altValue: null,
    maxValue: null,

    get current() { return this.c; },
    get max() { return this.maxValue; },
    get alt() { return this.altValue; },

    set max(e) { this.maxValue = e; },
    set alt(e) { this.altValue = e; },
    set current(e) {

      if (this.c !== null) {
        // Remove class from current
        (images_compiled[this.c].target).classList.remove('expand');
      }

      if (e === null) {
        debug(4);

        // Collapse Lightbox
        if (document.getElementById(instance.options.lightboxId)) {
          (images_compiled[this.c].target).classList.remove('expand');
          document.getElementsByTagName('body')[0].classList.remove(instance.options.activeClass);
          (document.getElementById(instance.options.lightboxId).parentNode).removeChild(document.getElementById(instance.options.lightboxId));
        }
        this.c = e;
        return;
      }

      // Change current element, update lightbox
      this.c = e;
      var lightbox = document.getElementById(instance.options.lightboxId),
          res;

      // Check lightbox exists, if so change the image src
      if (lightbox) {
        var image = images_compiled[e].image.src;
        if (instance.options.galleryFolder !== null) {
          var filter = image.match(/([^\/]*)/g).filter(Boolean);
          image = image.replace(filter[filter.length - 2], imageSizes(instance.options.galleryFolder));
        }
        lightbox.getElementsByTagName('img')[0].src = image;

        if (instance.options.showAlt) {
          debug(7);
          lightbox.getElementsByClassName(instance.options.lightboxId + '__alt')[0].innerHTML = images_compiled[e].alt;
        }

        if (instance.options.showMax && this.max > 1) {
          debug(8);
          lightbox.getElementsByClassName(instance.options.lightboxId + '__num')[0].innerHTML = (images_compiled[e].index + 1) + '/' + this.max;
        }

      } else {
        res = generateExpand(images_compiled[e]);
      }

      // Add active class
      if (res) {
        (images_compiled[e].target).classList.add('expand');
        document.getElementsByTagName('body')[0].classList.add(instance.options.lightboxId);
      }
    }
  };

  // Setup light box
  function setupLightbox() {
    var images;

    images = container.getElementsByTagName('img');

    if (instance.options.limit !== null) {
      var tmp = [];
      for (var i = 0, length = images.length; i < length; i++) {
        if (i < instance.options.limit) {
          tmp.push(images[i]);
        }
      }
      images = tmp;
    }

    if (images.length < 1 || images === undefined) {
      error(3);
      return;
    }

    try {
      images_compiled = Array.from(images, function(el) {
        // Generate array of objects containing image information
        instance.images_count++;
        return {
          target: function() {
            if (el.parentElement.nodeName === 'A') {
              return el.parentElement;
            }
            return el;
          }(),
          index: instance.images_count,
          alt: ((el.alt) ? el.alt : ''),
          image: function() {
            // If class put on an A link then find the image
            if (el.tagName === 'A') {
              return el.getElementsByTagName('img')[0];
            } else {
              return el;
            }
          }()
        };
      });
    } catch(e) {
      // Issue with generating array
      error(2, e);
    }

    debug(2, images_compiled.length);

    // Add image click event
    images_compiled.forEach(function(el) {
      if (el !== null) {
        var elm = el.target;

        elm.addEventListener('click', function(e) {
          if (elm.nodeName === 'A') {
            e.preventDefault();
          }

          instance.active = true;
          if (state.current === el.index) {
            state.current = null;
            return;
          }
          state.current = el.index;
          state.alt = el.alt;
        });
      }
    });
    state.max = images_compiled.length;
  }

  function generateExpand(img) {

    // Generate lightbox HTML and append
    var html = buildHTML(instance.options.html);
    instance.events = {
      EventClose: function(evt) {
        if (evt !== undefined) {
          evt.stopPropagation();
        }
        instance.active = false;
        state.current = null;
      },
      EventLeft: function(evt) {
        if (evt !== undefined) {
          evt.stopPropagation();
        }
        if (state.current !== 0 && state.max > 1) {
          state.current = state.current - 1;
        } else {
          state.current = instance.images_count;
        }
      },
      EventRight: function(evt) {
        if (evt !== undefined) {
          evt.stopPropagation();
        }
        if (state.current !== instance.images_count && state.max > 1) {
          state.current = state.current + 1;
        } else {
          state.current = 0;
        }
      },
      EventClass: function(evt) {
        var arg = (evt.dataset.hover).replace(/ /g,''),
            args = (arg.match(/[^(]*/g).filter(Boolean))[1].match(/(?:([^,()]+)?)+/g).filter(Boolean),
            target = args[0].objectType(),
            action = args[1].match(/^(.*):(.*)/).filter(Boolean);

        switch(action[1]) {
          case 'add':
            // Add class
            target.classList.add(action[2]);
          break;
          case 'remove':
           // Remove class
           target.classList.remove(action[2]);
          break;
          case 'toggle':
            target.classList.add(action[2]);
            evt.addEventListener('mouseout', function() {
              target.classList.remove(action[2]);
            });
          break;
          default:
            // Error
            error(7);
          break;
        }
      }
    };

    // Lightbox is active
    instance.active = true;

    // Assign event listeners
    Array.prototype.forEach.call(html.querySelectorAll('[data-click]'), function (e) {
      e.addEventListener('click', instance.events[eventName(e.dataset.click)]);
    });
    Array.prototype.forEach.call(html.querySelectorAll('[data-hover]'), function (e) {
      e.addEventListener('mouseover', function() { instance.events[eventName(e.dataset.hover)](e); });
    });

    // Insert lightbox into website
    var appendTo = (instance.options.appendTo).objectType();
    if (appendTo === null || (instance.options.appendTo).objectType() === undefined) {
      error(5, [instance.options.appendTo]);
      return false;
    }
    debug(5, ['id', instance.options.imagesContainer]);

    appendTo.insertBefore(html, appendTo.firstChild);
    return true;
  }

  MarvLightbox.prototype.current = function() {
    console.log(state);
  };

}());

如前所述,盡管state對象似乎在兩個實例之間共享,但它應該是唯一的。

問題就出在比如我認為:

var instance = null;
MarvLightbox = function() {
  instance = this;

因此,無論何時創建新的燈箱,實例都會指向它。 這也適用於異步回調函數,這些函數都將指向最后一個實例,而不是當前實例。 您可以在本地范圍內實例:

var instance=this;

同樣適用於狀態:

MarvLightbox.prototype.current = function() {
  console.log(this.state); //make it an objects property, not global
};

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM