Multiple Owl V2 sliders using single javascript declaration and .each() - javascript

Been trying various things, and searched around expecting others to have done the same, but getting nowhere fast .. the best result i've got is the first instance of the carousel working, but all the rest do not. Why is the .each() function not working?
Note: the main aim is to have multiple carousels with one block of .js control .. the script pulls the individual carousel instance variables such as number of items to show, delay, if lazy-load or not, theme classes, etc from each instance based on the data-variable="xyz" values ... and it works fine with individually identified (used unique classes or ID's) carousels on the page, but not this (more efficient) multi method.
As you see i've tried to find the carousels based on the common class ".galleryowlmulti", then find their parent container ID to uniquely identify it (as i thought this is the issue?) and then apply the variables to each carousel instance. Hope that makes sense?
Example HTML Snippet of one slider repeated in the same page but with unique ID's (please note that ".owl-carousel" is not needed where I use ".owlcarousel") :
<span id="unique-id1" class="slider">
<span class="titleh3">Title</span>
<div class="galleryowlmulti" data-owlitemshow="8" data-owlmargin="10" data-owltimeout="4000" data-owllazy="true" data-owldots="false" data-owlautoplay="false" data-owlslideby="page" data-owlthemes="owltheme-smallnav">
<div class="owlcarousel">
<div class="item">1st slide</div>
<div class="item">2nd slide</div>
</div>
</div>
</span>
<span id="unique-id2" class="slider">
<span class="titleh3">Title</span>
<div class="galleryowlmulti" data-owlitemshow="8" data-owlmargin="10" data-owltimeout="4000" data-owllazy="true" data-owldots="false" data-owlautoplay="false" data-owlslideby="page" data-owlthemes="owltheme-smallnav">
<div class="owlcarousel">
<div class="item">1st slide</div>
<div class="item">2nd slide</div>
</div>
</div>
</span>
The JavaScript:
$(function () {
$('.galleryowlmulti').each(function() {
// *** declare identifier? ***
var owl_id = $(this).closest('.slider').prop('id'); // .attr('id'); // .prop('id');
var owl_declare = $('#' + owl_id + ' .galleryowlmulti'); // owl_id.find('.galleryowlmulti'); // $('#' + owl_id + ' .galleryowlmulti'),
var owl_instance = $('#' + owl_id + ' .owlcarousel'); // $(".owlcarousel", this) // owl_id.find('.owlcarousel'); // $('#' + owl_id + ' .owlcarousel');
// pull variables from page
var owl_owlthemes = owl_declare.data('owlthemes'),
owl_owlitemshow = owl_declare.data('owlitemshow'),
owl_owllazy = owl_declare.data('owllazy'),
owl_owlmargin = owl_declare.data('owlmargin'),
owl_owldots = owl_declare.data('owldots'),
owl_owlautoplay = owl_declare.data('owlautoplay'),
owl_owltimeout = owl_declare.data('owltimeout'),
owl_slidebyf = owl_declare.data('owlslideby');
// calc the items to show breaks
var owl_owlitemshow75=Math.round(owl_owlitemshow*0.75),
owl_owlitemshow50=Math.round(owl_owlitemshow*0.5),
owl_owlitemshow25=Math.round(owl_owlitemshow*0.25);
// calculate item count
var item_count = parseInt(owl_instance.find('.item').length);
var true_false = 0;
if (item_count <=1) {true_false = false; owl_owldots = false;} else {true_false = true;}
//
// control nav visiblity thumbs shown vs thumbs allowed visible
// see: http://stackoverflow.com/a/33252395/3794783
// owl_instance.on('initialized.owl.carousel resized.owl.carousel', function(e) {
// $(e.target).toggleClass('owl-nonav', e.item.count <= e.page.size);
// });
owl_instance.owlCarousel({
themeClass: owl_owlthemes,
autoplay: owl_owlautoplay,
autoplayTimeout: owl_owltimeout,
items: owl_owlitemshow,
margin: owl_owlmargin,
responsive:{
0:{items:1,nav:true},
389:{items:owl_owlitemshow25},
605:{items:owl_owlitemshow50},
1023:{items:owl_owlitemshow75},
1289:{items:owl_owlitemshow}
},
loop: true_false,
nav: true_false,
slideBy: owl_slidebyf,
lazyLoad: owl_owllazy, // IMG markup (lazyOwl = V1 / owl-lazy = v2 ): class="owl-lazy" and data-src="url_to_img" src="" or/and data-src-retina="url_to_highres_img"
dots: owl_owldots,
//
// backport the classes to older used ones
navContainerClass: 'owl-buttons',
dotsClass: 'owl-pagination',
dotClass: 'owl-page',
autoplayHoverPause:true, //false
onInitialized: function() {
if(owl_slidebyf == 'page'){
owl_instance.owlCarousel({slideBy:page})
}
}
});
});
});
Fiddle example:
See JS Fiddle
UPDATE:
I got some way along with getting mutliple instances on page to fire and work (-ish as lazyload isnt happy) ..
In the fiddle, remove or comment out the following:
responsive:{
0:{items:1,nav:true},
389:{items:owl_owlitemshow25},
605:{items:owl_owlitemshow50},
1023:{items:owl_owlitemshow75},
1289:{items:owl_owlitemshow}
},
Ooops ... and this also needs commenting out or removing THEN it works? WTH?!
onInitialized: function() {
if(owl_slidebyf == 'page'){
owl_instance.owlCarousel({slideBy:page})
}
}
UPDATE 2:
Removing the "nav:true" in responsive seemed to fix .... seems to be the culprit for others reading ... if removed the original script code should work for you :)

