Instances conflicting on events - javascript

So I am trying to make a dropdown. It works fine except for when I click on one to expand and then click on the other, then when I make a selection on one of them both close. What is the work around this as well as well as what is the best practice/approach for this situations.
var extend = function(){
if(!arguments.length)
return {};
else if (arguments.length == 1)
return arguments[0];
var primary = arguments[0];
for(var v = 1; v < arguments.length; ++v){
for(prop in arguments[v]){
primary[prop] = arguments[v][prop];
}
}
return primary;
};
var Dropdown = (function(){
self = undefined;
Dropdown.instances = [];
function Dropdown(element, options){
self = this;
this.settings = extend(this.defaults, options);
console.log(this.settings);
this.element = this.getElement(element);
this.trigger = this.getElement(options.trigger);
if(!this.element)
throw new Error('No element found.');
if(!this.trigger)
throw new Error('No trigger found.');
if(!this.settings.optionSelector)
throw new Error('Option Selector Not Defined.');
if(this.element.dropdown)
throw new Error('Dropdown already exists.');
this.element.dropdown = this;
Dropdown.instances.push(this);
this.attachTriggerListener = function(event, trigger, dropdown){
trigger.addEventListener(event, function(e){
e.stopPropagation();
dropdown.classList.remove('hidden');
dropdown.classList.add('visible');
}, false);
document.addEventListener('click', function(e){
dropdown.classList.remove('visible');
dropdown.classList.add('hidden');
trigger.innerHTML = e.target.innerHTML;
}, false);
};
this.init();
}
Dropdown.prototype.defaults = {
css: '',
optionSelector: undefined,
trigger: undefined,
triggersOn: 'click',
onShow: function(){},
onClose: function(){},
};
Dropdown.prototype.init = function(){
this.element.classList.add('g-dropdown');
this.element.classList.add('hidden');
this.attachTriggerListener(this.settings.triggersOn, this.trigger, this.element);
};
Dropdown.prototype.getElement = function(object){
if(typeof object == 'object' &&
object instanceof HTMLElement)
return object;
else if(typeof object == 'string')
return document.querySelector(object);
};
return Dropdown;
}());
var dropdown = new Dropdown('#select', {
optionSelector: 'li',
trigger: '#trigger'
});
var dropdown = new Dropdown('#select2', {
optionSelector: 'li',
trigger: '#trigger2'
});
.g-dropdown{
max-height: 100px;
max-width: 75px;
overflow: scroll;
-webkit-transition: visibility 0.50s, height 0.50s;
-moz-transition: visibility 0.50s, height 0.50s;
transition: visibility 0.50s, height 0.50s;
}
.g-dropdown.visible{
visibility: visible;
height: 100px;
}
.g-dropdown.hidden{
visibility: hidden;
height: 0px;
}
<button id='trigger'>Click Me</button>
<ul id='select' style='padding: 0; margin: 0;'>
<li>One-one</li>
<li>Two-two</li>
<li>Three-three</li>
<li>Four-four</li>
<li>Five-five</li>
</ul>
<br/><br/><br/>
<button id='trigger2'>Click Me</button>
<ul id='select2' style='padding: 0; margin: 0;'>
<li>One-one</li>
<li>Two-two</li>
<li>Three-three</li>
<li>Four-four</li>
<li>Five-five</li>
</ul>

So how would I have to modify it so it also closes the dropdown on
clicking outside the dropdown?
Try checking e.target.parentElement.nodeName , this.element.nodeName at if condition within document.addEventListener handler , adding , removing class of dropdown if parent element is ul , else adjusting class of clicked li parent element ul
document.addEventListener('click', function(e){
if (e.target.parentElement.nodeName !== this.element.nodeName) {
dropdown.classList.remove('visible');
dropdown.classList.add('hidden');
} else {
e.target.parentElement.classList.remove('visible');
e.target.parentElement.classList.add('hidden');
}
self.settings.onClose(e.target);
}.bind(this), false);
var extend = function(){
if(!arguments.length)
return {};
else if (arguments.length == 1)
return arguments[0];
var primary = arguments[0];
for(var v = 1; v < arguments.length; ++v){
for(prop in arguments[v]){
primary[prop] = arguments[v][prop];
}
}
return primary;
};
var Dropdown = (function(){
self = undefined;
Dropdown.instances = [];
function Dropdown(element, options){
self = this;
this.settings = extend(this.defaults, options);
console.log(this.settings);
this.element = this.getElement(element);
this.trigger = this.getElement(options.trigger);
if(!this.element)
throw new Error('No element found.');
if(!this.trigger)
throw new Error('No trigger found.');
/*
if(!this.settings.optionSelector)
throw new Error('Option Selector Not Defined.');
*/
if(this.element.dropdown)
throw new Error('Dropdown already exists.');
this.element.dropdown = this;
Dropdown.instances.push(this);
this.attachTriggerListener = function(event, trigger, dropdown){
trigger.addEventListener(event, function(e){
e.stopPropagation();
dropdown.classList.remove('hidden');
dropdown.classList.add('visible');
}, false);
document.addEventListener('click', function(e){
if (e.target.parentElement.nodeName !== this.element.nodeName) {
dropdown.classList.remove('visible');
dropdown.classList.add('hidden');
} else {
e.target.parentElement.classList.remove('visible');
e.target.parentElement.classList.add('hidden');
}
self.settings.onClose(e.target);
}.bind(this), false);
};
this.init();
}
Dropdown.prototype.defaults = {
css: '',
optionSelector: undefined,
trigger: undefined,
triggersOn: 'click',
onShow: function(){},
onClose: function(){},
};
Dropdown.prototype.init = function(){
this.element.classList.add('g-dropdown');
this.element.classList.add('hidden');
this.attachTriggerListener(this.settings.triggersOn, this.trigger, this.element);
};
Dropdown.prototype.getElement = function(object){
if(typeof object == 'object' &&
object instanceof HTMLElement)
return object;
else if(typeof object == 'string')
return document.querySelector(object);
};
return Dropdown;
}());
var dropdown1 = new Dropdown('#select', {
optionSe1lector: 'li',
trigger: '#trigger'
});
var dropdown2 = new Dropdown('#select2', {
optionSe2lector: 'li',
trigger: '#trigger2'
});
.g-dropdown{
max-height: 100px;
max-width: 75px;
overflow: scroll;
-webkit-transition: visibility 0.50s, height 0.50s;
-moz-transition: visibility 0.50s, height 0.50s;
transition: visibility 0.50s, height 0.50s;
}
.g-dropdown.visible{
visibility: visible;
height: 100px;
}
.g-dropdown.hidden{
visibility: hidden;
height: 0px;
}
<button id='trigger'>Click Me</button>
<ul id='select' style='padding: 0; margin: 0;'>
<li>One-one</li>
<li>Two-two</li>
<li>Three-three</li>
<li>Four-four</li>
<li>Five-five</li>
</ul>
<br/><br/><br/>
<button id='trigger2'>Click Me</button>
<ul id='select2' style='padding: 0; margin: 0;'>
<li>One-one</li>
<li>Two-two</li>
<li>Three-three</li>
<li>Four-four</li>
<li>Five-five</li>
</ul>

