I have a jQuery function that runs when a box is clicked that affects multiple other elements on the page (creates a slideshow from a group of divs). There are multiple boxes on the page and I need to reset the elements affected by the function back to normal whenever the box is clicked again. Is there an easier way to reset the elements affected by the jQuery, or basically run the function backwards vs. writing another function that undoes what the first function did?
here is a jsfiddle.
There's no simple way to run the function backwards, because you need to know how to apply the opposite effect. Let's say you apply some color, you would first need to save the current color to be able to revert it.
The best way is to write a function that undoes what the first function did. Besides you can customize the "undo" function (suppose you want to do a different animation when scaling down an image or so).
One easy way to do what you describe is to use CSS classes to encapsulate all of the changes you want to apply with JQuery. Your first function uses addClass to apply the changes and then you can use removeClass to revert the changes.
Nope, there is no shortcut to this other than writing a custom function that does what you need.
Here's a discussion on the jQuery forum about this.
Well just a supplementary comment about your jsfiddle -- in your click listeners you should avoid create so many jquery objects using over and over again:
$(this)
Instead, define it just once at the top of the function:
var $this = $(this);
It will help performance and is a best practice.
Related
I understand how to implement a single click counter. However, I am struggling to figure out how to implement a button that will duplicate the click counter, while also allowing me to use the same javascript code.
My current layout for a single click counter has a div for the counter then a button for incrementing and another for decrementing. For the buttons, I am using a separate event listener for each one.
<div id="counter">0</div>
<button id="increment">+1</button>
<button id="decrement">-1</button>
Javascript:
var incr = document.getElementById('increment');
var decr = document.getElementById('decrement');
incr.addEventListener('click', ...);
decr.addEventListener('click', ...);
How would I implement another button that would copy the counter? Also, how do I approach the javascript side? My current approach does not seem to generalize well for multiple counters.
To generate a new div you will need to create it and append it to the DOM.
Something like:
const myDiv = document.createElement('div');
myDiv.id = 'id-blabla';
document.body.appendChild(div);
You can wrap that in the click handler.
Now, if you want to update several counters at the same time on a single button click, you will need to use another selector, instead of the id. Use a class or similar, like: <div class="counter"></div>, and then in the click handler get all those elements and iterate over them updating the counter value.
Does that make sense to you?
(Also, this link I think it will be very helpful for you, in terms of iterating over DOM elements and other concepts).
EDIT: This other answer on DOM manipulation is going to be very useful too!
EDIT2:
I understand now that what you want is to be able to generate counters (with the div and the buttons), that will have their click handlers and everything set it up correctly. You can use different approaches here, but the main purpose is to not repeat yourself:
You need your buttons to be able to identify the counter div target. You can achieve this using a custom data attribute in your buttons, like: <button class="increase" data-counterId="counter1"></button>. This way you would only need to write the click handler once (on all elements with "increase" or "decrease" class, and in the code extract the counterId data attribute so you would be able to edit the correct div counter.
You can achieve the same having some sort of common part in the id's of each block of HTML elements, like counter-1, increase-1 and decrease-1. Then each button would know which index it has, and which index the counter target should have.
This definitely looks like a component you could wrap in a Counter class. I don't know how familiar you are with JS UI frameworks, but depending on how complex your system is, you might want to think about using one. I recommend you to take a look to this link that explains React Components. Maybe it is out of scope of what you need, but I think it is worth to mention it.
I think with this information you have a good starting point to decide how you want to implement it, based on the size and complexity of what you want to build. I hope it helps!
My question involves the performance gain or loss when declaring and using functions with JavaScript. Suppose I have a function, foo(), defined as such:
function foo(arg1) {
//a simple CSS change, such as changing background color
}
Where arg1 is a String used to identify an HTML element. foo() is triggered on a mouseover event. The CSS change happens using jQuery, but I don't think that's important to my question. I would like to revert the CSS property back to default on a mouseout event. Would there be performance advantages of declaring another function, foo2(), such as:
function foo2(arg1) {
//undo the CSS change
}
Or would it be better to redefine foo() as follows, using a second argument as a switch:
function foo(arg1,arg2) {
if(arg2 == 'change') {
//make the CSS change for arg1
}else if(arg2 == 'revert') {
//revert the change for arg1
}
}
I am not concerned with the load time of the page. My goal is to minimize my amount of code, but without hampering the speed of the CSS change.
EDIT: arg1 will not strictly refer to one element. It can be used to identify a set of elements, such as a class name shared by <td>'s in a column. This is why the :hover CSS selector will not do what I need to do.
EDIT: Again, for clarity, suppose I have a set of elements containing the class arg1. I want ALL of the elements to experience the CSS change even when only ONE of the elements with that class name triggers the event.
You may want to consider using the CSS :hover psuedoselector instead of implementing this in javascript. To use your example of changing background color:
#yourElementID {
background-color: blue;
}
#yourElementID:hover {
background-color: green;
}
This will change the background color to green when the mouse is over the element, and back to blue when the mouse leaves.
As far as the best choice for code maintenance purposes, you would want to combine the two functions into a single function. This makes your code much more readable for people in the future.
As an added bonus, combining the functions leaves the possibility of very eloquent code such as switching the CSS back using a simple conditional or negation of a true to false value etc.
Robert C. Martin in Clean Code writes
FUNCTIONS SHOULD DO ONE THING. THEY SHOULD DO IT WELL. THEY SHOULD DO
IT ONLY.
He goes on to say
Flag arguments are ugly. Passing a boolean into a function is a truly
terrible practice. It immediately complicates the signature of the
method, loudly proclaiming that this function does more than one
thing. It does one thing if the flag is true and another if the flag
is false!
While your situation is not a boolean parameter, I interpret Martin's advice to apply to any parameter used for a branch to "do more than one thing".
Like many others have said, if you are just changing CSS-based on whether the mouse is hovering over an HTML element or not then you should probably just do that in the CSS with :hover:.
If you must use jQuery for some reason this is probably the best way to do it.
// Add event listener for mouseover to element(s)
$(".myHtmlElements").on("mouseover", function(){
// 'this' always refers to the element that triggered the event in jQuery
$(this).css("background-color", "#FF0000"); // Red
});
// Add event listener for mouseout to element(s)
$(".myHtmlElements").on("mouseout", function(){
// 'this' always refers to the element that triggered the event in jQuery
$(this).css("background-color", "#0000FF"); // Blue
});
I have a div in CSS that works like this: SomeDiv has another class, that's sometimes SomeRedDiv and other times SomeBlueDiv. When I mouseenter on SomeDiv, I want it to add the class SomeYellowDiv. But when I mouseleave, I want it each div to return to its initial state, either SomeRedDiv or SomeBlueDiv. This is what I have:
<div class="SomeDiv SomeRedDiv"></div>
<div class="SomeDiv SomeBlueDiv"></div>
$('.SomeDiv').mouseenter(function () {
// this makes all SomeDivs turn yellow
$(this).removeClass().addClass('SomeDiv SomeYellowDiv');
});
$('.SomeDiv').mouseleave(function () {
// here I want to use closure so that the function remembers
// which class it initially was; SomeBlueDiv or SomeRedDiv
$('this).removeClass().addClass('SomeDiv'); // add the initial color class
});
I could do this with a global but I want to see if a closure would make my code better; I know the concept of closure that allows functions to remember their state but I'm not sure how to make it work here.
Thanks for your suggestions.
Clsoures don't apply here, since you have two unrelated functions.
Instead, you should use $(this).data(...), which stores arbitrary data associated with an element.
There's no real need for closures here - you just need to push the red/blue class into some other data container on mouse enter, then reinstate it on mouse leave.
$('.SomeDiv').mouseenter(function () {
//remember the current colour class...
$(this).data('orig-colour', $(this).is('.SomeDivBlue') ? 'Blue' : 'Red'));
//...and now remove it and add yellow
$(this).removeClass('SomeDivRed SomeDivBlue').addClass('SomeYellowDiv');
});
$('.SomeDiv').mouseleave(function () {
//remove yellow and reinstate the original colour class
$(this).removeClass('SomeDivYellow').addClass('SomeDiv'+$(this).data('orig-colour'));
});
Note also I remove only the classes that need to be removed, as opposed to your code where you were removing all classes then re-adding as required.
You might also want to think about delegating the event if you have a lot of divs as this is more optimal performance wise. This isn't a big change;
$('.SomeDiv').mouseenter(...
becomes something like
$('body').on('mouseenter', '.SomeDiv', ...
Finally, I assume there is some programmatical reason as to why you physically need to remove a class. If the purpose is purely visual, at the risk of pointing out the obvious, you should craft your CSS so the yellow class merely overrides the effects of the blue/red class, alleviating the need to explicitly remove the latter.
I have two custom dropdown lists that have the same markup. I need to have only one show at a time. Right now, I'm able to open both at the same time. Both should also close when I click off the list.
The same markup for both lists is required, so I can't use unique ID's or additional classes to make this happen.
Here is a link to my fiddle example: http://jsfiddle.net/dg7Lc/29/
Any help would be greatly appreciated, thanks!
-D
Consider adding a data attribute such as 'active' via jquery when you click on one of them, then hide all those that have that attribute.
$('.custom-select').eq(0).hide() will hide the first one.
Use .show() instead of .hide() to show (obviously) and change the index to (1) to get the second one.
First thought would be if you could wrap a span or div around either or both and use that to get around the "same markup" limitation. Other than that, though, I'd suggest using order in page - use .next() and .prev() to get between them, and something like
$("div.custom-select").get(0)
or
$("div.custom-select").get(1)
to select them from outside.
edit: if you can run them off of something like an onmouseover, onchange, or whatnot, it's even easier - the one that's changing will be passed into the function as the "this" parameter. Just hide both, and show this, or show both and hide this.
edit2: similarly, once you have one of them hidden properly - well, that one will be hidden, and respond to the ":hidden" selector. Use that to distinguish between them (and save the distinction as a jquery variable) before you go showing or hiding anything else
Hide the first:
$('.custom-select').first().hide();
Hide the second:
$('.custom-select').last().hide();
And then put these lines of code where needed.
http://jsfiddle.net/dg7Lc/31/
Basically, closing the others:
$('.custom-select').not(this).find('ul').slideUp('fast');
And for closing when clicking outside the box, I used this piece of code but it's a bit dirty:
$("body").click(function(e) {
var should = true;
for($e = $(e.target); should && $e.length; $e = $e.parent()) {
should = !$e.is(".custom-select");
}
if(should) {
$('.custom-select').find('ul').slideUp('fast');
}
});
You can bind a click to the document, that looks to see if they clicked on the custom-select or the document outside it and hides any open lists as it should:
$(document).click(function(ev){
if(!$(ev.target).is('.custom-select span')){ $('.custom-select').find('ul').slideUp('fast'); }
});
Updated JSFiddle
I'm having a problem with my project, on combination with jQuery/Coffeescript.
On my homepage I have a block with text, with an arrow underneath it.
Under there, there are buttons, and every time I have my mouse over one of those buttons, I want the text block + arrow to move.
I do this with the jQuery UI library, with the method hide and show.
In my CSS code I made several classes: .position1, .position2 and .position1. Every time over hover with my mouse over one of the buttons, I want the text block to move to a specific position, so I change it's class (if someone has a better solution, I would gladly like to hear it).
Now the problem I'm having, is that sometimes the arrow hides and appears multiple times after each other (especially when I move my mouse very fast between the buttons)
A simple (partial) version of my jQuery is as follows:
var appear_arrow = function(to_position, show_delay) {
removeClasses($('.arrow'));
$('.arrow').addClass(to_class);
$('.arrow').delay(show_delay).show('slide', 'slow');
};
var to_position1 = function() {
$('.arrow').hide(0);
$('.text_block').hide(appear);
switchClass($('.text_block'));
$('.text_block').show(appear);
appear_arrow('position1', delay);
};
$('.button1').hover(
function() {
to_position1();
},
function() {}
);
My question, does anybody know why sometimes the arrow is appearing multiple times. Or does someone has a suggestion how to better do this?
jQuery queues up all its animations on a given Element rather than resetting it first.
$("selector").stop(); // stops animations on matched elements and resets queue
A stop().fadeIn('slow') however will start from the opacity the element had when you called stop, which could be solved by for example hiding/showing it immediately, then doing a full fade at the new location - whatever looks best for you.
On a side note:
Seems you wrote wrapper functions for jQuery's removeClass and toggleClass methods.
removeClass() if called without arguments removes all classes, so you could possibly just use those if in a jQuery context anyway (assuming your methods have no extra functionality).
Seeing how often you call $('.arrow') you may want to cache your selectors, at least per iteration, then pass it to appear_arrow().
var $arrow = $('.arrow')