I've created a simple javascript class to wrap slick carousel.
What is the best way to instantiate multiple instances of this class?
I've currently got the below, which search the DOM and creates a new class instance of the component, if found. I suspect my project will grow a little and this 'root' file may because a little bloated/confusing. Is there a better way to organize it?
I'm going to be adding additional classes for further functionality, so trying to find the best approach early.
main.js
import Carousel from '../app/assets/javascripts/carousel/Carousel';
var carouselElements = Array.from(document.getElementsByClassName('slick-media'));
if (carouselElements) {
carouselElements.map(function (element) {
new Carousel(element, true, {xs: 6, md: 6, lg: 6 });
});
}
Carousel.js
export default class Carousel {
constructor(element, variableWidth = false, breakpoint) {
this.element = element;
this.variableWidth = variableWidth;
this.breakpoint = breakpoint
this._init(element);
/* Bind methods */
this._init = this._init.bind(this);
}
_init() {
this._instantiateSlick(this.element);
}
_instantiateSlick(element) {
$(element).slick({
lazyLoad: 'ondemand',
slidesToScroll: 1,
adaptiveHeight: true,
useTransform: true,
/* Default over lg breakpoint */
slidesToShow: this.breakpoint.lg,
/* Allow slick to calculate widths */
variableWidth: this.variableWidth,
responsive: [
{
breakpoint: 1199,
settings: {
slidesToShow: this.breakpoint.lg,
slidesToScroll: 1,
infinite: true
}
},
{
breakpoint: 991,
settings: {
slidesToShow: this.breakpoint.md,
slidesToScroll: 1,
infinite: true
}
},
{
breakpoint: 480,
settings: {
slidesToShow: this.breakpoint.xs,
slidesToScroll: 1,
infinite: true
}
}]
});
}
}
You've clarified that your question is about how you're using your class (e.g., the code in main.js):
it's about the main.js file, and the way it's instantiating multiple instances. Is this approach the best possible?
There are a couple of nits to pick:
You're using map to create an array you fill with undefined and never use. When you're not using the returned array, don't use map; use forEach of any of several other ways to loop over arrays and array-like structures.
There's no need for the if; methods like getElementsByClassName always return a NodeList or HTMLCollection (depending on the method), even if it's empty.
Also, is it scalable in a big project, where there will be multiple classes, this file is going to become pretty unreadable?
I don't see any reason for it to become unreadable. If you want to stick to getElementsByClassName (because it can be amazingly fast; note that it doesn't exist on obsolete browsers like IE8 though):
import Carousel from '../app/assets/javascripts/carousel/Carousel';
const forEach = Function.call.bind(Array.prototype.forEach);
const hookUpClass = className => {
forEach(document.getElementsByClassName(className), element => {
new Carousel(element, true, {xs: 6, md: 6, lg: 6 });
});
};
hookUpClass('slick-media');
hookUpClass('another-class');
hookUpClass('some-further-class');
Note the use of Array.prototype.forEach to do the looping for us, since it'll work on any array-like thing. That will work on any modern browser and IE9-IE11.
If you don't mind a very very very very small bit of overhead, you can use querySelectorAll (which exists even on IE8) to get a list matching all of your classes, rather than having to handle each class separately:
import Carousel from '../app/assets/javascripts/carousel/Carousel';
document.querySelectorAll('.slick-media, .another-class, .some-further-class').forEach(element => {
new Carousel(element, true, {xs: 6, md: 6, lg: 6 });
});
I didn't use Array.prototype.forEach there because NodeList has its own forEach which does the same thing. If you need to support IE9-IE11, you'll want the polyfill, which is trivial:
if (typeof NodeList !== "undefined" &&
NodeList.prototype &&
!NodeList.prototype.forEach) {
// Surprisingly, the standard `NodeList.prototype.forEach` is enumerable (as well as
// writable and configurable) so we can just assign rather than using `defineProperty`
NodeList.prototype.forEach = Array.prototype.forEach;
}
Or of course, use Array.prototype.forEach instead as we did with getElementsByClassName if you prefer.
In cutting-edge environments like the latest Chrome and Firefox, you could do this:
import Carousel from '../app/assets/javascripts/carousel/Carousel';
for (const element of document.querySelectorAll('.slick-media, .another-class, .some-further-class')) {
new Carousel(element, true, {xs: 6, md: 6, lg: 6 });
}
...which relies on the NodeList being iterable. I didn't do that above because polyfilling an iterator on NodeList is harder than polyfilling forEach without knowing what transpiler you're using (if you're using one).
Using a class for this seems overkill to me. You are never accessing the created objects later, as you don't you keep a reference to them, so their sole purpose seems to be to perform one immediate action: apply slick to elements with some predefined configuration. After their construction they immediately become useless.
Secondly, using a default value for a parameter is not very useful when after that you still have a parameter that turns out to be mandatory. So swap the parameter positions of breakpoint and variableWidth.
As an alternative I would suggest creating a jQuery plug-in for this:
$.fn.simpleSlick = function (breakpoint, variableWidth = false) {
this.slick({
lazyLoad: 'ondemand',
slidesToScroll: 1,
adaptiveHeight: true,
useTransform: true,
/* Default over lg breakpoint */
slidesToShow: breakpoint.lg,
/* Allow slick to calculate widths */
variableWidth,
responsive: [{
breakpoint: 1199,
settings: {
slidesToShow: breakpoint.lg,
slidesToScroll: 1,
infinite: true
}
}, {
breakpoint: 991,
settings: {
slidesToShow: breakpoint.md,
slidesToScroll: 1,
infinite: true
}
}, {
breakpoint: 480,
settings: {
slidesToShow: breakpoint.xs,
slidesToScroll: 1,
infinite: true
}
}]
});
return this; // to allow chaining
};
And assuming that the slick plugin plays according to the rules (i.e. allows the jQuery object to match more than one element), your actual use of it becomes as easy as:
$('.slick-media').simpleSlick({xs: 6, md: 6, lg: 6 }, true);
When the class is a given
If the use of a class should be taken as a given, then you could still use the jQuery way for selecting the elements by their class attribute (since you already use jQuery for the slick plugin):
$('.slick-media').each(function () {
new Carousel(this, true, {xs: 6, md: 6, lg: 6 });
});
(But again, applying new without using the resulting object reveals a wrong design)
And if you want to access these Carousel objects later via the DOM element, then you could consider using the data method:
$('.slick-media').each(function () {
$(this).data('carousel', new Carousel(this, true, {xs: 6, md: 6, lg: 6 }));
});
For a given elem element, you can then access the corresponding Carousel instance as follows:
var carousel = $(elem).data('carousel'); // Get the Carousel instance
You can actually shortify it to:
Array.from(
document.getElementsByClassName('slick-media'),
node => new Carousel(node, true, {xs: 6, md: 6, lg: 6 })
);
Related
I have a javascript code to make a slider, and inside the slider, there are content changes when calling a method via livewire, and the problem is when calling it, the slider blows up, and the javascript code won't work.
view:
#foreach($section->children()->get() as $subSection)
<button wire:click="serviceChanger({{ $subSection->id }})">{{ $subSection->name }}</button>
#endforeach
#foreach($services as $service)
the services here changes depending on the serviceChanger method
#endforeach
javascript
<script>
var swiper = new Swiper('.swiper-container', {
slidesPerView: 1,
spaceBetween: 10,
// init: false,
pagination: {
el: '.swiper-pagination',
clickable: true,
},
breakpoints: {
640: {
slidesPerView: 1,
spaceBetween: 20,
},
768: {
slidesPerView: 2,
spaceBetween: 40,
},
1024: {
slidesPerView: 4,
spaceBetween: 50,
},
}
})
</script>
component:
public function serviceChanger($id)
{
// some logic
}
after I call the ServiceChanger, the javascript won't work when the services change.
Any Ideas?
I solve my problem by Dispatching a Browser Event at the end of the serviceChanger method
$this->dispatchBrowserEvent('contentChanged');
in the JavaScript, file Catch the event like that
window.addEventListener('contentChanged', event => {
// your JS code
});
You need to re-initialize your javaScript code when Livewire refreshes the content.
When livewire replaces the HTML in the DOM with your new HTML, none of the JS code executes
You have to manually make sure everything executes again
For More Info take a look at Livewire Event
I am trying to use scrollify for my website (Wordpress, woocomerce) www.chame-lemon.com and I'm totally green in programming, so I really need your help guys.
I need to disable the plugin on shop page and product pages, I'm using a class named "hwdp" to all sections on the pages when I want to use plugin. but he is activated on other pages because of the footer (it has a class to turn on scrollify also) but I can't use two separate footers in Wordpress, so I need to use code with using a function
$.scrollify.disable();
The disable method turns off the scroll snap behavior so that the page scroll like normal.
there is documentation for that plugin
https://projects.lukehaas.me/scrollify/#methods-continued
that should look like that:
if there is no class named hwdp on the page
the plugin should be disable
else
he should be enabled
and I tried to fix that by myself, I spend hours and i got no results... and i know that's a very simple thing for someone who knows jquery.
<script>
jQuery(document).ready(function($) {
$.scrollify({
section : ".hwdp",
interstitialSection: ".footer",
easing: "easeOutExpo",
scrollSpeed: 1200,
offset: 1,
scrollbars: true,
standardScrollElements: "",
setHeights: true,
overflowScroll: true,
updateHash: true,
touchScroll: false,
before:function() {},
after:function() {},
afterResize:function() {},
afterRender:function() {},
});
if (!$('section').hasClass('.hwdp')) {
$.scrollify.enable();
}else{
$.scrollify.disable();
}
});
</script>
In your code, the plugin is being initialized on every page regardless of whether it finds the .hwdp class. It's better to only be initialized when it needs to be.
Here's how you can enable the plugin only when there exists a section on the page with the class .hwdp.
<script>
jQuery(document).ready(function($) {
if($('section.hwdp').length) {
$.scrollify({
section : ".hwdp",
interstitialSection: ".footer",
easing: "easeOutExpo",
scrollSpeed: 1200,
offset: 1,
scrollbars: true,
standardScrollElements: "",
setHeights: true,
overflowScroll: true,
updateHash: true,
touchScroll: false,
before:function() {},
after:function() {},
afterResize:function() {},
afterRender:function() {},
});
}
});
</script>
I am using Angular 7.
I added ngx-slick-carousel as described here: https://www.npmjs.com/package/ngx-slick-carousel.
I adjusted slideConfig the way I want:
slideConfig = {
slidesToShow: 3,
slidesToScroll: 1,
autoplay: true,
autoplaySpeed: 1000,
speed: 1400,
infinite: true,
lazyLoad: "ondemand",
responsive: [
{
breakpoint: 991,
settings: {
slidesToShow: 2,
slidesToScroll: 2,
autoplaySpeed: 2000,
speed: 100
}
},
{
breakpoint: 575,
settings: unslick
}
]
};
I want to unslick on viewport less than 575px so I added this in slideConfig. Configuration parameters are explained here: https://kenwheeler.github.io/slick/
Now, if viewport gets back greater than 575px I want to re-activate the slick.
None of the solutions provided online worked.
Here for example, when I created the ViewChild, console prints undefined.
Reference slick instance from component (ngx-slick-carousel)
I referred also to solutions where jQuery is used to enabled slick again, but the jQuery functions are not identified in Angular.
https://github.com/kenwheeler/slick/issues/1730
The slick carousel element in html has a property #slickModal="slick-carousel".
In our component, we import the SlickCarouselComponent
import { SlickCarouselComponent } from "ngx-slick-carousel";
and refer to the slick carousel in a variable
#ViewChild("slickModal") slickModal: SlickCarouselComponent;
now we import the HostListener
import { HostListener } from "#angular/core";
and in the following function, we detect the width of the viewport. If the viewport is greater than the unslick breakpoint, we re-initialize the slick.
#HostListener("window:resize", ["$event"])
getScreenSize(event?) {
if (this.slickModal !== undefined) {
if (window.innerWidth > 575) {
if (!this.slickModal.initialized) {
this.slickModal.initSlick();
}
} else if (this.slickModal.initialized) {
this.slickModal.unslick();
}
}
}
make sure to call this function in the constructor of your component
constructor() {
this.getScreenSize();
}
I built an image slider using the carouFredSel plugin and everything works fine with one single carousel of images but as I continued building out my site I've reached some problems.
The first visible carousel works fine, but if you click the IMG link on the top right to see the second carousel, it neglects the bit of code telling it to only display one item at a time or something like that.
I've made sure everything is set up the same in both and I've tried experimenting with display and float changes with no luck
I'm also now realizing that it seems to be caused by the fading transition because the same thing happens on the first slider when I set a fade in to that as well.
Guessing it has something to do with the display change that the jQuery script does
http://coreytegeler.com/new/#work
Any ideas??
$("#web-carousel").carouFredSel({
width: "100%",
height: 450,
direction: "right",
circular: true,
infinite: true,
items: {
visible: 1,
width: "variable",
height: 400,
width: 720
},
scroll: {
duration: 1000,
pauseOnHover: true
},
auto: {
timeoutDuration: 9999,
delay: 5000
},
prev: {
button: "#web-left",
key: "left"
},
next: {
button: "#web-right",
key: "right"
},
pagination: {
container: "#web-pagination",
keys: true
},
swipe: true,
mousewheel: false
});
I'm not sure if this is what's causing you trouble, but it sounds like it might be the same issue I ran into. I have my carousel in a hidden div revealed by an animate() triggered by a click. It wasn't rendering correctly until I put the carousel initialization into a post-animate function -- ensuring that the carousel wasn't initialized until the div was ready seemed to be the key, probably because the carousel code looks at the sizing of things when it's preparing the images. So what I did was to wrap the carousel initialization in a function:
var do_carousel = function() {
$('#carousel').carouFredSel({
responsive: true,
circular: false,
etc...
Then in the code that reveals the div, it calls that function:
$("#carousel-div").animate({
opacity: 'toggle',
width: 'toggle',
}, 1200, function() {
do_carousel();
});
Once I moved the carousel initialization there, it started working. I hope perhaps that will help you find the root of your issue.
Is it possible to embed a video via javascript like vimeo & moogaloop, but for blip.tv?
Their documentation seems unclear about how to do it, they have no example and I couldn't find any by googling for about 40 minutes..
Thanks, S.
I finally came across a solution for what I wanted to do. If anybody is ever interested about it, here it is: (i'm using mootools)
function blipEmbed(episodeId) {
var swf_id = 'embeddedPlayer';
var moogaloop = new Swiff('http://blip.tv/scripts/flash/stratos.swf', {
id : swf_id,
container : 'playerHolder', /* the container's id where the swf will be embedded, i used a div */
fullScreen: true,
width : 1000,
height : 500,
vars : {
file: "http://blip.tv/file/"+episodeId+"?skin=rss",
autostart: true,
allowm4v: true,
showstrands: false,
showguidebutton: false,
showplaylist: false,
showsharebutton: false,
showmorebutton: false,
showfsbutton: false,
removebrandlink: true,
showinfo: false,
useoldendcap: false,
enablejs: true
}
});
}
/* and for the callback, blip forces you to use this function*/
function getUpdate(changeType, param1, param2){
if (changeType=="complete") {
// when the video is done playing, do something
}
}