jQuery hide and show not properly (multiple times) - javascript

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')

Related

Why do CSS transitions occur for unshift() and not for shift() in ng-repeat lists?

I'm moving some elements in the page using ng-repeat and CSS transitions. If I change the data array using unshift the list transitions nicely. (In my application I'm transitioning position as well as opacity.)
If, however, I use shift to update the array, the DOM is updated immediately with no transition.
Here's a demo of one approach, where all works as expected aside from the transitions. Compare the behavior when using the two buttons.
$scope.items.push( $scope.items.shift() );
Here's another demo of an alternative approach, where the transitions work, but the array loses an element each time the function runs.
$scope.items.shift( $scope.items.push() );
The idea is that the user can cycle through the items in the array indefinitely and the CSS transitions occur in both directions. What seems to be the issue is that AngularJS updates the DOM in one case, but not in the other, though I wasn't able to demonstrate that in my testing.
Also, based on some reading I did I tried using track by item.id with no success. Thanks much.
Very curious.
I was playing around with different possible solutions and one thing I found is that if you change your slide up to:
$scope.slideUp = function() {
$scope.items.push( angular.copy($scope.items.shift()) );
};
It will animate the third item. So it could be a reference issue within angular?
Note: In my fiddle below, while playing around I also made changes to your ng-class and css, but it still works the same with your first demo fiddle.
Demo
Another way to do it instead of creating a new object every time is to trick Angular's ng-repeat into thinking it's a new object.
$scope.inc = 9;
$scope.slideUp = function() {
var item = $scope.items.shift();
item.id = $scope.inc++;
$scope.items.push( item );
};
$scope.slideDown = function() {
var item = $scope.items.pop();
item.id = $scope.inc++;
$scope.items.unshift( item );
};
I added an id property to each object that I increment when sliding up or down, then set the ng-repeat to track by the id:
Demo
I think I understand your problem.
In your first "faulty" demo, you can see that when you press "Slide up", Item One will indeed fade out, but it appears on the bottom of the page! That's because you add it as the last element, and only then it begins its transition.
On Sliding down, the element is just pushed down and hence you see the transition more intuitively, namely coupled close with the first three elements, but actually the same thing happens.
What still puzzles me is why Asok's solution works the way you would expect it. I'll edit when I think of a reason.

DOM manipulation performance when toggle visibility

I have bunch of images in a wrapper div. I need to hide all of them except the one user clicks on.
What is the most performance concise way of doing that?
Should I apply class on all of them to hide them all and than filter out the one that was clicked and show only that one or should I loop over them all hiding them as loop progresses with exception of the one that was clicked or something else?
Tnx
In modern desktop browsers you won't see any difference. Browsers are tuned so that they are blazing fast in rendering any changes is DOM three. Guts tell me that it might be sligtly faster to loop through all images and set visibility depending on item clicked using style attribute and not using class. In that way you have to process only N elements and no external CSS files are involved. If you hide all and show the element with was clicked, you process N + 1 elements.
In your situation I would use solution that is fastest, more managable and clean from the developers standpoint as there won't be much difference in the final result if you use one method or another.
PS: If you're using jquery, you can use the following:
Lets say, your div has id='test-div', and there are several images in it. All these images can be accessed as:
$('#test-div img')
Now, lets assume you know the id of image which got clicked. Lets assume id='my-image'.
You can execute the following to hide all other images (except 'my-image'):
$('#test-div img').not('#my-image').addClass('hide')
One of the most performant ways would be to let CSS do the visibility. It sounds to me like you're only displaying one at a time, in which case you can do it with two DOM operations by using classes;
// scope above
var lastClicked = null;
// then in click listener, 1st param `e`
if (lastClicked) lastClicked.className = ''; // remove visible class
lastClicked = e.target; // get clicked node
lastClicked.className = 'visible'; // add visible class
I'm assuming event.target but depending how the listener is attached, you might want to use this or some other logic. Further, if you expect element.classList support, you can use add and remove from that.
Example CSS of how to show only nodes with class token visible.
selector:not(.visible) {
display: none;
}

how to reset elements affected by a jquery function

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.

jqueryui drag element multiple times

