Custom event on fragment change in Reveal.js - javascript

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);
}

Related

check if class exists on that page javascript

I have an index page and a dashboard, on the index I'm using typewriter and particlesjs on the index only, and on the dashboard I have a sidebar.
If I have all the code as-is, I get errors as the page is still looking for typewriter and particlesjs on all pages.
So I have attempted to wrap each section around an if so the plan is if that class or id exists on that page it will only render that JS. So I've created the following code.
edited code below based on groovy_guy's answer
document.querySelector('nav .toggle').addEventListener('click', e => {
document.querySelector('nav .hidden').classList.toggle('show')
});
let checkTypewriter = document.getElementById('typewriter');
if (checkTypewriter.length > 0) {
new Typewriter('#typewriter', {
strings: ['Website Developer', 'Freelancer' , 'System Admin'],
autoStart: true,
loop: true
});
}
let checkParticlesjs = document.getElementsByClassName('particlesjs');
if (checkParticlesjs.length > 0) {
let particles = Particles.init({
selector: '.particlesjs',
color: ['#48F2E3', '#48F2E3', '#48F2E3'],
connectParticles: true,
maxParticles: 200
});
}
let checkSidebar = document.getElementsByClassName('sidebar');
if (checkSidebar.length > 0) {
user_wants_collapse = false;
// Fetch all the details element.
const details = document.querySelectorAll("details");
// Add the onclick listeners.
details.forEach((targetDetail) => {
targetDetail.addEventListener("click", () => {
// Close all the details that are not targetDetail.
details.forEach((detail) => {
if (detail !== targetDetail) {
detail.removeAttribute("open");
};
});
});
});
document.querySelector('section').addEventListener('click', (ev) => {
// close any open details elements that this click is outside of
let target = ev.target;
let detailsClickedWithin = null;
while (target && target.tagName != 'DETAILS') {
target = target.parentNode;
};
if (target && target.tagName == 'DETAILS') {
detailsClickedWithin = target;
};
Array.from(document.getElementsByTagName('details')).filter(
(details) => details.open && details != detailsClickedWithin
).forEach(details => details.open = false);
// if the sidebar collapse is true and is re-expanded by clicking a menu item then clicking on the body should re-close it
if (user_wants_collapse == true && (document.querySelectorAll('.sidebar details'))) {
document.querySelector('body').classList.add('is--expand');
};
});
// when the sidebar menu is clicked this sets the user_wants_collapse var to true or false and toggles is--expand class on body
document.querySelector('.sidebar .menu-link').addEventListener('click', () => {
document.querySelector('body').classList.toggle('is--expand');
user_wants_collapse = !user_wants_collapse
document.querySelector('.sidebar').classList.toggle('is--expand');
// show all text
document.querySelectorAll('.sidebar .title').forEach((el) => {
el.classList.toggle('hidden');
});
// changing sidebar menu items and menu collapse icons
const icon = document.querySelector('.menu-link-arrows span');
if (icon.classList.contains('fa-angle-double-left')) {
icon.classList.remove('fa-angle-double-left');
icon.classList.add('fa-angle-double-right');
} else {
icon.classList.remove('fa-angle-double-right');
icon.classList.add('fa-angle-double-left');
}
});
// making sure the sidebar menu items re-expands the sidebar on click
let x = document.querySelectorAll('.sidebar details');
let i;
for (i = 1; i < x.length; i++) {
x[i].addEventListener('click', () => {
// changing sidebar menu items and menu collapse icons
// change menu items and menu collapse icons
const icon = document.querySelector('.sidebar-drop-parent-arrow span');
if (icon.classList.contains('fa-chevron-down')) {
icon.classList.remove('fa-chevron-down');
icon.classList.add('fa-chevron-up');
} else {
icon.classList.remove('fa-chevron-up');
icon.classList.add('fa-chevron-down');
}
if (document.querySelector('body').classList.contains('is--expand')) {
document.querySelector('body').classList.remove('is--expand');
};
});
};
};
when loading the JS I'm not getting any console errors but I'm not seeing any result of the JS.
Why don't you use querySelector()? I think that's more uniform across your codebase. Besides, I see that you only care about one element and not a list of elements, so this method is ideal since it gets the first element that encounters.
const checkTypewriter = document.querySelector('#typewriter')
if (checkTypewriter) {
// Element with an ID 'typewriter' exist in the DOM.
}
const checkParticlesjs = document.querySelector('.particlesjs')
if (checkParticlesjs) {
// Element with a class named "particlesjs" exist in the DOM.
}
Also, make sure to check if an element exist before attaching an event listener:
const toggleNav = document.querySelector('nav .toggle')
if (toggleNav) {
toggleNav.addEventListener('click', (e) => {
document.querySelector('nav .hidden').classList.toggle('show')
});
}
For Javascript:
var checkTypewriter = document.getElementsByClassName('typewriter');
if (checkTypewriter.length > 0) {
// Here write your code
}
var checkParticlesjs = document.getElementsByClassName('particlesjs');
if (checkParticlesjs.length > 0) {
// Here write your specific code
}
For JQuery:
if ($("#typewriter")[0]){
// Here write your code
}
if ($(".particlesjs")[0]){
// Here write your specific code
}
This is how you can check if your classes exist,

