A concise, simpler way to debounce the onclick event - javascript

Setup
I have a Mobile User Interface with its own UI Back Button in the bottom-left corner of the viewport.
N.B. In this case, the UI is an overlay which appears above the web-page being visited, so as much as I'm a fan of History.pushState(), in this
case, I have no specific intention to maintain a navigable history of
overlay views using History.pushState().
If the user has progressed into the UI-overlay by two or more views, then, whenever the UI Back Button is clicked or tapped, the UI-overlay should display the previous view.
Issue
I noticed my UI Back Button can be pretty sensitive and it's easy to inadvertently interact with it such that the EventListener thinks it has been tapped twice or even three times, when the user-intention is simply to tap it once.
Proposed Solution
Evidently some kind of debouncing is needed, as we might more commonly use with an onscroll or an onresize event.
I have written debouncers before using named setTimeout() functions and using clearTimeout() to repeatedly cancel the named setTimeout(), so that only one scroll or resize event (the very last one) actually fires.
But a debouncer with repeated clearTimeout() functions feels over-elaborate in this context.
Further Issues
Ideally, I am looking for a simple, quick, easy, non-verbose way to momentarily deactivate the EventListener after the first click has been detected.
I could use removeEventListener() - but this requires, in every deployment, specific knowledge of the format of the original addEventListener()
I could use AbortSignal - but this requires signal to be set in the options parameter of the original addEventListener() and I can't count on that
Is there a simple and generic approach to debouncing onclick events?

There is an unexpectedly simple way to debounce an onclick event, requiring only two lines of javascript.
The approach sets the CSS pointer-events property of the EventTarget to none before resetting it to auto moments later:
const myFunction = (e) => {
e.target.style.setProperty('pointer-events', 'none');
setTimeout(() => {e.target.style.setProperty('pointer-events', 'auto')}, 800);
// REST OF FUNCTION HERE
}
Working Example:
const myElement = document.querySelector('.my-element');
const myFunction = (e) => {
e.target.classList.add('unclickable');
setTimeout(() => {e.target.classList.remove('unclickable')}, 2000);
console.log('myElement clicked');
}
myElement.addEventListener('click', myFunction, false);
.my-element {
width: 100px;
height: 100px;
line-height: 100px;
text-align: center;
font-family: sans-serif;
font-weight: 900;
color: rgb(255, 255, 255);
background-color: rgb(255, 0, 0);
transition: opacity 0.3s linear;
cursor: pointer;
}
.my-element.unclickable {
opacity: 0.5;
pointer-events: none;
}
<div class="my-element">Click me!</div>

Related

How to change button color based on background?