how can set up drag and drop to allow dragging of one element multiple times
i have made something like this http://jsfiddle.net/28SMv/3/ but after draging an item from red to blue the element loses its "draggability" and i cant drag it once more and more.
adding revert:true to draggable seems to work but then i need to remove helper:'clone' cant use them at the same time
If you drag an item & release without dropping, it stops being draggable. I'd remove all droppable stuff from that jsfiddle example, just put a bunch of draggables on the screen & try with that.
One possible solution is to rebind draggables after you're done dragging. Think:
function rebindDraggables() {
$('...').draggable({
...
stop: function() {
rebindDraggables();
}
})
}
I'm not sure why in your examples draggables cease to be draggable after the first time. The jQuery docs might explain this.
You're duplicating/cloning your elements, so the new one obviously won't be draggable. You could make it draggable, although that's much too much work. Just move the original via .append(). Note that when you append an existing object to another element, it will remove the original from its place. That seems to me like what you want to do.
Example: http://jsfiddle.net/28SMv/7/
You might want the items to be transferable back into the original div: http://jsfiddle.net/28SMv/8/
Note that I'm using jQuery 1.4.4 there, when you switch over to 1.5 it doesn't work. Not sure if that's a bug, or a feature of jQuery. My guess is that it's a bug of 1.5.

Logic behind hiding elements

I am having a dilemma in the logic of this particular issue. Forgive me if this is quite newbie question but I'd rather have a solid bg on it.
There are a lot of examples of this all around the web where you click on an element to display another element. such case may be a menu that when you hover your mouse on it (or click on it) its get displayed. Later the element gets hidden either on mouse out, OR CLICKING ON ANY OTHER ELEMENT.. so, how is this achieved? I am sure the solution is not to bind a "hideElem" function on all the elements.
regards,
I haven't done it in a while, but an easy solution is to add a click event to the top of the DOM tree that will close the open element. Here's an example in psuedo-javascript:
document.body.onclick = function() {
element.style.display = "none";
}
If you need complex behaviors inside the "shown" element, make sure your preventing the necessary events from propagating up the DOM tree.
element.onclick = function(e) {
e.stopPropagation()
}
In general, the logic is the other way around (at least with menus) i.e. the element in question is hidden until a state-event unhides it, then hidden again as dictated. The point being that the hiding/unhiding logic is usually tied to the element itself, not everything else on the page.
As to how it's done, methods vary. There are lots of Javascript solutions, mostly along the lines of those already outlined, but menus can also be done purely with CSS - typically utilising the display: none; property, though you can also do stuff like setting/unsetting a negative margin so that the element is moved 'off and on the page'.
To use some of my own work by way of example:
Drop-down menu with Javascript
Drop-down menu with jQuery
Drop-down menu with CSS
$('#target').bind('click', function(event)
{
var $element = $('#element');
$element.show();
$(document).one('click', function()
{
$element.hide();
});
// If you don't stop the event, it will bubble up to the document
// and trigger the click event we just bound.
// This will hide the element right now just after showing it,
// we don't want that.
event.stopPropagation();
}
You have to keep in mind that a Javascript event goes up and down the whole tree when begin fired. So you can bind event listeners to any parent when you want to listen for an event on many elements.
This is called event delegation.
A cheap way to do it potentially is to bind an event handler to the "(on)blur" event of the clickable item and/or it's target. If your design allows.
That is one way to do it.
You could also write a method that traps (hooks into) all 'click' events regardless of the element, and hide your menu from there.
JQuery would make this task easier for you.
step 1- use a javascript library so you can have the code be as cross browser as possible - otherwise you have to cater to two different event models between internet explorer and gecko/webkit based browsers. JQuery, Mootools, YUI - all will handle this for you - there are more but those 3 are my favorite and are well documented.
step 2 - you prob would want to implement a clickshield for this - essentially a block-level dom element that is absolutely positioned over your entire page with a higher z-index than the rest of the page. attach a click event to that, and you can perform your logic for hiding elements on the page. The clickshield could easily have javascript code expand it to the width -height of your page post DOM rendering using the methods of any of the aforementioned javascript libraries.

Categories

Resources