How to enable Keyboard navigation between full-screen images in ant design images

How to add keyboard navigation to this ant design full screen images as of now it is accessible only through click of pointer
This component adds a bit of bloat and I really can't see a callback for after it loads a preview. However, that being said... This code does what you want. It's pretty fragile, and might break on updates of the module.
Update: Because I felt like I was taking something away with this answer, I fixed it now so all arrows are in sync with keyboard actions.
*Final Update I fixed an issue where having more than one gallery would break things. Should be perfect now.
Here's a CodeSandbox Example
// Must be assigned to an onClick event
function onClickImage(e, parentContainer = null) {
let currentImgSRC;
if (e.currentTarget) {
currentImgSRC = e.currentTarget.querySelector("img").src;
// Keep track of our parent container, as not to include other containers
parentContainer = e.currentTarget.parentElement;
} else {
currentImgSRC = e;
}
let PreviewWrapDoms = document.getElementsByClassName(
"ant-image-preview-wrap"
);
let ImgPreviewBodyDom = document.getElementsByClassName(
"ant-image-preview-body"
)[0];
// I don't see any kind of callback accessible for when the target
// gets rendered, so we simply wait until the time is right.
setTimeout(function () {
// Previous viewers aren't self clearing, so I added that in.
// Otherwise this breaks with multiple galleries
if (typeof ImgPreviewBodyDom === "undefined" || PreviewWrapDoms.length > 1 ) {
for (let WrapIndex = 0; WrapIndex < PreviewWrapDoms.length; WrapIndex++) {
if ( window.getComputedStyle(PreviewWrapDoms[WrapIndex]).display === "none") {
PreviewWrapDoms[WrapIndex].parentElement.parentElement.remove();
}
}
onClickImage(currentImgSRC, parentContainer);
return;
}
let ImgPreviewDom = ImgPreviewBodyDom.getElementsByClassName(
"ant-image-preview-img"
)[0];
let LeftArrowDom = ImgPreviewBodyDom.getElementsByClassName(
"ant-image-preview-switch-left"
)[0];
let RightArrowDom = ImgPreviewBodyDom.getElementsByClassName(
"ant-image-preview-switch-right"
)[0];
// Assigning event listeners for the left and right arrows.
if (LeftArrowDom.getAttribute("listener") !== "true")
LeftArrowDom.addEventListener("click", (e) => {
e.preventDefault();
onClickImage(
LeftArrowDom.getAttribute("data-img-prev"),
parentContainer
);
});
LeftArrowDom.setAttribute("listener", "true");
// Now the right arrow
if (RightArrowDom.getAttribute("listener") !== "true")
RightArrowDom.addEventListener("click", (e) => {
e.preventDefault();
onClickImage(
LeftArrowDom.getAttribute("data-img-next"),
parentContainer
);
});
RightArrowDom.setAttribute("listener", "true");
// Set these previous and next img sources to the current
// for the edge cases that may be there is no next or previous
let previousImgSRC = currentImgSRC;
let nextImgSRC = currentImgSRC;
let ImgDoms = parentContainer.getElementsByClassName("ant-image-img");
// Cycle through the dom in the container to see which we are
for (let ImgDomIndex = 0; ImgDomIndex < ImgDoms.length; ImgDomIndex++) {
if (currentImgSRC === ImgDoms[ImgDomIndex].src) {
if (ImgDomIndex > 0) { //If greater than zero, we know there's a previous
previousImgSRC = ImgDoms[ImgDomIndex - 1].src;
LeftArrowDom.setAttribute("data-img-prev", previousImgSRC);
LeftArrowDom.classList.remove(
"ant-image-preview-switch-left-disabled"
);
} else {
LeftArrowDom.classList.add("ant-image-preview-switch-left-disabled");
}
if (ImgDomIndex + 1 < ImgDoms.length) { // if the next index doesn't exist, no next
nextImgSRC = ImgDoms[ImgDomIndex + 1].src;
LeftArrowDom.setAttribute("data-img-next", nextImgSRC);
RightArrowDom.classList.remove(
"ant-image-preview-switch-right-disabled"
);
} else {
RightArrowDom.classList.add(
"ant-image-preview-switch-right-disabled"
);
}
// Once we know where we are in the index, we can set the preview
// Image source to our desired result.
ImgPreviewDom.src = currentImgSRC;
// We break here because once we know our index, we don't need more
break;
}
}
// checks for keydown events on the dom.
this.onkeydown = (e) => {
e = e || window.event;
switch (e.keyCode) {
// we use recursion here to keep everything contained nicely.
case 37:
onClickImage(previousImgSRC, parentContainer);
break;
case 39:
onClickImage(nextImgSRC, parentContainer);
break;
default:
break;
}
};
}, 500);
}
Here's the code you'll need for an Image preview group:
<Image.PreviewGroup>
<Image
onClick={onClickImage}
width={200}
src="https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg"
/>
<Image
onClick={onClickImage}
width={200}
src="https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg"
/>
</Image.PreviewGroup>
This Link can help you in similar way actually this is a image gallery that uses onclick event to open images you can use similar concept to get response.
https://www.codespeedy.com/how-to-create-a-tab-image-gallery-html-css-and-javascript/

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;
}

