I want to create a jquery slider using oop. This is the structure of my code :
var photoGallery = new Slider();
function Slider(){ this.init(); }
Slider.prototype = {
el : {
nav : $('.slideshowNav', this.main),
navItems : $('.items', this.nav),
main : $('.slideshow')
}
,init: function(){
// some code here
}//init
// rest of functions here
}
My problem is that i have two <div class="slideshow"> in same page and my code is wrong according to this situation.
I know that i can pass a parameter to init() and all my problems will fly away, but i use the this.el elements also in other functions so this is not a solution.
I want to know how i can add another element to this.el coming from Slider() parameter (I know, there is no parameter now)
Or, does somebody has an idea how i can make the structure which looks like
$('.slideshow').each(...)
?
I need my code working with multiple elements on a single page
Here is how many jquery plugins start:
$.fn.myplugin = function(opts) {
// Let each closure has its own options
var myplugin = new Myplugin(opts);
return this.each(myplugin);
};
function Myplugin(opts) {}
This way, people can call your code like this:
$('.slideshow').myplugin();
And each element will be handled separately. this.el will be able to be called easily in each of your instance with no issue.
You can try something like this (untested):
var galleries = [];
$('.slideshow').each(function(index, elem) {
galleries.push(new Slider(elem));
});
And then change your Slider implementation to something like this:
function Slider(elem) {
this.elem = elem;
this.init();
}
Slider.prototype = {
el : {
nav : $('.slideshowNav', this.main),
navItems : $('.items', this.nav),
main : $('.slideshow')
}
,init: function() {
// some code here
}//init
// rest of functions here
}
The new thing with this approach is, that you have a this.elem inside your Slider instance which you can refer to. You will have to adapt some of your code to refer to this.elem inside your Slider prototype to work, but this should get you started.
Related
Here is a fictional version of my jQuery plugin, but the structure is exactly the same:
(function ($)
{
var initialized = false;
var element;
var counter = 0;
$.fn.myPlugin= function(action)
{
if (action === "increase")
{
increase(arguments[1]);
}
else if (!initialized)
{
settings = $.extend({
...
}, action);
initialized = true;
element = $(this);
return this;
}
else
{
console.error("Unknown function call.");
return;
}
};
var increase = function(amount)
{
counter += amount;
element.text(counter);
};
}(jQuery));
With this code I am able to initialize my plugin like this:
$("#element").myPlugin(options);
And I can call the method increase like this:
$("#element").myPlugin("increase", 5);
However, I am not able to initialize my plugin on multiple elements on one page, because of the variables initilized, element and counter.
How do I modify this code in such a way that I can use it multiple times on one page without changing the way you can initialize and call methods?
I do this exact same thing myself, and it's very simple once you know how.
Take this example of a simple plugin...
$.fn.myPlugin = function() {
// plugin global vars go here
// do plugin stuff here
}
To modify it to work on multiple instances, you just have to parse this when you call it...
$.fn.myPlugin = function() {
$(this).each(function() {
// plugin global vars go here
// do plugin stuff here
});
}
That way it will work when you assign the plugin to either a single instance, or a collection of elements.
Then, to call methods on individual elements, you just need to specify the correct one...
$("#element1").doMethod(1, 2, 3);
$("#element2").doMethod(4, 5, 6);
Really asking this to get a better understanding of object-oriented javascript and to uncover some best practices for this scenario. Let's say I have a javascript object, such as:
SideSlider = {
rightArrow: '.controls i.right',
dot: '.controls .dot',
slide: '.slide',
init: function() {
$(this.rightArrow).click(this.nextSlide.bind(this));
$(this.leftArrow).click(this.prevSlide.bind(this));
$(this.dot).click(this.dotClick.bind(this));
},
nextSlide: function() {
var activeSlide = $('.slide.active-slide'),
firstSlide = $(this.slide).first(),
lastSlide = $(this.slide).last(),
nextUp = activeSlide.next(),
activeDot = $(".active-dot"),
nextDot = activeDot.next();
activeSlide.removeClass("active-slide");
nextUp.addClass("active-slide");
activeDot.removeClass("active-dot");
nextDot.addClass("active-dot");
$(this.leftArrow).removeClass("inactive");
if ( lastSlide.hasClass("active-slide")) {
$(this.rightArrow).addClass("inactive");
}
}
}
What is the proper way to use this object on multiple instances of DOM modules? In other words, what is the 'best-practice' way of using this object's functionality on two 'slide' instances in the same DOM
You could create a constructor for your object, and then pass a container element to that constructor, so it will be acting on that DOM-slider only. Everywhere where you perform a jQuery selector to retrieve certain element(s), you should set the scope to the given container element. You can do this by providing that container as second argument to $(..., ...).
The object instances are created with new SideSlider(container). It could look something like this:
function SideSlider(container) {
// Perform the jQuery selections with the second argument
// so that the selection returns only elements within the container:
this.$rightArrow = $('.controls i.right', container);
this.$dot = $('.controls .dot', container);
this.$slide = $('.slide', container);
this.container = container;
// ... etc
// Perform init-logic immediately
this.$rightArrow.click(this.nextSlide.bind(this));
this.$leftArrow.click(this.prevSlide.bind(this));
this.$dot.click(this.dotClick.bind(this));
// ... etc
}
// Define methods on the prototype
SideSlider.prototype.nextSlide = function() {
var activeSlide = $('.slide.active-slide', this.container),
firstSlide = $(this.slide, this.container).first(),
lastSlide = $(this.slide, this.container).last(),
nextUp = activeSlide.next(),
activeDot = $(".active-dot", this.container),
nextDot = activeDot.next();
activeSlide.removeClass("active-slide");
nextUp.addClass("active-slide");
activeDot.removeClass("active-dot");
nextDot.addClass("active-dot");
$(this.leftArrow, this.container).removeClass("inactive");
if (lastSlide.hasClass("active-slide")) {
$(this.rightArrow, this.container).addClass("inactive");
}
// ... etc
};
// Create & use the two objects:
var slider1 = new SideSlider($('#slider1'));
var slider2 = new SideSlider($('#slider2'));
// ...
slider1.nextSlide();
// ...etc.
If you have ES6 support, use the class notation.
Seeing as it looks like you are using jQuery, I would recommend looking into turning your project into a jQuery plugin. This would allow you to assign your code per use, and it's used quite commonly by developers of sliders, and other sorts of JavaScript powered widgets. The jQuery website has a great tutorial on how to accomplish this, and it can be found here:
https://learn.jquery.com/plugins/basic-plugin-creation/
I am trying to add a functionality to a web page that uses a jquery library which doesn't seem to have any documentation. (unknown origin) my problem is mainly due to the lack of understanding on jquery plugin model and/or inner workings of javascript.
1. the plugin is initiated as follows
jQuery('div.carousel').scrollGallery({
mask: 'div.mask',
slider: 'div.slideset',
slides: 'div.slide', ............ });
2. the plugin is defined in jquery as follows
;(function($){
function ScrollGallery(options) {
this.options = $.extend({
mask: 'div.mask', ...... }, options);
this.init();
3. in the Object.prototype declaration i see the following function numSlide defined.
ScrollGallery.prototype = {
....................
numSlide: function(c) {
if(this.currentStep != c) {
this.currentStep = c;
this.switchSlide();
}
},
.......... };
Question.
How do i reference numSlide(int) function externally?.
I tried the following methods and it did not work.
myx = jQuery('div.carousel').scrollGallery({ // var myx was added in the global scope
myx.numSlide(1); //error undefined is not a function
i tried adding return this; at the end of myx = jQuery('div.carousel').scrollGallery({ but it still returns the jQuery object.
i also tried
jQuery.scrollGallery().numSlide(2); //error undefined is not a function
jQuery.scrollGallery.numSlide(2); //same error
Do i need to add LIGHT BULB
// jquery plugin
$.fn.scrollGallery = function(opt){
return this.each(function(){
$(this).data('ScrollGallery', new ScrollGallery($.extend(opt,{holder:this})));
});
};
}(jQuery));
ANSWER (I think)
it looks like the ScrollGalary object is stored in a data for the selector. So i believe i can do the following jQuery('selector').data('ScrollGallery').numSlide(2);
I decided to post this anyway in-case if anyone in the future had a similar gullible situation.
One way of doing this will be to initiate ScrollGallery object first and then use it.
var test = new ScrollGallery();
test.numSlide();
if you want to extend jQuery and use the function you can assign it as follows
$.fn.scrollGallery = new ScrollGallery();
and use it
$("window").scrollGallery.numSlide();
I am currently running into the following issue, which I'd like to solve more elegantly:
My script works as follows:
Takes an element
Puts the element into a container (defined as var container = $('<div></div>') by using .append()
Keeps track of how far the container is 'filled'
If the container is full, clone a new container and continue there
Repeat this until every element is processed
Right now, this requires me to keep track of a 'fill' (and a 'max') variable to determine how far the container has been filled. So each time I do an append(), I have to increment these counters.
Now, what to me would be more elegant is making the container object smart, and enabling it to 'hook' into the append() event: whenever something is appended, the container object itself executes some code (incrementing its own counter, deciding if it is full, and if it is, returning a new, empty container).
I thought of solving it this way, by creating a function that returns a container:
var container = {
template : $('<div class="container"></div>'),
containers : [],
get : function (i) {
if (!this.containers[i]) {
this.containers[i] = this.template.clone()
.addClass('container-'+i)
.data('max', 500); //this determines the maximum (px) the container can hold
}
return this.containers[i];
}
};
This works, as I can now iterate over all the elements, and call container.get(i).append(element) for each one (while keeping count of height seperately and comparing that to container().get(i).data().max) and later in the script, when I need the output, I can return the container.containers object.
But I can't get it to work having the container.get function to 'watch' for an append() and act on it. I know this is not the way jQuery is meant to work, but I am sure there is another way of doing it, other than keeping local counters in the element iterator.
One other thing I tried is trying to set .on($.append, function() { //do stuff }); on the container, but that was a pipe dream..
I hope I have explained everything clearly, and would love to know if someone has a solution to this.
See this fiddle for a 'working' example (I highly doubt my programming skills)
Maybe you need something like this:
(function($)
{
var oldappend = $.fn.append;
var count = 0;
$.fn.newAppend = function()
{
var ret = oldappend.apply(this, arguments);
//your logic goes here
// count++;
return ret;
};
})(jQuery);
Or you need to store the count variable per container:
(function($)
{
var oldappend = $.fn.append;
$.fn.newAppend = function()
{
var ret = oldappend.apply(this, arguments);
//your logic goes here
if (!this.count){
this.count = 0;
}
this.count++;
return ret;
};
})(jQuery);
Use it:
$('<div class="container"></div>').newAppend(yourElement);
I need some help please with a javascript object. it goes like this:
I call this function addFilter(element) with a html DOM element.
The function looks like this:
function MyList() {
this.arr = new Array();
this.index = 0;
}
MyList.prototype.add = function(filter) {
this.arr[this.index++] = filter;
//alert(this.arr[0] + ' mylist arr');
}
function Filter(element) {
this.setElement(element);
}
Filter.prototype.setElement = function (element) {
this.element = element;
this.kinorid = $(element).attr('id');
}
function addFilter(element) {
filters.Add(new Filter(element));
}
var filters = new MyList();
Now with in another function that in my case the function creates the jquery UI Slider, and every time the slider changes i need to get the parent element of that element that was sent to addFilter like i said in the beginning. so then i try doing
var value = filters.arr[0];
but like i said it id undefined.
Can any one please help me by reviewing the code, and tell me whats wrong.
Thank you very much.
You still haven't said where or how you're using filters.arr[0], without which it's very difficult to help you.
Assuming your code using it looks something like this:
AddFilter($("#theElement"));
display(typeof filters.arr[0]);
filters.arr[0].element.css("color", "blue");
It should be working; live example.
My only thought is if AddFilter and filters are not defined within the same scope. You're using filters within AddFilter, so AddFilter must be defined in the same scope as filters (or in a sub-scope). So this would be fine:
var filters;
function AddFilter() { ... }
And this
function AddFilter() { ... }
var filters;
And this
var filters;
$(function() {
function AddFilter() { ... }
});
But not
function AddFilter() { ... }
$(function() {
var filters;
// ...
});
...because in that last case, AddFilter is defined outside the scope in which filters is defined.
Your code is very convoluted, I don't understand it at all. Anyway, you are looking for (I think):
var value = filters.arr[0].element;
since you assign the element reference to arr[this.index].
Incidentally, if you are passing an element, then:
$(this).attr('id');
is an awfully slow way to do:
this.id;
Edit
The code I used (where there was a div with id 'd0' in the DOM):
var filters = new MyList();
AddFilter(document.getElementById('d0'));
alert(filters.arr[0].element);