You could use event.currentTarget to know which element triggers the event and so find the element you need this target to show, that way your code will shorter and simpler
Try something like this:
var btn1 = document.getElementById('btn-1');
var btn2 = document.getElementById('btn-2');
function toggle(event) {
var nextNode = event.currentTarget.nextElementSibling;
nextNode.classList.toggle('disable');
nextNode.classList.toggle('enable');
}
btn1.addEventListener('click', toggle, false);
btn2.addEventListener('click', toggle, false);
.disable {
max-height: 0px;
overflow: hidden
}
.enable {
max-height: initial;
overflow: initial
}
<div class="first-container">
<button id="btn-1">Click Me</button>
<ul id="list-1" class="disable">
<li>item-1</li>
<li>item-2</li>
<li>item-3</li>
<li>item-4</li>
<li>item-5</li>
</ul>
</div>
<div class="second-container">
<button id="btn-2">Click Me</button>
<ul id="list-2" class="disable">
<li>item-1</li>
<li>item-2</li>
<li>item-3</li>
<li>item-4</li>
<li>item-5</li>
</ul>
</div>

Related

How could I make this active onclick?

var span = document.getElementById('loading_dots');
var int = setInterval(function() {
if ((span.innerHTML += '●').length == 4)
span.innerHTML = '';
}, 400);
(function(){
var loading_dots = document.getElementById("loading_dots"),
show = function(){
loading_dots.style.display = "block";
setTimeout(hide, 5000); // 5 seconds
},
hide = function(){
loading_dots.style.display = "none";
};
show();
})();
How can I make it so loading_dots start on the click of a button, and re-activates everytime I click the button? the bottom function is to stop it after 5 seconds, maybe could merge it into one function?
Needs to work for 3 seperate buttons and relaunch on click of each, also needs to display inside of <span class="loading_dots" id="loading_dots"></span> any method is fine, css, jquery, or javascript
here is a jQuery version:
(function ( $ ) {
$.fn.loader = function( options ) {
var settings = $.extend({
text:"●",
spn: undefined
}, options );
$.each(this, function(){
var btn = this;
var int;
var spn;
if (settings.spn === undefined) {
spn = $("<span/>" , { "class":"loading_dots" });
$(btn).append(spn);
} else {
spn= $(settings.spn);
}
var show = function(){
btn.setAttribute("disabled", "disabled")
clearInterval(int);
spn.show();
int = setInterval(function() {
if ((spn[0].innerHTML += settings.text).length == 4)
spn.html("");
}, 400);
setTimeout(hide, 5000); // 5 seconds
}
var hide = function (){
spn.hide();
btn.removeAttribute("disabled", "disabled")
clearInterval(int);
}
btn.addEventListener("click", show);
});
};
}( jQuery ));
// now bind it by its class, this only need to be run once every time new button is added to the html
$(".btn").loader({spn:".loading_dots"});
// and you could also specify the text by
// $(".btn").loader({text: "*"});
.loading_dots {
color:red;
display:none;
width:100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<span class="loading_dots"></span>
<button class="btn" type="button" >
submit
</button>
<button class="btn" type="button" >
submit
</button>
</div>
If you want to add an event listener for a button click, just select the buttons, and add the listeners in a loop:
document.querySelectorAll("button").forEach(e => e.addEventListener("click", myFunc));
Alternatively, listen for any click, then check if the event's target is a button:
document.addEventListener("click", (e) => if (e.target.tagName == "BUTTON") myFunc());
You could use CSS for the most part of your code, and than simply toggle a show class on the parent #loading element:
const Loading = () => {
let tOut = null;
const el = document.querySelector("#loading");
const show = () => {
el.classList.add('show');
tOut = setTimeout(hide, 5000);
};
const hide = () => {
el.classList.remove('show');
clearTimeout(tOut);
};
return {
show,
hide
};
};
const loadingDots = Loading();
const loadBtns = document.querySelectorAll('.load');
[...loadBtns].forEach(el => el.addEventListener('click', loadingDots.show));
// you can always use loadingDots.hide() to hide when needed (before the 5sec ticks out)
#loading {
position: fixed;
z-index: 100;
top:0;
left: 0;
width:100vw;
height:100vh;
display:flex;
background: rgba(0,0,0,0.5);
color: #fff;
font-size: 3em;
align-items: center;
justify-content:center;
visibility: hidden;
opacity: 0;
transition: 0.4s;
}
#loading.show {
opacity: 1;
visibility: visible;
}
#keyframes blink {
50% {opacity: 1;}
}
#loading i:after {content: "\25cf";}
#loading i { opacity: 0; animation: blink 1.2s infinite; }
#loading i:nth-child(2) { animation-delay: .2s; }
#loading i:nth-child(3) { animation-delay: .4s; }
<div id="loading"><i></i><i></i><i></i></div>
<button class="load">LOAD</button>
<button class="load">LOAD</button>
<button class="load">LOAD</button>
A plain javascript version with the option to programmatically/manually stop displaying the loading dots. Just pass the id of the parent element you want the loading to be attached to. By default the loading will be appended to the parent but you can optionally pass an object as the last parameter with a position property.
function removeLoading(id) {
var parent = document.getElementById(id);
var spans = parent.getElementsByClassName("loading_dots");
while (spans.length > 0) {
var span = spans[0];
if (span.dataset.timerId) {
clearTimeout(span.dataset.timerId);
}
span.remove();
}
}
function addLoading(id, options) {
options = options || {};
var parent = document.getElementById(id);
var existingSpans = parent.getElementsByClassName("loading_dots");
if (existingSpans.length > 0) {
removeLoading(id);
}
var span = document.createElement("span");
span.setAttribute("class", "loading_dots");
if (options.timerId) {
span.dataset.timerId = options.timerId;
}
parent.insertAdjacentElement(options.position || "beforeend", span);
setInterval(function () {
if ((span.innerHTML += '●').length == 4)
span.innerHTML = '';
}, 400)
}
function addLoadingWithTimeout(id, ms, options) {
options = options || {};
var timerId = setTimeout(function () { removeLoading(id) }, ms);
options.timerId = timerId;
addLoading(id, options);
}
<p id="load1">Load 1 - Will stop automatically in 3 seconds after starting. </p>
<button onclick="addLoadingWithTimeout('load1', 3000)">Start Load 1</button>
<button onclick="removeLoading('load1')">Stop Load 1</button>
<p id="load2">Load 2 - Only manual Stop </p>
<button onclick="addLoading('load2')">Start Load 2</button>
<button onclick="removeLoading('load2')">Stop Load 2</button>
Here you go. on the HTML side, you just pass the event to the button that you want and then the id, as a string, of the span/div where you want the load icons to appear.
HTML:
<button id="btn" onclick="load(event, 'loadDiv')">Load</button>
<div>
<span id="loadDiv"></span>
</div>
Below, we are getting the btn id from event so you don't have to manually pass it everytime. Then we are defining function for the innerhtml icons. Lastly, we are running the showIcon function every .4s and then clearing the interval after 5 seconds.
JS:
function load(e, location) {
var btn = document.getElementById(e.srcElement.id)
var loadDiv = document.getElementById(location)
function showLoad() {
if (loadDiv.innerHTML.length < 3) {
return loadDiv.innerHTML += '●'
}
loadDiv.innerHTML = ''
}
(function() {
var loadIcons = setInterval(function() {
showLoad()
}, 400)
var clear = setTimeout(function() {
clearInterval(loadIcons)
}, 5000)
})()
}
Hope this helps!
You can define your code in a function and add click handler to the button.
function myFunc() {
var span = document.getElementById('loading_dots');
var int = setInterval(function() {
if ((span.innerHTML += '●').length == 4)
span.innerHTML = '';
}, 400);
(function(){
var loading_dots = document.getElementById("loading_dots"),
show = function(){
loading_dots.style.display = "block";
setTimeout(hide, 5000); // 5 seconds
},
hide = function(){
loading_dots.style.display = "none";
};
show();
})();
}
document.getElementById("myBtn1").addEventListener("click", myFunc);
document.getElementById("myBtn2").addEventListener("click", myFunc);

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">
❮
</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();