Simply add some arrows (div, span, whatever) near the each carousel and assign to them class .carousel-arrow-left and .carousel-arrow-right. Next add this javascript to your js file.
The Javascript
// carousel arrows
$('.carousel-arrow-left').click(function(){
$(this).next(".carousel-product-list").trigger('prev.owl.carousel');
});
$('.carousel-arrow-right').click(function(){
$(this).prev(".carousel-product-list").trigger('next.owl.carousel');
});
Let's say every carousel is called .carousel-product-list. Now every right arrow on your page after click finds next class which name is .carousel-product-list and do a command next.owl.carousel, which means the carousel go forward by a one item.

Related

Adding css after creating elements

I want to add some icons to elements I created with angularJS directly after creating them.
So I am calling the function to set the icons at the same time the elements were created.
data-ng-click="opeTab($event); getObjects($event); loadObj($event); setIcons();"
The problem is, I can get the elements with:
$scope.setIcons = function(){
var tbs = document.getElementsByClassName("tabTr");
for(let i = 0; i < tbs.length; i++){
console.log(i);
tbs[i].style.backgroundImage = "url('../ICONS\Icons_24\'" + tbs[i].id + "')";
}
}
And the list in the console is filled, but the length of the array is 0.
So what possibility do I have to "wait" for the creation except setting a timeout?
You should try to avoid creating elements yourself from your controllers. Maybe you have a good reason for doing this, but I can't see that from the example you have given.
Somewhere in your template you should have an ng-repeat which renders your tabs. Each tab should have an ng-style. Lets say:
// template.html
<div class="tabs" ng-repeat="tab in tabs">
<div
class="tab"
ng-style="getBackgroundImageStyle(tab.id)">
tab {{ tab.id }}
</div>
</div>
// controller.js
$scope.tabs = [];
$scope.getBackgroundImageStyle = tabId => `{
'background-image': 'url('../ICONS/Icons_24/${tabId}')'
}`
$scope.openTab = () => {
$scope.tabs.push(new Tab(nextTabId)); // or however you create your tabs
}
If you have a good reason for accessing the dom directly like this, then there is no problem using $timeout with a delay of 0 and wrapping your dom modification inside this. Everything should be rendered before the code inside your $timeout runs.

using OOP on .click function in javascript

