Remove class on previous slide - javascript

I'm working on carousel using ant design and reactjs.
Im trying to make this slider 3d smooth carousel and I successfully able to do it.
my problem is on the previous image which is the left side. I cant remove the class that I added every time the carousel change, the class should be always in the last previous slide not on all previous slides.
Hope you understand me.
thanks.
SAMPLE CODE
constructor(props) {
super(props);
this.state = {
prev: 0
};
this.onChange = this.onChange.bind(this);
}
onChange(a, b, c) {
this.setState({
prev: b
});
}
// onLoad
componentDidUpdate() {
var list = document.getElementsByClassName("slick-slide");
list[this.state.prev].classList.add("prev");
}
// onChange
componentWillUpdate() {
var list = document.getElementsByClassName("slick-slide");
list[this.state.prev].classList.add("prev");
}

You can remove all ocurrences of the class before asigning it to the new element with this function:
function removeClassFromPrevious() {
var elements = document.getElementsByClassName("prev");
var array = [].slice.call(elements);
array.forEach(function(item, index){
item.classList.remove("prev");
});
}
So you can call it like this:
// onLoad
componentDidUpdate() {
removeClassFromPrevious();
var list = document.getElementsByClassName("slick-slide");
list[this.state.prev].classList.add("prev");
}
// onChange
componentWillUpdate() {
removeClassFromPrevious();
var list = document.getElementsByClassName("slick-slide");
list[this.state.prev].classList.add("prev");
}
Or in a more reusable way:
function removeAllClassOcurrences(className) {
var elements = document.getElementsByClassName(className);
var array = [].slice.call(elements);
array.forEach(function(item, index){
item.classList.remove(className);
});
}
...
removeAllClassOcurrences("prev");

Related

this.router.routeReuseStrategy.shouldReuseRoute = () => false;

this.router.routeReuseStrategy.shouldReuseRoute = () => false;
I have applied this sort of line in order to make the component UI updated everytime. But in some other cases it start to refreshing the page event if it should reuse the route.
How can we overcome this issue?
Actually in my application there are three tabs in left panel. In each tab there are some listings clicking on list items opens the content on right panel. But in one of the listing there is a common UI that is getting open on some list item, but the problem is that when we don't apply above sort of code then the UI is not getting updated. But if we apply the code then the UI is updated everytime we click on other list item. But the problem is that when we apply this code it start to refresh the page everytime we click on other list in different tabs also, that should not be the case.
If we apply this code this.router.routeReuseStrategy.shouldReuseRoute = () => false; then how can we revert this functionality under this.router?
To take less risks I'm just reverting it back to what it was once the reload is done:
refresh() {
const prev = this.router.routeReuseStrategy.shouldReuseRoute;
const prevOSN = this.router.onSameUrlNavigation;
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
this.router.onSameUrlNavigation = 'reload';
this.router.navigate([this.router.url]);
setTimeout(() => {
this.router.routeReuseStrategy.shouldReuseRoute = prev;
this.router.onSameUrlNavigation = prevOSN;
}, 0);
}
I have the same issue, I changed that line for this:
// override the route reuse strategy
this.router.routeReuseStrategy.shouldReuseRoute = function () {
return false;
};
this.router.events.subscribe((evt) => {
if (evt instanceof NavigationEnd) {
// trick the Router into believing it's last link wasn't previously loaded
this.router.navigated = false;
// if you need to scroll back to top, here is the right place
window.scrollTo(0, 0);
}
});
I don't even know if this works well or do the same thing.
private saveRouterStrategyReuseLogic: any;
ngOnInit() {
// Save logic
this.saveRouterStrategyReuseLogic = this.router.routeReuseStrategy.shouldReuseRoute;
this.router.routeReuseStrategy.shouldReuseRoute = (future, curr) => { return false; };
}
ngOnDestroy() {
this.router.routeReuseStrategy.shouldReuseRoute =
this.saveRouterStrategyReuseLogic;
}

Custom event on fragment change in Reveal.js