Can't manage to close Coverpop popup

I'm using CoverPop to display a popup to my customers. Everything seems so easy but somehow I'm to dumb to make the popup closeable. I have inserted a "close" link, as described in the setup. However when I click on it, nothing happens.
Only way to close the popup is by pressing the escape key on my keyboard.
I know this must be ridiculous for some of you. I'd really appreciate some help though.
Thanks.
HTML
<script>
CoverPop.start({});
</script>
<div id="CoverPop-cover" class="splash">
<div class="CoverPop-content splash-center">
<h2 class="splash-title">Willkommen bei Exsys <span class="bold">Schweiz</span></h2>
<p class="splash-intro">Kunden aus Deutschland und anderen EU-Ländern wechseln bitte zu unserer <span class="bold">deutschen</span> Seite.</p>
<img src="{$ShopURL}/templates/xt_grid/img/shop-ch.png" title="EXSYS Online-Shop Schweiz" height="60" style="margin: 0 20px 0 0;" alt="Schweizer Exsys-Shop"/>
<img src="{$ShopURL}/templates/xt_grid/img/shop-de.png" height="60" alt="Shop Deutschland"/>
<p class="close-splash"><a class="CoverPop-close" href="#">Close</a></p>
</div><!--end .splash-center -->
</div><!--end .splash -->
CSS
.CoverPop-open,
.CoverPop-open body {
overflow: hidden;
}
#CoverPop-cover {
display: none;
position: fixed;
overflow-y: scroll;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1000;
-webkit-animation: fade-in .25s ease-in;
-moz-animation-name: fade-in .25s ease-in;
-ms-animation-name: fade-in .25s ease-in;
-o-animation-name: fade-in .25s ease-in;
animation-name: fade-in .25s ease-in;
}
.CoverPop-open #CoverPop-cover {
display: block;
}
.splash {
background-color:rgba(47, 99, 135, 0.9);
}
.splash-center {
background-color: white;
border-right: 8px solid #007ec8;
border-bottom: 8px solid #007ec8;
border-left: 8px solid #007ec8;
margin: 15px;
text-align: center;
top: 7px;
width: 15%;
}
.splash-center p{
margin: 20px 10px;
}
.splash-center h2{
font-size:16px;
width: 100%;
background:#007ec8;
padding: 10px 0;
color:#FFF;
}
JS
(function (CoverPop, undefined) {
'use strict';
// set default settings
var settings = {
// set default cover id
coverId: 'CoverPop-cover',
// duration (in days) before it pops up again
expires: 30,
// close if someone clicks an element with this class and prevent default action
closeClassNoDefault: 'CoverPop-close',
// close if someone clicks an element with this class and continue default action
closeClassDefault: 'CoverPop-close-go',
// change the cookie name
cookieName: '_ExsPop',
// on popup open function callback
onPopUpOpen: null,
// on popup close function callback
onPopUpClose: null,
// hash to append to url to force display of popup
forceHash: 'splash',
// hash to append to url to delay popup for 1 day
delayHash: 'go',
// close if the user clicks escape
closeOnEscape: true,
// set an optional delay (in milliseconds) before showing the popup
delay: 2000,
// automatically close the popup after a set amount of time (in milliseconds)
hideAfter: null
},
// grab the elements to be used
$el = {
html: document.getElementsByTagName('html')[0],
cover: document.getElementById(settings.coverId),
closeClassDefaultEls: document.querySelectorAll('.' + settings.closeClassDefault),
closeClassNoDefaultEls: document.querySelectorAll('.' + settings.closeClassNoDefault)
},
/**
* Helper methods
*/
util = {
hasClass: function(el, name) {
return new RegExp('(\\s|^)' + name + '(\\s|$)').test(el.className);
},
addClass: function(el, name) {
if (!util.hasClass(el, name)) {
el.className += (el.className ? ' ' : '') + name;
}
},
removeClass: function(el, name) {
if (util.hasClass(el, name)) {
el.className = el.className.replace(new RegExp('(\\s|^)' + name + '(\\s|$)'), ' ').replace(/^\s+|\s+$/g, '');
}
},
addListener: function(target, type, handler) {
if (target.addEventListener) {
target.addEventListener(type, handler, false);
} else if (target.attachEvent) {
target.attachEvent('on' + type, handler);
}
},
removeListener: function(target, type, handler) {
if (target.removeEventListener) {
target.removeEventListener(type, handler, false);
} else if (target.detachEvent) {
target.detachEvent('on' + type, handler);
}
},
isFunction: function(functionToCheck) {
var getType = {};
return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';
},
setCookie: function(name, days) {
var date = new Date();
date.setTime(+ date + (days * 86400000));
document.cookie = name + '=true; expires=' + date.toGMTString() + '; path=/';
},
hasCookie: function(name) {
if (document.cookie.indexOf(name) !== -1) {
return true;
}
return false;
},
// check if there is a hash in the url
hashExists: function(hash) {
if (window.location.hash.indexOf(hash) !== -1) {
return true;
}
return false;
},
preventDefault: function(event) {
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
},
mergeObj: function(obj1, obj2) {
for (var attr in obj2) {
obj1[attr] = obj2[attr];
}
}
},
/**
* Private Methods
*/
// close popup when user hits escape button
onDocUp = function(e) {
if (settings.closeOnEscape) {
if (e.keyCode === 27) {
CoverPop.close();
}
}
},
openCallback = function() {
// if not the default setting
if (settings.onPopUpOpen !== null) {
// make sure the callback is a function
if (util.isFunction(settings.onPopUpOpen)) {
settings.onPopUpOpen.call();
} else {
throw new TypeError('CoverPop open callback must be a function.');
}
}
},
closeCallback = function() {
// if not the default setting
if (settings.onPopUpClose !== null) {
// make sure the callback is a function
if (util.isFunction(settings.onPopUpClose)) {
settings.onPopUpClose.call();
} else {
throw new TypeError('CoverPop close callback must be a function.');
}
}
};
/**
* Public methods
*/
CoverPop.open = function() {
var i, len;
if (util.hashExists(settings.delayHash)) {
util.setCookie(settings.cookieName, 1); // expire after 1 day
return;
}
util.addClass($el.html, 'CoverPop-open');
// bind close events and prevent default event
if ($el.closeClassNoDefaultEls.length > 0) {
for (i=0, len = $el.closeClassNoDefaultEls.length; i < len; i++) {
util.addListener($el.closeClassNoDefaultEls[i], 'click', function(e) {
if (e.target === this) {
util.preventDefault(e);
CoverPop.close();
}
});
}
}
// bind close events and continue with default event
if ($el.closeClassDefaultEls.length > 0) {
for (i=0, len = $el.closeClassDefaultEls.length; i < len; i++) {
util.addListener($el.closeClassDefaultEls[i], 'click', function(e) {
if (e.target === this) {
CoverPop.close();
}
});
}
}
// bind escape detection to document
util.addListener(document, 'keyup', onDocUp);
openCallback();
};
CoverPop.close = function(e) {
util.removeClass($el.html, 'CoverPop-open');
util.setCookie(settings.cookieName, settings.expires);
// unbind escape detection to document
util.removeListener(document, 'keyup', onDocUp);
closeCallback();
};
CoverPop.init = function(options) {
if (navigator.cookieEnabled) {
util.mergeObj(settings, options);
// check if there is a cookie or hash before proceeding
if (!util.hasCookie(settings.cookieName) || util.hashExists(settings.forceHash)) {
if (settings.delay === 0) {
CoverPop.open();
} else {
// delay showing the popup
setTimeout(CoverPop.open, settings.delay);
}
if (settings.hideAfter) {
// hide popup after the set amount of time
setTimeout(CoverPop.close, settings.hideAfter + settings.delay);
}
}
}
};
// alias
CoverPop.start = function(options) {
CoverPop.init(options);
};
}(window.CoverPop = window.CoverPop || {}));
Additional information
I quickly checked my site and these are the sections I found where the click event is present. Honestly I have no idea how they do interfere with the popup.
// tabs
$('ul.tabs').each(function(){
var $active, $content, $links = $(this).find('a');
$active = $links.first().addClass('active');
$content = $($active.attr('rel'));
$links.not(':first').each(function () {
$($(this).attr('rel')).hide();
});
$(this).on('click', 'a', function(e){
$active.removeClass('active');
$content.hide();
$active = $(this);
$content = $($(this).attr('rel'));
$active.addClass('active');
$content.show();
e.preventDefault();
});
});
// track box clicks and route them to parent radio button
$('div.box-hover').click( function(e)
{
$(this).find("input[type=radio]").click();
});
$('input[type=radio]').click(function(e){
if (this.checked != true && $(this).hasClass('autosubmit')){
this.checked = true;
this.form.submit();
}
e.stopPropagation();
});
// track box clicks to show/hide some desc (shipping/payment)
$('div.box-hover').click( function(e)
{
// ok. wir wollen clicks auf shipping abfangen
// und - laut tmpl - kann nur EIN passendes kind da sein
// also geht das mit dem length check!
if( $(this).children('p.shipping-name').length > 0)
{
$('div.box-hover').children('p.shipping-desc').css('display','none');
$(this).children('p.shipping-desc').css('display','block');
}
if( $(this).children('p.payment-name').length > 0)
{
$('div.box-hover').children('p.payment-desc').css('display','none');
$(this).children('p.payment-desc').css('display','block');
}
});
// autosize the comment textarea
$('#comments').autosize();
// slide in/out guest account form
$("#guest").click( function(e){
$("#cust_info_customers_password").val('');
$("#cust_info_customers_password_confirm").val('');
$('#guest-account').slideUp(250);
});
$("#account").click( function(e){
$('#guest-account').slideDown(250);
});
});
#santadani, found there is a rule to follow due to the implmentation of CoverPop itself. from your production environment, could you move the <script type="text/javascript" src="http://www.exsys.ch/templates/xt_grid/javascript/CoverPop.js"></script> to the end of document, before the </body> tag and try again?
It is because i saw in the CoverPop source, it grabs the element upon the script is loaded
$el = {
html: document.getElementsByTagName('html')[0],
cover: document.getElementById(settings.coverId),
closeClassDefaultEls: document.querySelectorAll('.' + settings.closeClassDefault),
closeClassNoDefaultEls: document.querySelectorAll('.' + settings.closeClassNoDefault)
},
which then the document.querySelectorAll('.' + settings.closeClassDefault) will retrieve nothing (becasue the script was loaded before the DOM are ready, therefore i suggest to try to move the script tag down)

