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">
❮
</li>
<li class="tkp-slide-btn tkp-slide-btn-next">
❯
</li>
</ul>
</div>
</section>
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">
❮
</li>
<li class="tkp-slide-btn tkp-slide-btn-next">
❯
</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();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With