I am making a webpage that has a baseball strikezone with 25 buttons that will be clickable in 25 locations. I need to know if there is a easier way to do this then what I am doing. Maybe something that will take up far less lines. The button is clicked and then the counter is added by one to another table.
$('#one').click(function(){
counter++;
$('#ones').text(counter);
});
var countertwo = 0;
$('#two').click(function(){
countertwo ++;
$('#twos').text(countertwo);
});
A bit of a guess here, but:
You can store the counter on the button itself.
If you do, and you give the buttons a common class (or some other way to group them), you can have one click handler handle all of them.
You can probably find the other element that you're updating using a structural CSS query rather than id values.
But relying on those ID values:
$(".the-common-class").click(function() {
// Get a jQuery wrapper for this element.
var $this = $(this);
// Get its counter, if it has one, or 0 if it doesn't, and add one to it
var counter = ($this.data("counter") || 0) + 1;
// Store the result
$this.data("counter", counter);
// Show that in the other element, basing the ID of what we look for
// on this element's ID plus "s"
$("#" + this.id + "s").text(counter);
});
That last bit, relating the elements by ID naming convention, is the weakest bit and could almost certainly be made much better with more information about your structure.
You can use something like this:
<button class="button" data-location="ones">One</button>
...
<button class="button" data-location="twenties">Twenty</button>
<div id="ones" class="location">0</div>
...
<div id="twenties" class="location">0</div>
$('.button').on('click', function() {
var locationId = $(this).data('location')
, $location = $('#' + locationId);
$location.text(parseInt($location.text()) + 1);
});
Also see this code on JsFiddle
More clean solution with automatic counter
/* JS */
$(function() {
var $buttons = $('.withCounter'),
counters = [];
function increaseCounter() {
var whichCounter = $buttons.index(this)+1;
counters[whichCounter] = counters[whichCounter] ? counters[whichCounter] += 1 : 1;
$("#counter"+whichCounter).text(counters[whichCounter]);
}
$buttons.click(increaseCounter);
});
<!-- HTML -->
<button class="withCounter">One</button>
<button class="withCounter">Two</button>
<button class="withCounter">Three</button>
<button class="withCounter">Four</button>
<p id="counter1">0</p>
<p id="counter2">0</p>
<p id="counter3">0</p>
<p id="counter4">0</p>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Why control buttons in jcarousel not working?

I would like use control buttons in JsCarousel slider.
In my code i make function for initialization carousel:
<script type="text/javascript">
function InitializationJsCarousel(obj){
var carousel = $(obj).jcarousel({
// Core configuration goes here
});
$(obj)
.on('jcarousel:create jcarousel:reload', function() {
var element = $(this),
width = element.innerWidth();
// This shows 1 item at a time.
// Divide `width` to the number of items you want to display,
// eg. `width = width / 3` to display 3 items at a time.
width = width / 3;
element.jcarousel('items').css('width', width + 'px');
})
.jcarousel({
// Core configuration goes here
});
$('.jcarousel-prev').jcarouselControl({
target: '-=1',
carousel: carousel
});
$('.jcarousel-next').jcarouselControl({
target: '+=1',
carousel: carousel
});
}
$(function(){
...
InitializationJsCarousel('.jcarousel');
...
});
</script>
But as i can see jcarouselControl not working. I see console,but errors not found. It problem becouse control buttons not working...
Tell me please why control buttons(jcarouselControl) not working and how make that theys work?
Code working for me.
First check that include jquery.jcarousel-control.js or jquery.jcarousel-control.min.js
Second check class name for control element (in your code it jcarousel-prev). Check name in js and in html.

Can you do a loop to change array size depending on the number of photos in a file in javascript/php?

