Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript prototype extend base-class unable to access base-class properties/methods from prototype class

I have created a javascript class TkpSlider being inspired from this w3schools page. (JSFiddle)

var TkpSlider = function (args) {
    args= args|| {};
};

var mainSwiper = new TkpSlider();

I have extended this to add some swipe ability being inspired from this page so that I can the slider works on user swipe. (JSFiddle)

var TkpSwiper = function (args) {
    TkpSlider.call(this, args);
};

TkpSwiper.prototype = Object.create(TkpSlider.prototype);

var mainSwiper = new TkpSwiper();

I separated the code so I don't get confused later from the base code and if any additional function. But with OOP I have to extend by creating a new name for it TkpSwiper, But I want to find another way to use the same name TkpSlider.

I saw this article and tried to use prototype to extend TkpSlider in this JSFiddle (Snippet below). The problem is I can't access the public methods of the base class from child class. The sample javascript in the blog I tested working though, I must be missing something with my code. Could anyone shed some insight? Thanks.

var TkpSlider = function (args) {
	args= args|| {};
    //private variable
     var btnPrev = args.btnPrev || "tkp-slide-btn-prev";//class for previous button (default: "tkp-slide-btn-prev")
    var btnNext = args.btnNext || "tkp-slide-btn-next";//class for next button (default: "tkp-slide-btn-next")
    var itemClass = args.itemClass || "tkp-slide-item"; //each item container class to slide (default: "tkp-slide-item")
    var isAutoSlide = args.isAutoSlide || false;//set auto slide (default: false)
    var autoSlideTimer = args.autoSlideTimer || 2000;//set auto slide timer when isAutoSlide is true(default: 2000)
    var hideBtnNav = args.hideBtnNav || false; //hide button navigation(default: false)
    var isRandom = args.isRandom || false; //whether next or previous slide should be random index
    var slideIndex = args.startIndex || 0; //start slide index number (zero based, index 0 = slide number 1, default :0)


    var itemNodes = document.getElementsByClassName(itemClass);
    var itemLength = itemNodes.length;
    var autoSlideInterval;

    //public variable
    this.testParentVar = "parent var";

    //init function
    init();
    function init() {
        if (itemLength > 0) {
            showSlide();
            bindEvent();
            isAutoSlide ? setAutoSlide() : "";
            hideBtnNav ? setHideBtnNav() : "";
        } else {
            throw "Cannot find slider item class";
        }

    }


    //private function
    function showSlide() {
        if (slideIndex >= itemLength) { slideIndex = 0; }
        if (slideIndex < 0) { slideIndex = (itemLength - 1); }
        for (var i = 0; i < itemLength; i++) {
            itemNodes[i].style.display = "none";
        }
        itemNodes[slideIndex].style.display = "block";
    }

    function nextPrevSlide(n) {
        slideIndex += n;
        showSlide();
    }

    function bindEvent() {
        var btnPrevNode = document.getElementsByClassName(btnPrev);
        if (btnPrevNode.length > 0) {
            btnPrevNode[0].addEventListener("click", function () {

                if (isRandom) {
                    setRandomSlideIndex();
                } else {
                    nextPrevSlide(-1);
                }


                if (isAutoSlide) {
                    clearInterval(autoSlideInterval);
                    setAutoSlide();
                }
            });
        } else {
            throw "Cannot find button previous navigation";
        }

        var btnNextNode = document.getElementsByClassName(btnNext);
        if (btnNextNode.length > 0) {
            btnNextNode[0].addEventListener("click", function () {
                if (isRandom) {
                    setRandomSlideIndex();
                } else {
                    nextPrevSlide(1);
                }
                if (isAutoSlide) {
                    clearInterval(autoSlideInterval);
                    setAutoSlide();
                }
            });
        } else {
            throw "Cannot find button next navigation";
        }

    }

    function setAutoSlide() {
        autoSlideInterval = setInterval(function () {
            if (isRandom) {
                setRandomSlideIndex();
            } else {
                nextPrevSlide(1);
            }

        }, autoSlideTimer);
    }

    function setHideBtnNav() {
        document.getElementsByClassName(btnPrev)[0].style.display = "none";
        document.getElementsByClassName(btnNext)[0].style.display = "none";
    }

    function setRandomSlideIndex() {
        var randomIndex = Math.floor(Math.random() * itemLength);
        if (randomIndex == slideIndex) {
            setRandomSlideIndex();
        } else {
            slideIndex = randomIndex;
            showSlide();
        }
    }

    //public function
    this.nextPrevSlide = function (n) {
        nextPrevSlide(n);
        //console.log("nextPrevSlide");
    };

    //getter setter
    this.setItemClass = function (prm) {
        itemClass = prm;
    };
    this.getItemClass = function () {
        return itemClass;
    };


};