I write in React. On the page I have a scroll up button.
When scrolling on some blocks, it is not visible due to the fact that the colors match.
How to make it so that when it touches certain blocks of the same color as the button, it turns white.
How to track the contact of a button with a specific block? What listeners to put?
Since the button is probably position: fixed or position: absolute, the easiest solution would be to give CSS property mix-blend-mode a try, instead of listeners, as there is no accurate way of telling the position w.r.t to background.
Check this
Try to give mix-blend-mode: difference; for the scroll to top button.
Although I think in this case you will have the color yellow for the button when it overlaps blue.
Although Sanjay's answer "works", you really don't have much control over the styles you get, unless that blend-mode:difference is the look you're going for.
For more fine grained control, you need to use the IntersectionObserver API.
There's really three steps to this process:
1. Get the required options for IntersectionObserver constructor
This means you need to find out the negative margins to set as the rootMargin key. This key is part of the configuration object of your IntersectionObserver instance.
In practice you would have to find the distance of your trigger element(in your case the floating action button) to the edges of the viewport. In the code snippet below, this is done using the getDistanceToEdge() function.
It's better to do it this way instead of statically setting your margins, since it dynamically infers the margins from the position set from your styles.
2. Tell the observer what you wish to when triggered
This is the callback which will trigger the new styles on your floating button. You need to check whether your elements are intersecting with the button using the entry.isIntersecting key, and then conditionally render the style.
3. Setup the observer with elements you wish to cause the trigger
The elements that scroll up causing your floating button to change color, should be passed to the observer, using the observer.observe() function.
The following code snippet really does all the setup you need. It won't trigger unless the element is actually underneath the button (the API internally checks vertical and horizontal overlaps).
For some reason I can't get it to work on iframes embedded in browsers. It could be something to do with the negative margins and multiple viewports.
It works perfectly on normal full page websites, as shown in this hosted link.
One little caveat is that consecutive elements that trigger the observer, will cause each other to cancel out, since the button enters the second area before fully leaving the first one. I'll leave it to someone else to figure out the specifics there.
// get the element that reacts to background elements
const fabElement = document.getElementById("fab");
// what do you want to do when it overlaps?
function reactToOverlap(entries) {
entries.forEach((entry) => {
if (entry.isIntersecting) fabElement.classList.add("triggered");
else fabElement.classList.remove("triggered");
});
}
const observerOptions = {
rootMargin: getDistanceToEdge(fabElement)
.map((val) => `-${val}px`)
.join(" "), // https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#rootmargin for syntax
};
const observerInstance = new IntersectionObserver(
reactToOverlap,
observerOptions
);
// what elements do you want to trigger the change?
const elementsToTriggerFab = document.querySelectorAll(".triggeringContent");
elementsToTriggerFab.forEach((el) => observerInstance.observe(el));
/* Represents how far the element is from the edge of the browser, in all directions. */
function getDistanceToEdge(element) {
const elementBounds = element.getBoundingClientRect();
// top and left margins are identical to the pixel coordinates
const top = elementBounds.top;
const left = elementBounds.left;
// bottom and right need to be subtracted from the viewport bounds
const bottom = window.innerHeight - elementBounds.bottom;
const right = window.innerWidth - elementBounds.right;
return [top, right, bottom, left];
}
body {
font-size: 3rem;
background-color: white;
margin: 0;
padding: 0;
}
#fab {
background-color: blue;
color: white;
border-radius: 50%;
position: fixed;
padding: 1rem;
bottom: 50px;
right: 50px;
transition-property: background-color, color;
transition-duration: 0.5s
}
#fab.triggered {
background-color: white;
color: blue;
}
.normalContent {
color: green;
border: 10px solid green;
height: 100vh;
}
.triggeringContent {
height: 300px;
background-color: blue;
color: white;
}
.triggeringContent.avoidFab {
margin-right: 200px;
}
<div id="fab">Up</div>
<div class="normalContent">Other Content</div>
<div class="triggeringContent">Triggerring Content</div>
<div class="triggeringContent avoidFab">Triggerring Content</div>
<div class="normalContent">Problems coming up</div>
<div class="triggeringContent">Triggerring Content</div>
<div class="triggeringContent">Triggerring Content(Error)</div>
<div class="normalContent">Other Content</div>
<div class="triggeringContent avoidFab">Triggerring Content</div>
<script type="module" src="/main.js"></script>
Shoutout to cloned's comment for giving the idea for the solution.

Delayed CSS rendering while Javascript does it's work