Vue Transition - JavaScript hooks

Based on this answer, I'm trying to create a Vue slideToggle component using transition.
The slideToggle is a classic paradigm in height animation. I've been successful so far...
I don't want to set a fixed max-height or height, I want the height to be dynamic.
My animation is working properly when displaying and hiding. The problem is in canceling while displaying or hiding.
How to handle the #enter-cancelled and the #leave-cancelled hooks? I'm new to vue transitions :)
I put my code inside this CodeSandBox: https://codesandbox.io/s/vue-template-3b7oj
I don't know if this helps you, but try this:
declare a new variable:
data() {
return {
height: null,
toggling: false
};
},
when the open or close function start, verify if toggling is true, if yes, just cancel, like this:
enter(el) {
if (this.toggling) return;
this.toggling = true;
this.height = el.offsetHeight;
el.style.overflow = "hidden";
el.style.height = 0;
el.style.paddingTop = 0;
el.style.paddingBottom = 0;
el.style.marginTop = 0;
el.style.marginBottom = 0;
setTimeout(() => {
el.style.transitionProperty = `height, margin, padding`;
el.style.transitionDuration = this.duration + "ms";
el.style.height = this.height + "px";
el.style.removeProperty("padding-top");
el.style.removeProperty("padding-bottom");
el.style.removeProperty("margin-top");
el.style.removeProperty("margin-bottom");
this.toggling = false;
});
},
Will be something like this:
https://codesandbox.io/s/vue-template-78n7t?fontsize=14
Maybe i broke your code, sorry, but will you get the idea.
As per the offical documentation Javacript transition hooks
the #leave-cancelled is only available with v-show, where are in your sample code you are using v-if, if you change this you will be able to capture the #leave-cancelled hook,#leave-cancelled and #enter-cancelled are triggered when enter or leave are interrupted, say you press the toggle button while opening as well as pressing the button while its closing.
Vue-Transition-Cancel
tl;dr
leave event cancels not yet called enter
enter cancels not yet called leave
cancel state is stored in
el._enterCb.cancelled
el._leaveCb.cancelled
analysis
Consider:
const cb = el._enterCb = once(() => {
if (expectsCSS) {
removeTransitionClass(el, toClass)
removeTransitionClass(el, activeClass)
}
if (cb.cancelled) {
if (expectsCSS) {
removeTransitionClass(el, startClass)
}
enterCancelledHook && enterCancelledHook(el)
} else {
afterEnterHook && afterEnterHook(el)
}
el._enterCb = null
})
source: _enterCb
So a naive solution to cancel #enter is
el => {el._enterCb.cancelled = true; done()}
This is what actually happens when one triggers leave
// call enter callback now
if (isDef(el._enterCb)) {
el._enterCb.cancelled = true
el._enterCb()
}
source: leave
Same applies to:
const cb = el._leaveCb = once(() => {
if (el.parentNode && el.parentNode._pending) {
el.parentNode._pending[vnode.key] = null
}
if (expectsCSS) {
removeTransitionClass(el, leaveToClass)
removeTransitionClass(el, leaveActiveClass)
}
if (cb.cancelled) {
if (expectsCSS) {
removeTransitionClass(el, leaveClass)
}
leaveCancelled && leaveCancelled(el)
} else {
rm()
afterLeave && afterLeave(el)
}
el._leaveCb = null
})
source: _leaveCb
One can check for possible assignments:
https://github.com/vuejs/vue/search?q=_leaveCb&unscoped_q=_leaveCb