//Adding touch swiper
TkpSlider.prototype = function () {
    var self = this;
    var touchStartCoords = { 'x': -1, 'y': -1 }, // X and Y coordinates on mousedown or touchstart events.
        touchEndCoords = { 'x': -1, 'y': -1 },// X and Y coordinates on mouseup or touchend events.
        direction = 'undefined',// Swipe direction
        minDistanceXAxis = 30,// Min distance on mousemove or touchmove on the X axis
        maxDistanceYAxis = 30,// Max distance on mousemove or touchmove on the Y axis
        maxAllowedTime = 1000,// Max allowed time between swipeStart and swipeEnd
        startTime = 0,// Time on swipeStart
        elapsedTime = 0,// Elapsed time between swipeStart and swipeEnd
        //targetElement = document.getElementById('el'), // Element to delegate
        targetElements = self.getItemClass()
        ;

    init();
    function init() {
       
        addMultipleListeners(targetElements, 'mousedown touchstart', swipeStart);
        addMultipleListeners(targetElements, 'mousemove touchmove', swipeMove);
        addMultipleListeners(targetElements, 'mouseup touchend', swipeEnd);
    }

    function swipeStart(e) {
        e = e ? e : window.event;
        e = ('changedTouches' in e) ? e.changedTouches[0] : e;
        touchStartCoords = { 'x': e.pageX, 'y': e.pageY };
        startTime = new Date().getTime();
        //targetElement.textContent = " ";
    }

    function swipeMove(e) {
        e = e ? e : window.event;
        e.preventDefault();
    }

    function swipeEnd(e) {
        e = e ? e : window.event;
        e = ('changedTouches' in e) ? e.changedTouches[0] : e;
        touchEndCoords = { 'x': e.pageX - touchStartCoords.x, 'y': e.pageY - touchStartCoords.y };
        elapsedTime = new Date().getTime() - startTime;
        if (elapsedTime <= maxAllowedTime) {
            if (Math.abs(touchEndCoords.x) >= minDistanceXAxis && Math.abs(touchEndCoords.y) <= maxDistanceYAxis) {
                direction = (touchEndCoords.x < 0) ? 'left' : 'right';
                switch (direction) {
                    case 'left':
                        //targetElement.textContent = "Left swipe detected";
                        console.log("swipe left");
                        self.nextPrevSlide(1);

                        break;
                    case 'right':
                        // targetElement.textContent = "Right swipe detected";
                        console.log("swipe right");
                        self.nextPrevSlide(-1);

                        break;
                }
            }
        }
    }

    function addMultipleListeners(el, s, fn) {
        var evts = s.split(' ');
        var elLength = el.length;
        for (var i = 0, iLen = evts.length; i < iLen; i++) {
            if (elLength > 1) {
                for (var j = 0; j < elLength; j++) {
                    el[j].addEventListener(evts[i], fn, false);
                }
            } else {
                el.addEventListener(evts[i], fn, false);
            }

        }
    }
}();