I have a very large and complicated jQuery plugin which I will avoid posting here for simplicity's sake. My problem is very simple, and I will reduce it only to the relevant code:
I have a click event attached to a set of buttons:
$("ul.tick-boxes button").click(function(event) {
event.preventDefault();
$("ul.product-grid").addClass("loading");
$(this).toggleClass("active");
$theElement.trigger("filterOptionsChanged");
});
If you go to this link, you can see these in action in the left sidebar:
http://mazer.com/collections/refrigerator?preview_theme_id=22019779
Here is the css that produces a check-mark when you click the buttons:
ul.tick-boxes button.active .tick-box::after {
content: "\e603";
font-family: "custom-icons";
color: #51425d;
top: 2px;
left: 2px;
font-size: 0.75rem;
position: absolute;
}
If your computer is as slow as mine, then when you click these filter options, it takes a second or so to "tick" the "tick-box". If you can't see it, try unticking it, which for me takes noticeably longer. The time-point where the "tick" visibly renders is always simultaneous with the product-grid rebuilding and rendering. I haven't posted the code for manipulating the product-grid, but you can know that the line $theElement.trigger("filterOptionsChanged") triggers a lot of array and object processing to build a document fragment of the new product list, and updates the DOM at the end. I understand this can take a second, that is not my problem. But what I don't understand is why my "tick-boxes" are waiting until after the code of that event is finished to render. According to my css, all I need is a class active on the button, and that code is fired one line above triggering the "filterOptionsChanged" event, so it should fire before any product grid changes happen.
Now. If I open up my inspector in chrome, I can actually see the active classes toggling instantaneously on click, before the product grid updates. However, the css which adds the tick doesn't catch the active class on the element until after my "filterOptionsChanged" code completes.
My first attempt to solve the problem will be posted below. I read a good bit about the "expensiveness" of css pseudo-selectors. That essentially to a browser, it is like a dom manipulation every time an ::after element is created. So I then write this css:
ul.tick-boxes button .tick-box::after {
content: "\e603";
font-family: "custom-icons";
color: #51425d;
top: 2px;
left: 2px;
font-size: 0.75rem;
position: absolute;
opacity: 0;
}
ul.tick-boxes button.active .tick-box::after {
opacity: 1;
}
So now an ::after element always exists from the beginning, any rendering costs are paid for at the outset, so my thought was, now when I click this button, the tick is already there, we are just giving it an opacity of 1. Didn't fix the delay.
Now, I try removing the "filterOptionsChanged" event trigger entirely. This makes my whole sorting plugin stop working, but I don't care at this point, because I want to understand what is causing the problem. When I do remove that event trigger, the buttons and css render snappy. No more problems.
I have a vague thought that, ok, if a click event can be snappy without that event trigger, I need a way of seperating the two. First add the active class, then trigger "filterOptionsChanged". I think, ok, jQuery Deferreds. Here is that code:
$("ul.tick-boxes button").click(function(event) {
event.preventDefault();
var showLoading = jQuery.Deferred();
$("ul.product-grid").addClass("loading");
$(this).toggleClass("active");
showLoading.resolve();
$.when(showLoading).done(function() {
$theElement.trigger("filterOptionsChanged");
});
});
So showLoading is a blank Deferred, I then add my classes for the tick boxes to show, then I resolve the deferred. Now I say, when showLoading is done, then do the whole product-grid manipulation. Don't do these at the same time, javascript, wait for one to finish, then do the other. Still no avail. Any ideas?
According to this, all function calls in JavaScript block the UI until they complete; I'd wager that this includes the original function call to start the click event. A cheap solution might be to replace the trigger with something like:
setTimeout(function() { $theElement.trigger("filterOptionsChanged"); }, 200);
Which will hopefully delay the trigger long enough for the browser to repaint the UI (you could/should add a little loading icon in the original function, then remove it in the timeout). You could also take a look at web workers, which look like they're pretty much threads.
Try this,
CSS:
ul.tick-boxes button.active .tick-box::after {
content: "\e603";
font-family: "custom-icons";
color: #51425d;
top: 2px;
left: 2px;
font-size: 0.75rem;
position: absolute;
}
JS:
$("ul.tick-boxes button").click(function(event) {
event.preventDefault();
$("ul.product-grid").addClass("loading");
$(this).toggleClass("active").fadeIn(10, function () {
$theElement.trigger("filterOptionsChanged");
});
});
Using callback to delay the triggering. See if this work for you.

Hover state is maintained during a transition even if the element has gone