What would be the right way to programmatically add fragments to a slide in Reveal.js? I have a JavaScript widget on a slide that can go through 5 states, and I would like to go through them with fragment transitions.
I tried to achieve something similar with dummy fragments, like in the representative example below. This is intended to change the src of an image on fragment change. The example has an issue, though. When approaching a slide by pressing previous a number of times, the slide should start at its last fragment state. In the example, however, the image src starts in state 1, and doesn't know how to go further back on additional previous-steps.
Any pointers would be appreciated!
<img src="img1.png" id="my-image">
<span class="fragment update-img-src" data-target="my-image" data-src="img2.svg"></span>
<script>
Reveal.addEventListener('fragmentshown', function(event) {
if (event.fragment.classList.contains('update-img-src')) {
// Find the target image by ID
var target = document.getElementById(event.fragment.dataset.target);
// Keep a stack of previously shown images, so we can always revert back on 'fragmenthidden'
if (target.dataset.stack == null) {
target.dataset.stack = JSON.stringify([target.getAttribute('src')]);
}
target.dataset.stack = JSON.stringify([event.fragment.dataset.src, ...JSON.parse(target.dataset.stack)]);
// Update the image
target.setAttribute('src', event.fragment.dataset.src);
}
});
Reveal.addEventListener('fragmenthidden', function(event) {
if (event.fragment.classList.contains('update-img-src')) {
// Return to the previously shown image.
// Remove the top from the history stack
var target = document.getElementById(event.fragment.dataset.target);
if (target.dataset.stack == null) {
console.log('Trying to hide', event.fragment.dataset.src, 'but there is no stack.');
} else {
var [_, ...tail] = JSON.parse(target.dataset.stack);
target.dataset.stack = JSON.stringify(tail);
// Set the image source to the previous value
target.setAttribute('src', tail[0]);
}
}
});
</script>
Here's a hacky solution that I put together. It allows you to register any number of fragments on a slide with a callback function.
function registerFakeFragments(slide, fragmentIndices, stateChangeHandler) {
const identifier = `fake-${Math.round(1000000000*Math.random())}`;
let i = 1;
for (let fragmentIndex of fragmentIndices) {
const span = document.createElement('span');
span.dataset.target = identifier;
span.classList.add('fragment');
span.classList.add('fake-fragment');
span.setAttribute('data-fragment-index', JSON.stringify(fragmentIndex));
span.dataset.stateIndex = JSON.stringify(i);
slide.appendChild(span);
++i;
}
let currentState = null; // last reported state
const listener = () => {
const currentSlide = Reveal.getCurrentSlide();
if (currentSlide && currentSlide === slide) {
// Find the latest visible state
let state = 0;
currentSlide.querySelectorAll(`.fake-fragment.visible[data-target=${identifier}]`).forEach(f => {
const index = JSON.parse(f.dataset.stateIndex);
if (index > state) {
state = index;
}
});
// If the state changed, call the handler.
if (state != currentState) {
stateChangeHandler(state);
currentState = state;
}
}
};
Reveal.addEventListener('fragmentshown', listener);
Reveal.addEventListener('fragmenthidden', listener);
Reveal.addEventListener('slidechanged', listener);
}

click event listener fires immediately