JS/CSS accordion sliding transition problems

I'm trying to do JS module for slidingup/slidingdown HTML blocks.
JS module is calculating block max-height.
The problem is, that CSS transition works only for sliding down.
I can't find the right solution.
Here is jsFiddle
var Accordion = {
'vars': {
'attrItem': 'data-accordion-item',
'attrToggle': 'data-accordion-toggle',
'classOpened': '_active',
'classPrepare': '_preparing',
'timer': null
},
'accObject': {},
'accData': function(accID, key, value) {
var module = this;
if (typeof value != 'undefined') {
if (typeof module.accObject[accID] == 'undefined') {
module.accObject[accID] = {};
}
module.accObject[accID][key] = value;
} else {
return module.accObject[accID][key];
}
},
'init': function() {
var module = this;
// Loop accToggles
$('*[' + module.vars.attrToggle + ']').each(function() {
var accToggle = $(this);
var accID = accToggle.attr(module.vars.attrToggle);
// Store data of ID
module.accData(accID, 'accID', accID);
// Store data of toggle
if (!module.accData(accID, 'accToggle')) {
module.accData(accID, 'accToggle', accToggle);
} else {
module.accData(accID, 'accToggle', module.accData(accID, 'accToggle').add(accToggle));
}
// Attach click to accToggle
accToggle.click(function(e) {
e.preventDefault();
// Toggle between open/close state
module.accData(accID, 'accOpened') ? module.toggle(accID, false) : module.toggle(accID, true);
});
});
// Loop accItems
$('*[' + module.vars.attrItem + ']').each(function() {
var accItem = $(this);
var accID = accItem.attr(module.vars.attrItem);
var accOpened = accItem.hasClass(module.vars.classOpened) ? true : false;
// Store data of ID
module.accData(accID, 'accID', accID);
// Store data of item
if (!module.accData(accID, 'accItem')) {
module.accData(accID, 'accItem', accItem);
} else {
module.accData(accID, 'accItem', module.accData(accID, 'accItem').add(accItem));
}
// Store data of state
if (!module.accData(accID, 'accOpened')) module.accData(accID, 'accOpened', accOpened);
// Check to open or close accItem
accOpened ? module.toggle(accID, true) : module.toggle(accID, false);
});
},
'getSize': function(accItem) {
var module = this;
accItem.addClass(module.vars.classPrepare);
var height = accItem.outerHeight();
accItem.removeClass(module.vars.classPrepare);
return height;
},
'toggle': function(accID, open, skipAnimation) {
var module = this;
var accItems = module.accData(accID, 'accItem');
var accToggles = module.accData(accID, 'accToggle');
var accOpened = module.accData(accID, 'accOpened');
var action;
// Check for action open/close and set vars
if (open) {
module.accData(accID, 'accOpened', true);
action = 'addClass';
} else {
module.accData(accID, 'accOpened', false);
action = 'removeClass';
}
// Loop items
if (accItems) {
accItems.each(function() {
var accItem = $(this);
accItem[action](module.vars.classOpened);
// Timer fights against no animation
if (module.timer) clearTimeout(module.timer);
module.timer = setTimeout(function() {
accItem.css('max-height', open ? module.getSize(accItem) : 0);
}, 10);
});
}
// Loop toggles
if (accToggles) {
accToggles.each(function() {
var accToggle = $(this);
accToggle[action](module.vars.classOpened);
});
}
}
}
$(document).ready(function() {
Accordion.init();
});
.f-control {
background: #ddd;
}
*[data-accordion-item] {
max-height: 0;
padding-bottom: 6px;
overflow: hidden;
opacity: 0;
-webkit-transition-property: max-height, opacity;
transition-property: max-height, opacity;
-webkit-transition-duration: 300ms;
transition-duration: 300ms;
margin-left: -20px;
margin-right: -20px;
padding-left: 20px;
padding-right: 20px;
}
*[data-accordion-item]._active {
opacity: 1;
}
*[data-accordion-item]._preparing {
max-height: initial !important;
-webkit-transition: none;
transition: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div class="f-row">
<a href="#" class="side-title" data-accordion-toggle="1">
<span>Show/hide</span>
</a>
<div class="f-control" data-accordion-item="1">
<h1>Test1</h1>
<h2>Test2</h2>
</div>
</div>
JQuery has a simple enough way of generating this kind of functionality using slideToggle to show/hide elements. You can even set the speed :)
$('#showHide').click(function(){
$('div').slideToggle("fast");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<button id="showHide">hideshow</button>
<div>
this <br/>
and this
</div>

Multiple instances of jQuery plugin on same page is not working

I have searched on this site but did not get what I need.
My issue is that I have created a jquery plugin for carousels, its working fine on 1 instance, but if I created multiple instance its only working on last.
ex:
$('#one').smartCarousel(); // its not working
$('#two').smartCarousel(); // its working
Here is the plugin code:
;(function($){
// default options
var defaults = {
slide : 1,
autoPlay : false,
autoPlayTime : 3000,
speed : 400,
next : false,
prev : false,
reverse : false,
show : 4
}
// function
function sc(el, o){
this.config = $.extend({}, defaults, o);
this.el = el;
this.init();
return this;
}
// set init configurations
sc.prototype.init = function(){
$this = this;
// get children
$this.children = $this.el.children();
// wrape element, add basic css properties
$this.el.wrap('<div class="smartCarouselWrapper clearfix"></div>')
.css({
position: 'absolute',
}).parent().css({
height: $this.el.outerHeight(true), // Height is setting on line 57
width: '100%',
overflow: 'hidden',
position: 'relative'
});
// Show element by config
// Calculate width by deviding wraper width
// Set width of items
$elw = $this.el.parent().width()/$this.config.show;
$this.children.each(function(index, el) {
$(this).width($elw);
});
w = $elw*$this.config.slide; // init width
// get width, hadle diffrent width
$this.children.each(function(index, el) {
w += $(this).outerWidth(true);
});
// set lement width
$this.el.width(w);
// Set height for wrapper
$this.el.parent().height($this.el.outerHeight(true));
// check if next handle assigned
if ($this.config.next != false ) {
$(this.config.next).click(function(e) {
e.preventDefault()
$this.next();
});
};
// check if prev handle assigned
if ($this.config.prev != false ) {
$(this.config.prev).click(function(e) {
e.preventDefault()
$this.prev();
});
};
$this.ready();
} // end of inti
sc.prototype.autoPlay = function(){
// if reverse enabled
if (this.config.reverse != false) { this.prev(); } else { this.next(); };
}
// do stuffs when ready
sc.prototype.ready = function(){
if(this.config.autoPlay != false){
this.timeOut = setTimeout('$this.autoPlay()', this.config.autoPlayTime);
}
}
sc.prototype.next = function(){
$this = this;
clearTimeout($this.timeOut);
l = 0; // left
i = 0; // index
// Add width to l from each element, limiting through slide
$this.children.each(function(index, el) {
if (i < $this.config.slide) {
l -= $(this).outerWidth(true);
//Clone first item after last for smooth animation
$this.el.append($this.children.eq(i).clone());
$this.children = $this.el.children();
};
i++;
});
// animat to show next items and appent prev items to end
$this.el.stop().animate({
left: l},
$this.config.speed, function() {
i = 0; // index
$this.children.each(function(index, el) {
if (i < $this.config.slide) {
$this.children.last().remove();
$this.children = $this.el.children();
};
i++;
});
i = 0;
$this.children.each(function(index, el) {
if (i < $this.config.slide) {
$(this).appendTo($this.el);
$this.el.css('left', parseInt($this.el.css('left'))+$(this).outerWidth(true));
};
i++;
});
$this.children = $this.el.children();
$this.ready();
});
} // end of next
sc.prototype.prev = function(){
$this = this;
clearTimeout($this.timeOut);
l = 0; // left
i = 0; // index
//move last item to first through slide
$this.children.each(function(index, el) {
if (i < $this.config.slide) {
//Clone first item after last for smooth animation
$this.el.prepend($this.children.eq(($this.children.length-1)-i).clone());
l -= $this.children.eq(($this.children.length-1)-i).outerWidth(true);
$this.el.css('left', l);
console.log(1);
};
i++;
});
console.log(l);
$this.children = $this.el.children();
// animate back to 0
$this.el.stop().animate({left: 0}, $this.config.speed, function(){
i = 0;
$this.children.each(function(index, el) {
if (i <= $this.config.slide) {
$this.children.eq($this.children.length-i).remove();
};
i++;
});
$this.children = $this.el.children();
$this.ready();
});
} // end of prev
// plugin
if (typeof $.smartCarousel != 'function') {
$.fn.smartCarousel = function(o){
if (this.length > 0) {
new sc(this.first(), o);
};
return this;
}
}else{
console.log('Function already declared.');
return this;
}
}(jQuery))
Here the html:
<ul class="smart-carousel-list clearfix" id="one">
<li><!-- Image here -->
<h3>Premium Quality DATES</h3>
</li>
<li><!-- Image here -->
<h3>Variety of Export Quality RICE</h3>
</li>
<li><!-- Image here -->
<h3>Sports Goods</h3>
</li>
<li><!-- Image here -->
<h3>Surgical Items</h3>
</li>
<li><!-- Image here -->
<h3>Bad Sheets</h3>
</li>
<li><!-- Image here -->
<h3>Towals</h3>
</li>
<li><!-- Image here -->
<h3>Fruits & Vegetable</h3>
</li>
</ul>
HERE IS THE CSS:
`
.smart-carousel{
width: 100%;
position: relative;
}
.smart-carousel-list{
list-style: none;
margin: 0;
padding: 0;
}
.smart-carousel-list li {
float: left;
-webkit-box-sizing: border-box !important; /* Safari/Chrome, other WebKit */
-moz-box-sizing: border-box !important; /* Firefox, other Gecko */
box-sizing: border-box !important; /* Opera/IE 8+ */
}
.smart-carousel-nav{
position: absolute;
top: 0;
z-index: 1000;
opacity: 0;
transition: opacity 0.4s;
width: 100%;
}
.smart-carousel:hover .smart-carousel-nav{
opacity: 1;
}
.smart-carousel-nav a{
display: block;
width: 29px;
height: 28px;
text-indent: -999999px;
outline: none;
}
.smart-carousel-nav a.sc_next{
background-image: url('next.png');
margin-right: 10px;
float: right;
}
.smart-carousel-nav a.sc_prev{
background-image: url('prev.png');
margin-left: 10px;
float: left;
}
/**
* STYLE FOR TYPE : Images;
*/
.smart-carousel.type-images .smart-carousel-list li img{
max-width: 100%;
display: block;
margin: 0 auto;
}
/**
* STYLE FOR TYPE : Products;
*/
.smart-carousel.type-products .smart-carousel-list li{
border: solid 1px #efefef;
}
.smart-carousel.type-products .smart-carousel-list li img{
max-width: 100%;
display: block;
margin: 0 auto;
}
.smart-carousel.type-products .smart-carousel-list li h3{
width: 100%;
font-size: 18px;
margin: 0;
padding: 0;
}
.smart-carousel.type-products .smart-carousel-list li h3 a{
display: block;
padding: 10px;
font-weight: bold;
}
.smart-carousel.type-products .smart-carousel-list li h3 a span{
float: right;
font-weight: normal;
}
/**
* STYLE FOR TYPE : Posts;
*/
.smart-carousel.type-posts .smart-carousel-list li{
/*border: solid 1px #efefef;*/
}
.smart-carousel.type-posts .smart-carousel-list li img{
max-width: 100%;
display: block;
margin: 0 auto;
}
.smart-carousel.type-posts .smart-carousel-list li h3{
width: 100%;
font-size: 18px;
margin: 0;
padding: 0;
}
.smart-carousel.type-posts .smart-carousel-list li h3 a{
display: block;
padding: 10px;
font-weight: bold;
text-align: center;
}
.smart-carousel.type-posts .smart-carousel-list li h3 a span{
float: right;
font-weight: normal;
}
`
Your plugin is written to only connect to a single jQuery element at a time. You can improve that like this:
// plugin
if (typeof $.smartCarousel != 'function') {
$.fn.smartCarousel = function (o) {
this.each(function(){
// Connect to each jQuery element
new sc($(this), o);
});
return this;
}
} else {
console.log('Function already declared.');
return this;
}
As for the other problems, you have a single global $this shared all over the place. I added all the missing var $this where required for you and correctly reference it in the timer (via an anonymous function wrapper, so that I can reference the local $this):
JSFiddle: http://jsfiddle.net/TrueBlueAussie/b7u4635x/4/
;
(function ($) {
// default options
var defaults = {
slide: 1,
autoPlay: true,
autoPlayTime: 1000,
speed: 400,
next: false,
prev: false,
reverse: false,
show: 4
}
// function
function sc(el, o) {
this.config = $.extend({}, defaults, o);
this.el = el;
this.init();
return this;
}
// set init configurations
sc.prototype.init = function () {
var $this = this;
// get children
$this.children = $this.el.children();
// wrape element, add basic css properties
$this.el.wrap('<div class="smartCarouselWrapper clearfix"></div>')
.css({
position: 'absolute',
}).parent().css({
height: $this.el.outerHeight(true), // Height is setting on line 57
width: '100%',
overflow: 'hidden',
position: 'relative'
});
// Show element by config
// Calculate width by deviding wraper width
// Set width of items
var $elw = $this.el.parent().width() / $this.config.show;
$this.children.each(function (index, el) {
$(this).width($elw);
});
var w = $elw * $this.config.slide; // init width
// get width, hadle diffrent width
$this.children.each(function (index, el) {
w += $(this).outerWidth(true);
});
// set lement width
$this.el.width(w);
// Set height for wrapper
$this.el.parent().height($this.el.outerHeight(true));
// check if next handle assigned
if ($this.config.next != false) {
$(this.config.next).click(function (e) {
e.preventDefault()
$this.next();
});
};
// check if prev handle assigned
if ($this.config.prev != false) {
$(this.config.prev).click(function (e) {
e.preventDefault()
$this.prev();
});
};
$this.ready();
} // end of inti
sc.prototype.autoPlay = function () {
var $this = this;
// if reverse enabled
if ($this.config.reverse != false) {
$this.prev();
} else {
$this.next();
};
}
// do stuffs when ready
sc.prototype.ready = function () {
var $this = this;
if ($this.config.autoPlay != false) {
$this.timeOut = setTimeout(function(){$this.autoPlay();}, $this.config.autoPlayTime);
}
}
sc.prototype.next = function () {
var $this = this;
clearTimeout($this.timeOut);
var l = 0; // left
var i = 0; // index
// Add width to l from each element, limiting through slide
$this.children.each(function (index, el) {
if (i < $this.config.slide) {
l -= $(this).outerWidth(true);
//Clone first item after last for smooth animation
$this.el.append($this.children.eq(i).clone());
$this.children = $this.el.children();
};
i++;
});
// animat to show next items and appent prev items to end
$this.el.stop().animate({
left: l
},
$this.config.speed, function () {
i = 0; // index
$this.children.each(function (index, el) {
if (i < $this.config.slide) {
$this.children.last().remove();
$this.children = $this.el.children();
};
i++;
});
i = 0;
$this.children.each(function (index, el) {
if (i < $this.config.slide) {
$(this).appendTo($this.el);
$this.el.css('left', parseInt($this.el.css('left')) + $(this).outerWidth(true));
};
i++;
});
$this.children = $this.el.children();
$this.ready();
});
} // end of next
sc.prototype.prev = function () {
var $this = this;
clearTimeout($this.timeOut);
var l = 0; // left
var i = 0; // index
//move last item to first through slide
$this.children.each(function (index, el) {
if (i < $this.config.slide) {
//Clone first item after last for smooth animation
$this.el.prepend($this.children.eq(($this.children.length - 1) - i).clone());
l -= $this.children.eq(($this.children.length - 1) - i).outerWidth(true);
$this.el.css('left', l);
console.log(1);
};
i++;
});
console.log(l);
$this.children = $this.el.children();
// animate back to 0
$this.el.stop().animate({
left: 0
}, $this.config.speed, function () {
i = 0;
$this.children.each(function (index, el) {
if (i <= $this.config.slide) {
$this.children.eq($this.children.length - i).remove();
};
i++;
});
$this.children = $this.el.children();
$this.ready();
});
} // end of prev
// plugin
if (typeof $.smartCarousel != 'function') {
$.fn.smartCarousel = function (o) {
this.each(function () {
new sc($(this), o);
});
return this;
}
} else {
console.log('Function already declared.');
return this;
}
}(jQuery));
//$('.smart-carousel-list').smartCarousel();
$('#one').smartCarousel();
$('#two').smartCarousel();
You have a global variable in your plugin, thus making it impossible for the plugin to work with more than one element because each call to the plugin will overwrite the $this for the previous instance to target the new element.
you simply need to add var in each location where it is missing.
var $this = this;
This will of course break any other place where you attempted to use a global $this (such as the setTimeout('$this.autoplay()',200), so you will need to re-write that portion of the code to not execute autoplay() in that way.

Categories