Consider a simple element, and its associated CSS:
<div id="content">Hover me !</div>
#content {
width: 100px;
height: 100px;
}
#content:hover {
transform: translateY(500px);
transition: transform 1s 500ms;
}
JSFiddle
The principle is straightforward: while the element is hovered, it must go down. The problem is, when the mouse doesn't move, that the :hover state is maintained even if the element is not physically below the mouse anymore (due to the translation). The state seems to be updated only after an mouse move.
Notice the cursor (a pointer) and its relative position with the element!
That's a real problem when a JavaScript function must be executed only if the mouse is on an element, after a timeout:
// The mouseleave event will not be called during the transition,
// unless the mouse move !
element.on('mouseenter', executeAfterTimeout);
element.on('mouseleave', cancelTimeout);
So here are my questions:
Is this behaviour normal (compliant with the norms)?
What are the solutions to avoid this problem?
Edit : To give you a context, here is what I want to do concretely: with JavaScript, I display a tooltip when the mouse is on an element (and hide it when the mouse leaves it). But the same element can be transform-ed when the user click on it. If the user simply clicks without moving the mouse, the tooltip will remain displayed, which is a real problem. How can I detect that the element is gone?
Part 1 of your question:
The principle is straightforward: while the element is hovered, it
must go down. The problem is, when the mouse doesn't move, that the
:hover state is maintained even if the element is not physically below
the mouse anymore (due to the translation). The state seems to be
updated only after an mouse move.
So here are my questions:
Is this behaviour normal (compliant with the norms)?
Yes. This behaviour is normal. Although not specified verbatim in the standards, it is mentioned in detail here: http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105
Take this fiddle as reference: http://jsfiddle.net/Blackhole/h7tb9/3/
The upper div has mouse-events bound to it directly. The lower div has mouse-event bound to its parent. Pick up the lower one. Move the mouse slowly at one edge and watch the console to see what happens.
You touch the edge and mouseover and mouseenter are fired in quick succession (hover).
As a result the inner div translates.
Do nothing. No event is fired and so nothing happens.
Move the mouse inside the outer div. mousemove fires and the inner div is still translated.
Slowly move the mouse out. mouseout and mouseleave are fired in quick succession and the inner div translates back to its original position.
This is described here: http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105/#events-mouseevents under the section Mouse Event Order.
Step 3 above is important. Because you are doing nothing, no event is fired and hence nothing happens. If the inner div were to bounce back to its original position in this step, then it would mean that an activation happened without any event!
This is in line with the definition of event as the document in this section: http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105/#glossary-event says:
An event is the representation of some occurrence (such as a mouse
click on the presentation of an element, the removal of child node
from an element, or any number of other possibilities) which is
associated with its event target. Each event is an instantiation of
one specific event type.
Now have a look at the document here: http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105/#event-flow, just before the section 3.2 starts, it says:
After an event completes all the phases of its propagation path, its
Event.currentTarget must be set to null and the Event.eventPhase must
be set to 0 (NONE). All other attributes of the Event (or interface
derived from Event) are unchanged (including the Event.target
attribute, which must continue to reference the event target).
The last line (in parentheses) is important. The event.target continues to reference the event target even after the event completes.
Now pick the upper div in the fiddle for reference. On mouseenter the div itself is translated. It does not matter if it moves away from below the mouse pointer. The event.target is still referencing to it and if no other mouse event occurs, nothing happens and it remains translated. The moment you move your mouse (anywhere in or out), the activation occurs on the event.target (which is still this div) and now the user-agent finds that the mouse pointer is no longer over the element and immediately mouseout and mouseleave events fire (after firing mousemove of course) causing the div to translate back.
Part 2 of your question:
2.What are the solutions to avoid this problem?
Edit : To give you a context, here is what I want to do concretely:
with JavaScript, I display a tooltip when the mouse is on an element
(and hide it when the mouse leaves it). But the same element can be
transform-ed when the user click on it. If the user simply clicks
without moving the mouse, the tooltip will remain displayed, which is
a real problem. How can I detect that the element is gone?
If you look at the implementation in the lower div in this fiddle: http://jsfiddle.net/abhitalks/h7tb9/2/ ; as compared to the upper div, there is no flutter/jitter when mousing over. This is because rather than the div itself, the events are being handled on the parent.
So, that could be one solution for your use case.
See this demo: http://jsfiddle.net/Blackhole/nR8t9/9/
This addresses your edit. Tooltip gets displayed on mouseover. Tooltip gets hidden on mouseleave. The same element can be transform-ed when you click. If you simply click without moving the mouse, the tooltip hides.
Here, if you click, the element is being translated and then no further hover action would happen. The tooltip itself is implemented using a :before pseudo-element. This separates out the tooltip and the element which you want to change after click. You still handle events on the element itself. No need for timeout as it is handled by the css itself. If you mouseout, the tooltip will hide after a delay.
Hope that helps.
It's a solution to use JavaScript and a class to indicate the status. In your case, you could use mouseover event to toggle a class like this:
$('#content').on('mouseover', function() {
$(this).toggleClass('down');
});
CSS
#content.down {
background-color: deepskyblue;
transform:translateY(300px);
-webkit-transform: translateY(300px);
}
jsFiddle
The other solution is to use a wrapper as hover block
<div id="container">
<div id="wrapper">
<div id="content">Hover me !</div>
</div>
</div>
CSS
#wrapper:hover #content {
background-color: deepskyblue;
transform:translateY(300px);
-webkit-transform: translateY(300px);
}
jsFiddle
Notice, this two solutions have different behaviors for different requirements.
My suggestion is to look at this problem another way: if an element is going to be transitioned when you click on it. Why not just execute your callback on click instead of mouseleave?
I am assuming the tooltip has some connection to the element you mouseenter, in which case mouseleave and click are effectively the same - they both cause mouse pointer to not be over the element anymore (regardless of how browser behaves).
PS: note that in your example, how mouseenter and mouseleave fire also depends on whether you set the transition as default property or as a :hover state property, since this looks like an area where browser vendors are free to optimize as they please, you should probably avoid they in the first place.
http://jsfiddle.net/U44Zf/13/ - transition on #content:hover
http://jsfiddle.net/U44Zf/14/ - transition on #content
This behavior is normal to prevent the element from bouncing under the cursor. Imagine the transition would revert as soon as the element is away from the cursor. As soon as the cursor has left the element, it would go back, so the cursor is again above the element and it moves down. This way it would bounce up and down at the edge of the cursor.
One solution would be to implement the transition with JavaScript instead of CSS, then the element will "bounce". But is this really the desired behavior? What exactly are you trying to do?
This behavior is normal and can not be changed. It is correctly implemented according to the specification #Stasik linked to.
If you have to change this behavior, you could use javascript with jquery instead of css pseudo classes. I created a jsfiddle to demonstrate a possible approach using the .ismouseover() jQuery extension by #Ivan Castellanos provided here.
Check if this is the behaviour you want to accomplish. Some are example styles, adjust as you please.
http://jsfiddle.net/U44Zf/9/
.tooltip {
background-color: white;
border: 1px solid black;
padding: 10px;
opacity: 0;
transition: 1s 500ms;
transition-property: transform, opacity;
position: absolute;
right: 0;
top: 0;
}
#content:hover .tooltip {
display: block;
opacity: 1;
transform:translateX(100px);
-webkit-transform: translateX(100px);
}
#content {
transition: 1s 500ms;
transition-property: background-color, color;
cursor: pointer;
position: relative;
}
#content.active {
background-color: blue;
color: white;
}
#content.active .tooltip {
opacity: 0;
transform: none;
-webkit-transform: none;
}
I've added this javascript snippet to control the click state
$('#content').click(function () {
$(this).toggleClass('active');
});