How can I retain the scroll position of a scrollable area when pressing back button?

I have a long list of links inside a big scrollable div. Each time when a user click on a link then click the back button, it starts at the very top of the div. It is not user friendly to our users. Any ways to let the browser scroll to the previous position when pressing the back button?
Thank you very much!
During page unload, get the scroll position and store it in local storage. Then during page load, check local storage and set that scroll position. Assuming you have a div element with id element. In case it's for the page, please change the selector :)
$(function() {
$(window).unload(function() {
var scrollPosition = $("div#element").scrollTop();
localStorage.setItem("scrollPosition", scrollPosition);
});
if(localStorage.scrollPosition) {
$("div#element").scrollTop(localStorage.getItem("scrollPosition"));
}
});
I think we should save scroll data per page, also we should use session storage instead of local storage since session storge effects only the current tab while local storage shared between all tabs and windows of the same origin
$(function () {
var pathName = document.location.pathname;
window.onbeforeunload = function () {
var scrollPosition = $(document).scrollTop();
sessionStorage.setItem("scrollPosition_" + pathName, scrollPosition.toString());
}
if (sessionStorage["scrollPosition_" + pathName]) {
$(document).scrollTop(sessionStorage.getItem("scrollPosition_" + pathName));
}
});
I had the same problem with a simple user interface consisting of a fixed menu div and a scrolling document div ("pxeMainDiv" in the code example below). The following solution worked for me in Chrome 47.0.2526.106 m and in Firefox 43.0.3. (My application is for use in-house and I did not need to cater for old versions of IE).
$(document).ready(function(){
if (history.state) {
$("#pxeMainDiv").scrollTop(history.state.data);
}
$("#pxeMainDiv").scroll(function() {
var scrollPos = $("#pxeMainDiv").scrollTop();
var stateObj = { data: scrollPos };
history.replaceState(stateObj, "");
});
});
On the div scroll event, the scroll position of the div is stored in the state object inside the browser history object. Following a press of the Back button, on the document ready event, the scroll position of the div is restored to the value retrieved from the history.state object.
This solution should work for the reverse navigation of an arbitrarily long chain of links.
Documentation here: https://developer.mozilla.org/en-US/docs/Web/API/History_API
When using window.history.back(), this is actually default browser functionality as user SD. has pointed out.
On a site I am currently building, I wanted the logo of the company to backlink to the index page. Since jQuery 3, $(window).unload(function() should be rewritten to $(window).on('unload', function(). My code looks like this (using Kirby CMS' php syntax):
<?php if ($page->template() == 'home'): ?>
<script>
$(function() {
$(window).on("unload", function() {
var scrollPosition = $(window).scrollTop();
localStorage.setItem("scrollPosition", scrollPosition);
});
if(localStorage.scrollPosition) {
$(window).scrollTop(localStorage.getItem("scrollPosition"));
}
});
</script>
For anyone coming from react or anything similar to react router, here are two simple functions:
export function saveScrollPosition(context: any) {
let path = context.router.route.location.pathname;
let y = window.scrollY;
sessionStorage.setItem("scrollPosition_" + path, y.toString());
}
export function restoreScrollPosition(context: any) {
let path = context.router.route.location.pathname;
let y = Number(sessionStorage.getItem("scrollPosition_" + path));
window.scrollTo(0, y);
}
If a back button is kind of history back button window.history.back() Then what you are seeking for, is a default browser functionality. So you don't have to worry about it.
If your back button actually point to some URL in your application via link or form, then you have to take care that manually.
For solution you may use cookies to store your page scroll value. Each time user scroll on your page, do save scroll value for that page to cookie. Extra work is applied to manual cookie management.
window.onScroll = function(){
document.cookie="pageid=foo-"+window.scrollY+";";
}
This cookie value can be use to set scroll value of the page on page visit.
window.scroll(0,cookievalue);
With History api you can utilize scrollRestoration and stop browser from resetting scroll position.
Read it here. https://developers.google.com/web/updates/2015/09/history-api-scroll-restoration
/* Use the below code to restore the scroll position of individual items and set focus to the last clicked item. */
(function ($)
{
if (sessionStorage)
$.fn.scrollKeeper = function (options)
{
var defauts =
{
key: 'default'
};
var params = $.extend(defauts, options);
var key = params.key;
var $this = $(this);
if (params.init === true)
{
var savedScroll = sessionStorage.getItem(key);
if (typeof savedScroll != 'undefined')
{
var int_savedScroll = parseInt(savedScroll);
if (int_savedScroll > 0)
$this.scrollTop(int_savedScroll);
setTimeout(function ()
{
var selectorFocus = sessionStorage.getItem(key + "-focus");
if (selectorFocus && selectorFocus != "")
$(document.querySelector(selectorFocus)).focus();
}, 100, key);
}
}
window.addEventListener("beforeunload", function ()
{
sessionStorage.setItem(key, $this.scrollTop());
if (document.activeElement)
{
var selectorFocus = elemToSelector(document.activeElement);
if (selectorFocus)
sessionStorage.setItem(key + "-focus", selectorFocus);
else
sessionStorage.setItem(key + "-focus", "");
}
else
sessionStorage.setItem(key + "-focus", "");
});
};
function elemToSelector(elem) /* written by Kévin Berthommier */
{
const {
tagName,
id,
className,
parentNode
} = elem;
if (tagName === 'HTML') return 'HTML';
let str = tagName;
str += (id !== '') ? `#${id}` : '';
if (className)
{
const classes = className.split(/\s/);
for (let i = 0; i < classes.length; i++)
{
if(typeof classes[i] === 'string' && classes[i].length > 0)
str += `.${classes[i]}`;
}
}
let childIndex = 1;
for (let e = elem; e.previousElementSibling; e = e.previousElementSibling)
{
childIndex += 1;
}
str += `:nth-child(${childIndex})`;
return `${elemToSelector(parentNode)} > ${str}`;
}
})(jQuery);
/*
Usage:
$('#tab1div').scrollKeeper({ key: 'uniq-key1', init: true });
If you don't need to restore the scroll positions (for example for restart), and you need just to save the scroll positions for the next time, use:
$('#tab1div').scrollKeeper({ key: 'uniq-key1', init: false });
*/

Categories

Resources