var mainSwiper = new TkpSlider();
.tkp-slide {
  width: 100%;
  position: relative; }
  .tkp-slide .tkp-slide-item:not(:first-child) {
    display: none; }
    .tkp-slide .tkp-slide-item img {
      width: 100%; }
  .tkp-slide .tkp-slide-btn {
    color: #fff !important;
    background-color: #000 !important;
    border: none;
    display: inline-block;
    outline: 0;
    padding: 8px 16px;
    vertical-align: middle;
    overflow: hidden;
    text-decoration: none;
    text-align: center;
    cursor: pointer;
    white-space: nowrap;
    opacity: 0.7; }
    .tkp-slide .tkp-slide-btn:hover {
      background-color: #333 !important; }
  .tkp-slide .tkp-slide-btn-prev {
    position: absolute;
    top: 50%;
    left: 0%;
    transform: translate(0%, -50%);
    -webkit-transform: translate(0%, -50%);
    -ms-transform: translate(0%, -50%); }
    .tkp-slide .tkp-slide-btn-next {
      position: absolute;
      top: 50%;
      right: 0%;
      transform: translate(0%, -50%);
      -ms-transform: translate(0%, -50%); }
<section class="main-slide">
  <div class="tkp-slide tkp-slide--container">
    <div class="tkp-slide-item">
      <a href="javascript:void(0)">
        <img src="https://dummyimage.com/400x200/f2d9f2/080708&text=1" alt="Slide 1" />
      </a>
    </div>
    <div class="tkp-slide-item">
      <a href="javascript:void(0)">
        <img src="https://dummyimage.com/400x200/f2d9f2/080708&text=2" alt="Slide 2" />
      </a>
    </div>
    <div class="tkp-slide-item">
      <a href="javascript:void(0)">
        <img src="https://dummyimage.com/400x200/f2d9f2/080708&text=3" alt="Slide 3" />
      </a>
    </div>
    <div class="tkp-slide-item">
      <a href="javascript:void(0)">
        <img src="https://dummyimage.com/400x200/f2d9f2/080708&text=4" alt="Slide 4" />
      </a>
    </div>
    <div class="tkp-slide-item">
      <a href="javascript:void(0)">
        <img src="https://dummyimage.com/400x200/f2d9f2/080708&text=5" alt="Slide 5" />
      </a>
    </div>
    <ul>
      <li class="tkp-slide-btn tkp-slide-btn-prev">
        &#10094;
      </li>
      <li class="tkp-slide-btn tkp-slide-btn-next">
        &#10095;
      </li>
    </ul>
  </div>
</section>
like image 211
Ardeus Avatar asked Feb 02 '18 12:02

Ardeus


1 Answers

Problem 1: You are not returning anything from the prototype function. You have to return each public method as properties of the return Object.

return {
  initSwiper : init,
  method1 : method1,
  method2 : method2,
};

Problem 2: You need to set variables inside the init function. Outside it this variable won't refer to TkpSlider.

    // init();
    function init() {
        self=this;
        var targetElements = document.getElementsByClassName(this.getItemClass());
        addMultipleListeners(targetElements, 'mousedown touchstart', swipeStart);
        addMultipleListeners(targetElements, 'mousemove touchmove', swipeMove);
        addMultipleListeners(targetElements, 'mouseup touchend', swipeEnd);
        return this;
    }

Problem 3 You need to manually call the initSwiper() method to get your variables initialized when you create the TkpSlider Object.

var mainSwiper = new TkpSlider().initSwiper();

Then it will work as you need and you can call public methods if you need.

mainSwiper.method1();
mainSwiper.method2();

Working Snippet:

var TkpSlider = function(args) {
  args = args || {};
  //private variable
  var btnPrev = args.btnPrev || "tkp-slide-btn-prev"; //class for previous button (default: "tkp-slide-btn-prev")
  var btnNext = args.btnNext || "tkp-slide-btn-next"; //class for next button (default: "tkp-slide-btn-next")
  var itemClass = args.itemClass || "tkp-slide-item"; //each item container class to slide (default: "tkp-slide-item")
  var isAutoSlide = args.isAutoSlide || false; //set auto slide (default: false)
  var autoSlideTimer = args.autoSlideTimer || 2000; //set auto slide timer when isAutoSlide is true(default: 2000)
  var hideBtnNav = args.hideBtnNav || false; //hide button navigation(default: false)
  var isRandom = args.isRandom || false; //whether next or previous slide should be random index
  var slideIndex = args.startIndex || 0; //start slide index number (zero based, index 0 = slide number 1, default :0)


  var itemNodes = document.getElementsByClassName(itemClass);
  var itemLength = itemNodes.length;
  var autoSlideInterval;

  //public variable
  this.testParentVar = "parent var";

  //init function
  init();

  function init() {
    if (itemLength > 0) {
      showSlide();
      bindEvent();
      isAutoSlide ? setAutoSlide() : "";
      hideBtnNav ? setHideBtnNav() : "";
    } else {
      throw "Cannot find slider item class";
    }

  }


  //private function
  function showSlide() {
    if (slideIndex >= itemLength) {
      slideIndex = 0;
    }
    if (slideIndex < 0) {
      slideIndex = (itemLength - 1);
    }
    for (var i = 0; i < itemLength; i++) {
      itemNodes[i].style.display = "none";
    }
    itemNodes[slideIndex].style.display = "block";
  }

  function nextPrevSlide(n) {
    slideIndex += n;
    showSlide();
  }

  function bindEvent() {
    var btnPrevNode = document.getElementsByClassName(btnPrev);
    if (btnPrevNode.length > 0) {
      btnPrevNode[0].addEventListener("click", function() {

        if (isRandom) {
          setRandomSlideIndex();
        } else {
          nextPrevSlide(-1);
        }


        if (isAutoSlide) {
          clearInterval(autoSlideInterval);
          setAutoSlide();
        }
      });
    } else {
      throw "Cannot find button previous navigation";
    }

    var btnNextNode = document.getElementsByClassName(btnNext);
    if (btnNextNode.length > 0) {
      btnNextNode[0].addEventListener("click", function() {
        if (isRandom) {
          setRandomSlideIndex();
        } else {
          nextPrevSlide(1);
        }
        if (isAutoSlide) {
          clearInterval(autoSlideInterval);
          setAutoSlide();
        }
      });
    } else {
      throw "Cannot find button next navigation";
    }

  }

  function setAutoSlide() {
    autoSlideInterval = setInterval(function() {
      if (isRandom) {
        setRandomSlideIndex();
      } else {
        nextPrevSlide(1);
      }

    }, autoSlideTimer);
  }

  function setHideBtnNav() {
    document.getElementsByClassName(btnPrev)[0].style.display = "none";
    document.getElementsByClassName(btnNext)[0].style.display = "none";
  }

  function setRandomSlideIndex() {
    var randomIndex = Math.floor(Math.random() * itemLength);
    if (randomIndex == slideIndex) {
      setRandomSlideIndex();
    } else {
      slideIndex = randomIndex;
      showSlide();
    }
  }

  //public function
  this.nextPrevSlide = function(n) {
    nextPrevSlide(n);
    //console.log("nextPrevSlide");
  };

  //getter setter
  this.setItemClass = function(prm) {
    itemClass = prm;
  };
  this.getItemClass = function() {
    return itemClass;
  };


};

//Adding touch swiper
TkpSlider.prototype = function() {
  var self;
  var touchStartCoords = {
      'x': -1,
      'y': -1
    }, // X and Y coordinates on mousedown or touchstart events.
    touchEndCoords = {
      'x': -1,
      'y': -1
    }, // X and Y coordinates on mouseup or touchend events.
    direction = 'undefined', // Swipe direction
    minDistanceXAxis = 30, // Min distance on mousemove or touchmove on the X axis
    maxDistanceYAxis = 30, // Max distance on mousemove or touchmove on the Y axis
    maxAllowedTime = 1000, // Max allowed time between swipeStart and swipeEnd
    startTime = 0, // Time on swipeStart
    elapsedTime = 0, // Elapsed time between swipeStart and swipeEnd
    //targetElement = document.getElementById('el'), // Element to delegate
    targetElements; //this.prototype.getItemClass()
  ;

  //    init();
  function initSwiper() {
    self = this;
    var targetElements = document.getElementsByClassName(this.getItemClass());
    addMultipleListeners(targetElements, 'mousedown touchstart', swipeStart);
    addMultipleListeners(targetElements, 'mousemove touchmove', swipeMove);
    addMultipleListeners(targetElements, 'mouseup touchend', swipeEnd);
    return this;
  }

  function swipeStart(e) {
    e.preventDefault();
    e = e ? e : window.event;
    e = ('changedTouches' in e) ? e.changedTouches[0] : e;
    touchStartCoords = {
      'x': e.pageX,
      'y': e.pageY
    };
    startTime = new Date().getTime();
    //targetElement.textContent = " ";
  }

  function swipeMove(e) {
    e = e ? e : window.event;
    e.preventDefault();
  }

  function swipeEnd(e) {
    e.preventDefault();
    e = e ? e : window.event;
    e = ('changedTouches' in e) ? e.changedTouches[0] : e;
    touchEndCoords = {
      'x': e.pageX - touchStartCoords.x,
      'y': e.pageY - touchStartCoords.y
    };
    elapsedTime = new Date().getTime() - startTime;
    if (elapsedTime <= maxAllowedTime) {
      if (Math.abs(touchEndCoords.x) >= minDistanceXAxis && Math.abs(touchEndCoords.y) <= maxDistanceYAxis) {
        direction = (touchEndCoords.x < 0) ? 'left' : 'right';
        switch (direction) {
          case 'left':
            //targetElement.textContent = "Left swipe detected";
            console.log("swipe left");
            self.nextPrevSlide(1);

            break;
          case 'right':
            // targetElement.textContent = "Right swipe detected";
            console.log("swipe right");
            self.nextPrevSlide(-1);

            break;
        }
      }
    }
  }

  function addMultipleListeners(el, s, fn) {
    var evts = s.split(' ');
    var elLength = el.length;
    for (var i = 0, iLen = evts.length; i < iLen; i++) {
      if (elLength > 1) {
        for (var j = 0; j < elLength; j++) {
          el[j].addEventListener(evts[i], fn, false);
        }
      } else {
        el.addEventListener(evts[i], fn, false);
      }

    }
  }
  return {
    initSwiper: initSwiper
  };
}();

var mainSwiper = new TkpSlider().initSwiper();
.tkp-slide {
  width: 100%;
  position: relative;
}

.tkp-slide .tkp-slide-item:not(:first-child) {
  display: none;
}

.tkp-slide .tkp-slide-item img {
  width: 100%;
}

.tkp-slide .tkp-slide-btn {
  color: #fff !important;
  background-color: #000 !important;
  border: none;
  display: inline-block;
  outline: 0;
  padding: 8px 16px;
  vertical-align: middle;
  overflow: hidden;
  text-decoration: none;
  text-align: center;
  cursor: pointer;
  white-space: nowrap;
  opacity: 0.7;
}

.tkp-slide .tkp-slide-btn:hover {
  background-color: #333 !important;
}

.tkp-slide .tkp-slide-btn-prev {
  position: absolute;
  top: 50%;
  left: 0%;
  transform: translate(0%, -50%);
  -webkit-transform: translate(0%, -50%);
  -ms-transform: translate(0%, -50%);
}

.tkp-slide .tkp-slide-btn-next {
  position: absolute;
  top: 50%;
  right: 0%;
  transform: translate(0%, -50%);
  -ms-transform: translate(0%, -50%);
}
<section class="main-slide">
  <div class="tkp-slide tkp-slide--container">
    <div class="tkp-slide-item">
      <a href="javascript:void(0)">
        <img src="https://dummyimage.com/400x200/f2d9f2/080708&text=1" alt="Slide 1" />
      </a>
    </div>
    <div class="tkp-slide-item">
      <a href="javascript:void(0)">
        <img src="https://dummyimage.com/400x200/f2d9f2/080708&text=2" alt="Slide 2" />
      </a>
    </div>
    <div class="tkp-slide-item">
      <a href="javascript:void(0)">
        <img src="https://dummyimage.com/400x200/f2d9f2/080708&text=3" alt="Slide 3" />
      </a>
    </div>
    <div class="tkp-slide-item">
      <a href="javascript:void(0)">
        <img src="https://dummyimage.com/400x200/f2d9f2/080708&text=4" alt="Slide 4" />
      </a>
    </div>
    <div class="tkp-slide-item">
      <a href="javascript:void(0)">
        <img src="https://dummyimage.com/400x200/f2d9f2/080708&text=5" alt="Slide 5" />
      </a>
    </div>
    <ul>
      <li class="tkp-slide-btn tkp-slide-btn-prev">
        &#10094;
      </li>
      <li class="tkp-slide-btn tkp-slide-btn-next">
        &#10095;
      </li>
    </ul>
  </div>
</section>

Prototype confusion: which this is this?

TkpSlider.prototype = function() {

  console.log(this); /* this this is Child */
  init();

  function init() {
    console.log(this); /* this this is Child, called from child context */
  }

  function initSwiper() {
    console.log(this); /* this this is Parent, called from parent context */
  }

  function swipeStartEventHandler(event) {
    console.log(this); /* this this is HTML element #customId*/
  }

  return {
    initSwiper: initSwiper,
  };
}();

new TkpSlider().initSwiper();
like image 194
Munim Munna Avatar answered Sep 24 '22 10:09

Munim Munna