Using addClass() and CSS transition on render function in Backbone app not working correctly

In my backbone.js application, I'm trying to fade in the view element after it's been appended. However it doesn't work.
Live example here: http://metropolis.pagodabox.com
var itemRender = view.render().el;
$('#items-list').append(itemRender);
$(itemRender).addClass('show');
However if I add a small setTimeout function, it works.
var itemRender = view.render().el;
$('#items-list').append(itemRender);
setTimeout(function(){
$(itemRender).addClass('show');
},10);
Using fadeIn() also works but I prefer to use straight CSS for the transition as it's more efficient, and prefer not to use any setTimeout "hacks" to force it to work. Is there a callback I can use for append? Or any suggestions? The full code is below:
itemRender: function (item) {
var view = new app.ItemView({ model: item }),
itemName = item.get('name'),
itemRender = view.render().el;
$('#items-list').append(itemRender);
$(itemRender).addClass('show');
app.itemExists(itemName);
}
CSS/LESS:
#items-list li {
padding: 0 10px;
margin: 0 10px 10px;
border: 1px solid #black;
.border-radius(10px);
position: relative;
.opacity(0);
.transition(opacity)
}
#items-list li.show {.opacity(1)}
This "hack" you mention (or some variant of it) is occasionally necessary for web development, simply due to the nature of how browsers render pages.
(NOTE: This is all from memory, so while the overall idea is right please take any details with a small grain of salt.)
Let's say you do the following:
$('#someElement').css('backgroundColor', 'red');
$('#someElement').css('backgroundColor', 'blue');
You might expect to see the background color of #someElement flash red for a brief moment, then turn blue right? However, that won't happen, because browsers try to optimize rendering performance by only rendering the final state at the end of the JS execution. As a result, the red background will never even appear on the page; all you'll ever see is the blue.
Similarly here, the difference between:
append
set class
and:
append
wait 1ms for the JS execution to finish
set class
Is that the latter allows the element to enter the page and AFTER the JS is executed have its style change, while the former just applies the style change before the element gets shown.
So while in general window.setTimeout should be avoided, when you need to deal with these ... complications of browser rendeering, it's really the only way to go. Personally I like using the Underscore library's defer function:
var itemRender = view.render().el;
$('#items-list').append(itemRender);
_(function(){
$(itemRender).addClass('show');
}).defer();
It's the same darn thing, but because it's encapsulated in a library function it feels less dirty to me :-) (and if the "post-render" logic is more than a line or two I can factor it in to a Backbone View method and do _(this.postRender).defer() inside my render method).
You can use CSS animations
#keyframes show {
0% { opacity: 0; }
100% { opacity: 1; }
}
#items-list li {
padding: 0 10px;
margin: 0 10px 10px;
border: 1px solid #black;
.border-radius(10px);
position: relative;
}
#items-list li.show {
animation: show 1s;
}