Sorry for the question isn't very clear, basically
I have got the php code to search for photos in the directory based on the userId given in the url. So if the userId = 1, it will go to Photos/1 and get all the photos in that directory and output it into an array that I can use in Javascript. It works.
I have an external javascript to my php/html code.
I am changing the attr of the div's to display the photos. I have 5 "photo containers" in the array called photodisplay:
var photodisplay =
[
$("#photo1"),
$("#photo2"),
$("#photo3"),
$("#photo4"),
$("#photo5"),
];
Then I have a loop to change the attribute/img src:
function preloadingPhotos() {
for (var x=0; x<galleryarray.length; x++)
{
photodisplay[x].attr("src", "Photos/" + userid + "/" + galleryarray[x]);
console.log("preloaded photos");
}
displayPhoto();
}
It works. Providing no more than 5 photos because that is how many photocontainers I have. But what if I had photos? The question is: Would I be able to do a loop to keep changing the photos in the photodisplay array?
I also have code for the photocontainers to fade in and out:
function displayPhoto(){
photodisplay[0].fadeIn(3000);
photodisplay[0].delay(3000).fadeOut(3000, function() { //first callback func
photodisplay[1].fadeIn(3000);
photodisplay[1].delay(3000).fadeOut(3000, function() { //second callback func
photodisplay[2].fadeIn(3000);
photodisplay[2].delay(3000).fadeOut(3000, function() { //third callback func
photodisplay[3].fadeIn(3000);
photodisplay[3].delay(3000).fadeOut(3000, function() { // fourth callback func
photodisplay[4].fadeIn(3000);
photodisplay[4].delay(3000).fadeOut(3000, function() {
setTimeout(displayPhoto(), 3000);
});
});
});
});
});
}// end of function displayPhoto
Which requires me to manually enter the number of the array of the photodisplay.
I would thinking of adding more to the array with duplications of the photocontainers. But I don't think that would work since I would have to manually enter the number of the array in the code above to make it fade in and out.
Sorry if this is confusing. I tried my best to explain my problem. I hope someone can help. Don't worry about the retrieving images in the directory part, because it works. It increases the array of photos accordingly, I just don't know how to adjust this change with my javascript.
The method you are using, does not scale as you have a callback function for every element in your slideshow.
What you should do, is put all images in a list (or a list of div's) and hide them all / change the z-index so that only the active one shows. The you can loop through your elements using .next() on the list items to get the next one (or the first one if .next().length is 0).
This will clean up your code and is pretty easy to do yourself but there are also loads of jQuery plugins that do it for you.
You need a little bit of abstraction here. So instead of manually code numbers, try another approach. For this example I've used jQuery; since you've tagged your question with it, I assume it's okay:
// Set a default value and store the current photo in it.
var currentPhoto = 0;
// Calculate the total number of photos
var photoTotal = photodisplay.length;
var photoTimeout = false;
// Create a function to go to the next photo
var nextPhoto = function () {
// Keep track of the new current
currentPhoto = (currentPhoto + 1) % photoTotal;
// Just to be sure clearTimeout
clearTimeout(photoTimeout);
// Fadein each photo; you might want to do something to reset the style
photodisplay[0].fadeIn({
duration: 3000,
complete: function () {
photoTimeout = setTimeout(nextPhoto, 3000);
}
});
}
nextPhoto();
You don't want to define JS from the backend like that; just have PHP render the markup, then use JS to query and parse the markup for the presentational layer.
Let's assume your markup looks like this:
<div id="photocontainers">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<!-- A hidden array of images. -->
<div id="images">
<img src="http://placehold.it/100x100&text=1" />
<img src="http://placehold.it/100x100&text=2" />
<img src="http://placehold.it/100x100&text=3" />
<img src="http://placehold.it/100x100&text=4" />
<img src="http://placehold.it/100x100&text=5" />
<img src="http://placehold.it/100x100&text=6" />
<img src="http://placehold.it/100x100&text=7" />
<img src="http://placehold.it/100x100&text=8" />
<img src="http://placehold.it/100x100&text=9" />
<img src="http://placehold.it/100x100&text=10" />
<img src="http://placehold.it/100x100&text=11" />
</div>
So your server renders this; #images is just a hidden container, that basically preloads all the image assets you'll cycle between in #photocontainers.
var cycleImages = function() {
// Cache selectors that we'll need.
var $photoContainers = $('#photocontainers').children(),
$images = $('#images img'),
// Use `.data()` to get the starting point, or set to 0
// (if this is the first time the function ran).
startImage = $images.data('nextImage') || 0;
// Loop from the starting point, filling up the number of
// photocontainers in the DOM.
for (var i = startImage; i < startImage + $photoContainers.length; i++) {
var $targetImage = $images.eq(i % $images.length),
$targetPhotoContainer = $photoContainers.eq(i - startImage);
// Get rid of the current contents.
$targetPhotoContainer.empty();
// Clone the desired image, and append it into place.
$targetImage.clone().appendTo($targetPhotoContainer).fadeOut(0).fadeIn();
}
// Let's figure out which starting image is next up, and store that
// with `.data()`.
var nextImage = startImage + $photoContainers.length;
if (nextImage >= $images.length) {
nextImage -= $images.length;
}
$images.data('nextImage', nextImage);
}
// When the DOM is ready, call the method, then
// call it again however often you'd like.
$(document).ready(function() {
cycleImages();
setInterval(function() {
cycleImages();
}, 3000);
});
Here's a plunkr showing that in action: http://plnkr.co/SumqkXYpRXcOqEhAPOHm

How to Reduce Size of This jQuery Script and Make it More Flexible?

I just created script that shows/hides (toggles) block of HTML. There are four buttons that each can toggle its HTML block. When any HTML block is opened, but user has been clicked on other button than that HTML block's associated button... it hides that HTML block and shows new one.
Here is what I have at the moment:
$('.btn_add_event').click( function() {
$('.block_link, .block_photos, .block_videos').hide();
$('.block_event').toggle();
});
$('.btn_add_link').click( function() {
$('.block_event, .block_photos, .block_videos').hide();
$('.block_link').toggle();
});
$('.btn_add_photos').click( function() {
$('.block_event, .block_link, .block_videos').hide();
$('.block_photos').toggle();
});
$('.btn_add_videos').click( function() {
$('.block_event, .block_link, .block_photos').hide();
$('.block_videos').toggle();
});
Any ideas how to reduce code size? Also, this script isn't very flexible. Imagine to add two new buttons and blocks.
like Sam said, I would use a class that all the blocks share, so you never have to alter that code. Secondly, you can try 'traversing' to the closest block, therefore avoiding it's name. That approach is better than hard coding each specific block, but if the html dom tree changes you will need to refactor. Last, but best, you can pass in the class name desired block as a variable to the function. Below is something you can copy paste that is close to what you started with.
$('.myAddButtonClass').click( function() {
$('.mySharedBlockClass').filter(':visible').hide();
//find a good way to 'traverse' to your desired block, or name it specifically for now.
//$(this).closest(".mySharedBlockClass").show() complete guess
$('.specificBlockClass').show();
});
I kept reading this "When any HTML block is opened, but user has been clicked on other button than that HTML block's associated button" thinking that my eyes were failing me when Its just bad English.
If you want to make it more dynamic, what you can do is add a common class keyword. Then
when the click event is raise. You can have it loop though all the classes that have the
keyword and have it hide them all (except the current one that was clicked) and then show the current one by using the 'this' keyword.
you can refer below link,
http://chandreshmaheshwari.wordpress.com/2011/05/24/show-hide-div-content-using-jquery/
call function showSlidingDiv() onclick event and pass your button class dynamically.
This may be useful.
Thanks.
try this
$('input[type=button]').click( function() {
$('div[class^=block]').hide(); // I resumed html block is div
$(this).toggle();
});
Unfortunatly I couldn't test it, but if I can remember right following should work:
function toogleFunc(clickObject, toogleTarget, hideTarget)
{
$(clickObject).click(function()
{
$(hideTarget).hide();
$(toogleTarget).toggle();
});
}
And the call:
toogleFunc(
".btn_add_videos",
".block_videos",
".block_event, .block_link, .block_photos"
);
and so far
Assuming the buttons will only have one class each, something like this ought to work.
var classNames = [ 'btn_add_event', 'block_link', 'block_photos', 'block_videos' ];
var all = '.' + classNames.join(', .'); // generate a jquery format string for selection
$(all).click( function() {
var j = classNames.length;
while(j--){
if( this.className === classNames[j] ){
var others = classNames.splice(j, 1); // should leave all classes but the one on this button
$('.' + others.join(', .')).hide();
$('.' + classNames[j]).toggle();
}
}
}
All the buttons have the same handler. When the handler fires, it checks the sender for one of the classes in the list. If a class is found, it generates a jquery selection string from the remaining classes and hides them, and toggles the one found. You may have to do some checking to make sure the strings are generating correctly.
It depends by how your HTML is structured.
Supposing you've something like this
<div class="area">
<div class="one"></div>
<div class="two"></div>
<div class="three"></div>
</div>
...
<div class="sender">
<a class="one"></a>
<a class="two"></a>
<a class="three"></a>
</div>
You have a class shared by the sender and the target.
Your js would be like this:
$('.sender > a').click(function() {
var target = $(this).attr('class');
$('.area > .' + target).show().siblings().hide();
});
You show your real target and hide its siblings, which aren't needed.
If you put the class postfixes in an array, you can easily make this code more dynamic. This code assumed that it doesn't matter in which order toggle or hide are called. If it does matter, you can just remember the right classname inside the (inner) loop, and toggle that class after the loop.
The advantage to this approach is that you can extend the array with an exta class without needing to modifying the rest of the code.
var classes = new Array('videos', 'event', 'link', 'photos');
for (var i = 0; i < classes.length; ++i)
{
$('.btn_add_' + classes[i]).click(
function()
{
for (var j = 0; j < classes.length; ++j)
{
if (this.hasClass('btn_add_' + classes[j]))
{
$('.block_' + classes[j]).toggle();
}
else
{
$('.block_' + classes[j]).hide();
}
}
});
}
You could make this code more elegant by not assigning those elements classes like btn_add_event, but give them two classes: btn_add and event, or even resort to giving them id's. My solution is based on your description of your current html.
Here is what I think is a nice flexible and performant function. It assumes you can contain your links and html blocks in a parent, but otherwise it uses closures to precalculate the elements involved, so a click is super-fast.
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js" ></script>
<script type="text/javascript">
// Enables show/hide functionality on click.
// The elements within 'container' matching the selector 'blocks' are hidden
// When elements within 'container' matching the selector 'clicker' are clicked
// their attribute with the name 'clickerAttr' is appended to the selector
// 'subject' to identify a target, usually one of the 'blocks'. All blocks
// except the target are hidden. The target is shown.
//
// Change clickerAttr from 'linkTarget' to 'id' if you want XHTML compliance
//
// container: grouping of related elements for which to enable this functionality
// clicker: selector to element type that when clicked triggers the show/hide functionality
// clickerAttr: name of the DOM attribute that will be used to adapt the 'subject' selector
// blocks: selector to the html blocks that will be shown or hidden when the clicker is clicked
// subject: root of the selector to be used to identify the one html block to be shown
//
function initToggle(container,clicker,clickerAttr,blocks,subject) {
$(container).each(
function(idx,instance) {
var containerElement = $(instance);
var containedBlocks = containerElement.find(blocks);
containerElement.find(clicker).each(function(idxC, instanceClicker) {
var tgtE = containerElement.find(subject+instanceClicker.getAttribute(clickerAttr));
var clickerBlocks = containedBlocks.not(tgtE);
$(instanceClicker).click(function(event) {
clickerBlocks.hide();
tgtE.toggle();
});
});
// initially cleared
containedBlocks.hide();
}
);
}
$(function() {
initToggle('.toggle','a.link','linkTarget','div.block','div.');
});
</script>
</head>
<body>
Example HTML block toggle:
<div class="toggle">
a <br />
b <br />
c <br />
<div class="A block"> A </div>
<div class="B block"> B </div>
<div class="C block"> C </div>
</div> <!-- toggle -->
This next one is not enabled, to show scoping.
<div class="toggle2">
a <br />
<div class="A block">A</div>
</div> <!-- toggle2 -->
This next one is enabled, to show use in multiple positions on a page, such as in a portlet library.
<div class="toggle">
a <br />
<div class="A block">A</div>
</div> <!-- toggle (2) -->
</body>
</html>

Categories