I have a class that handle the Education section of my web page:
class Education{
constructor(){
this.courses = [
{id:'css-course',state:'70'},
{id:'vue-course',state:'85'},
{id:'text-mining-course',state:'50'}
];
this.eduButton = document.getElementById('edu-button');
this.init()
}
init(){
this._initEducation()
}
_initEducation(){
let self = this;
this.eduButton.addEventListener('click',function(){
self._initTimeline().play();
})
this.eduButton.previousElementSibling.addEventListener('click',function(){
self.courses.forEach(function(course){
document.getElementById(course.id).style.width = '0%'
})
})
}
_initTimeline(){
const courseTimeline = new TimelineLite();
this.courses.forEach(function(course){
let curr = document.getElementById(course.id)
courseTimeline.to(curr,.3,{width:`${course.state}%`},'width')
})
return courseTimeline
}
}
module.exports = {
Education
}
the _initEducation method is quite similar to another method that i have in the About section, both of them serve to animate a list of progress bar with gsap:
_initAbout:
_initAbout(){
let self = this;
this.aboutButton.addEventListener('click',function(){
self._initTimeline().play();
})
this.aboutButton.previousElementSibling.addEventListener('click',function(){
self.skills.forEach(function(skill){
document.getElementById(skill.id).style.width = '0%'
})
})
}
Because this two methods are similar, i wrote an utility class named InitProgressBarAnimation:
class InitProgressBarAnimation{
init(){
return{
start:this._start,
reset:this._reset
}
}
_start(button,timeline){
button.addEventListener('click',function(){
console.log(timeline)
timeline.play()
})
}
_reset(button,data){
button.previousElementSibling.addEventListener('click',function(){
console.log('reset')
data.forEach(function(d){
document.getElementById(d.id).style.width = '0%'
})
})
}
}
and replace, for testing the initEducation method with this:
class Education{
constructor(ProgressBarAnimationHandler){
...
this.ProgressBarAnimationHandler = ProgressBarAnimationHandler;
this.init()
}
_initEducation(){
this.ProgressBarAnimationHandler.start(this.eduButton,this._initTimeline());
this.ProgressBarAnimationHandler.reset(this.eduButton,this.courses);
/*
let self = this;
this.eduButton.addEventListener('click',function(){
self._initTimeline().play();
})
this.eduButton.previousElementSibling.addEventListener('click',function(){
self.courses.forEach(function(course){
document.getElementById(course.id).style.width = '0%'
})
})
*/
}
Now seems that when i load the page, the timeline start doing his job (i can see the style witdh attribute fill itself with the value), and if i close the section the reset function works but then the start function doesn't start again. How that possible? it is a problem with gsap?
the start method only attach an handler to the button...
Remove the "()" from _initTimeline. The () tell the function to execute. If you leave them off you are just passing the function to the event handler to execute. This is the standard pattern for what you are trying to do. Finally it would look like this:
this.ProgressBarAnimationHandler.start(this.eduButton,this._initTimeline);

Concatenate function

The idea behind this to animate section with mousewheel - keyboard and swipe on enter and on exit. Each section has different animation.
Everything is wrapp inside a global variable. Here is a bigger sample
var siteGlobal = (function(){
init();
var init = function(){
bindEvents();
}
// then i got my function to bind events
var bindEvents = function(){
$(document).on('mousewheel', mouseNav());
$(document).on('keyup', mouseNav());
}
// then i got my function here for capture the event
var mouseNav = function(){
// the code here for capturing direction or keyboard
// and then check next section
}
var nextSection = function(){
// Here we check if there is prev() or next() section
// if there is do the change on the section
}
var switchSection = function(nextsection){
// Get the current section and remove active class
// get the next section - add active class
// get the name of the function with data-name attribute
// trow the animation
var funcEnter = window['section'+ Name + 'Enter'];
}
// Let's pretend section is call Intro
var sectionIntroEnter = function(){
// animation code here
}
var sectionIntroExit = function(){
// animation code here
}
}();
So far so good until calling funcEnter() and nothing happen
I still stuck to call those function...and sorry guys i'm really not a javascript programmer , i'm on learning process and this way it make it easy for me to read so i would love continue using this way of "coding"...Do someone has a clue ? Thanks
Your concatenation is right but it'd be better if you didn't create global functions to do this. Instead, place them inside of your own object and access the functions through there.
var sectionFuncs = {
A: {
enter: function() {
console.log('Entering A');
},
exit: function() {
console.log('Exiting A');
}
},
B: {
enter: function() {
console.log('Entering B');
},
exit: function() {
console.log('Exiting B');
}
}
};
function onClick() {
var section = this.getAttribute('data-section');
var functions = sectionFuncs[section];
functions.enter();
console.log('In between...');
functions.exit();
}
var buttons = document.querySelectorAll('button');
for (var i = 0; i < buttons.length; i++) {
buttons[i].addEventListener('click', onClick);
}
<button data-section="A">A</button>
<button data-section="B">B</button>
You could have an object that holds these functions, keyed by the name:
var enterExitFns = {
intro: {
enter: function () {
// animation code for intro enter
},
exit: function () {
// animation code for intro exit
}
},
details: {
enter: function () {
// animation code for details enter
},
exit: function () {
// animation code for details exit
}
}
};
var name = activeSection.attr('data-name');
enterExitFns[name].enter();

Is it possible to reinitialize a CKEditor Combobox/Drop Down Menu?

How do I dynamically update the items in a drop down?
I have a custom plugin for CKEditor that populates a drop down menu with a list of items which I can inject into my textarea.
This list of items comes from a Javascript array called maptags, which is updated dynamically for each page.
var maptags = []
This list of tags gets added to the drop down when you first click on it by the init: function. My problem is what if the items in that array change as the client changes things on the page, how can I reload that list to the updated array?
Here is my CKEditor Plugin code:
CKEDITOR.plugins.add('mapitems', {
requires: ['richcombo'], //, 'styles' ],
init: function (editor) {
var config = editor.config,
lang = editor.lang.format;
editor.ui.addRichCombo('mapitems',
{
label: "Map Items",
title: "Map Items",
voiceLabel: "Map Items",
className: 'cke_format',
multiSelect: false,
panel:
{
css: [config.contentsCss, CKEDITOR.getUrl(editor.skinPath + 'editor.css')],
voiceLabel: lang.panelVoiceLabel
},
init: function () {
this.startGroup("Map Items");
//this.add('value', 'drop_text', 'drop_label');
for (var this_tag in maptags) {
this.add(maptags[this_tag][0], maptags[this_tag][1], maptags[this_tag][2]);
}
},
onClick: function (value) {
editor.focus();
editor.fire('saveSnapshot');
editor.insertHtml(value);
editor.fire('saveSnapshot');
}
});
}
});
I think I just solved this actually.
Change your init like this:
init: function () {
var rebuildList = CKEDITOR.tools.bind(buildList, this);
rebuildList();
$(editor).bind('rebuildList', rebuildList);
},
And define the buildList function outside that scope.
var buildListHasRunOnce = 0;
var buildList = function () {
if (buildListHasRunOnce) {
// Remove the old unordered list from the dom.
// This is just to cleanup the old list within the iframe
$(this._.panel._.iframe.$).contents().find("ul").remove();
// reset list
this._.items = {};
this._.list._.items = {};
}
for (var i in yourListOfItems) {
var item = yourListOfItems[i];
// do your add calls
this.add(item.id, 'something here as html', item.text);
}
if (buildListHasRunOnce) {
// Force CKEditor to commit the html it generates through this.add
this._.committed = 0; // We have to set to false in order to trigger a complete commit()
this.commit();
}
buildListHasRunOnce = 1;
};
The clever thing about the CKEDITOR.tools.bind function is that we supply "this" when we bind it, so whenever the rebuildList is triggered, this refer to the richcombo object itself which I was not able to get any other way.
Hope this helps, it works fine for me!
ElChe
I could not find any helpful documenatation around richcombo, i took a look to the source code and got an idea of the events i needed.
#El Che solution helped me to get through this issue but i had another approach to the problem because i had a more complex combobox structure (search,groups)
var _this = this;
populateCombo.call(_this, data);
function populateCombo(data) {
/* I have a search workaround added here */
this.startGroup('Default'); /* create default group */
/* add items with your logic */
for (var i = 0; i < data.length; i++) {
var dataitem = data[i];
this.add(dataitem.name, dataitem.description, dataitem.name);
}
/* other groups .... */
}
var buildListHasRunOnce = 0;
/* triggered when combo is shown */
editor.on("panelShow", function(){
if (buildListHasRunOnce) {
// reset list
populateCombo.call(_this, data);
}
buildListHasRunOnce = 1;
});
/* triggered when combo is hidden */
editor.on("panelHide", function(){
$(_this._.list.element.$).empty();
_this._.items = {};
_this._.list._.items = {};
});
NOTE
All above code is inside addRichCombo init callback
I remove combobox content on "panelHide" event
I repopulate combobox on "panelShow" event
Hope this helps

Categories

Resources