Making an image change once it is clicked

I’ve already spent hours looking at as many online resources and stackoverflow questions as I can find but for some reason I just can’t figure this out.
I’m attempting to use CSS and image sprites to make a link display as an image that changes once it is hovered over and once it has been clicked. I’ve played round with CSS and looked at JavaScript for far too long now and I just need some direction on how to get it working.
I’ve managed to make it change once its hovered over however what i really need to do is have the image change once it is clicked. So the begin with it displays the play button and when its clicked it displays a pause button, click it again and it displays the play button etc.
From what i can gather i will need to use JavaScript and an onclick event. However I’m not sure how this would work or how to use it with image sprites.
My CSS so far looks like this
.testclass .stratus {
background-position: -1px -1px;
width: 21px;
height: 21px;}.
.testclass .stratus:hover {background-position: -1px -29px; width: 21px; height:
21px;}.
However this only effects the play button and when it is hovered over. Now i need a way to display the pause button when the play button is clicked and vice versa.
Image sprites URL.
http://www.danceyrselfclean.com/wp-content/uploads/2012/12/sprites.png
URL of page im trying to get this to work on.
http://www.priceofmilk.co.uk/uncategorized/ddd-2
Can this be achieved using CSS and HTML or will I also need to use some JavaScript? Any assistance would be much appreciated.
I made a simple example. I use background colors and an anchor but you can easy implement this in your situation.
update
Updated the code so it uses your images.
HTML
<a class="play_pause">PLAY</a>​
CSS
.play_pause {
display: block;
width: 24px;
height: 23px;
text-indent: -99999px;
background: url(http://www.danceyrselfclean.com/wp-content/uploads/2012/12/sprites.png);
cursor: pointer;
}
.playing {
background-position: -27px 0;
}
.playing:hover {
background-position: -27px -28px !important;
}
.play_pause:hover {
background-position: 0 -28px;
}​
And the JS:
$(document).ready(function() {
$(".play_pause").click(function() {
$(this).toggleClass('playing');
});
});​​
​
JsFiddle example
If you only wanted to detect the first click, you could do this in pure CSS by giving the link an id and using the :target pseudoclass (e.g. a#theid:target {...})
But since you need to detect a second click, you'll need to use JS to toggle between CSS classes. The basic way is to use an event handler:
document.getElementById('theid').onclick = function(){
this.className = this.className=='play' ? 'pause' : 'play';
};
You will have to use JavaScript to accomplish the switching, there is no way to accomplish such logic with pure CSS.
The easiest way to go would be to have two classes play and pause. Through CSS you declare which part of the sprite you want to show for each of those classes. Then you attach a click-event listener to the element with JavaScript, and in the click-event callback you remove class play from the element and apply class pause instead, and vice versa.
MDN has a good article on how to attach event-listeners to an element. And this SO question discuss how you can add/remove classes on an element.
That is simple where have you read?
jQuery('.testclass .stratus').click(function{
jQuery(this).toggleClass('played');
})
css:
.played{
background-position: -1px -29px;
}
Example using .querySelectorAll and .addEventListener, with your current sprite. No jQuery is used.
var elm = document.querySelectorAll('.testclass .stratus'), i = elm.length, e;
while (e = elm[--i])
e.addEventListener('click', function () { // this fn generates the listener
var pause = false; // captured in scope, not global
return function () { // this is the actual listener
this.style.backgroundPositionX = (pause = !pause)?'-20px':'0px';
}
}(), false); // the () invokes the generator

Categories