Toggling appendTo() using jQuery Waypoints - javascript

My .topics element relocates itself in the DOM -- and for testing purposes acquires the "active" class -- when the Waypoint triggers.
$('.navbar').waypoint(function() {
$('.topics').toggleClass('active');
$('.topics').appendTo( $('#menu') );
}, { offset: -50 });
When the waypoint is no longer in view the "active" class is removed from .topics as expected, yet the .topics elements remains appended to #menu.
Is it possible to restore .topics to its original DOM location by either toggling appendTo(); or triggering the event when the Waypoint becomes inactive?

How about putting in an if statement to check if the element exists?
$('.navbar').waypoint(function() {
$('.topics').toggleClass('active');
if ($('#menu .topics').length) {
$('#menu .topics').remove();
} else {
$('.topics').appendTo( $('#menu') );
}
}, { offset: -50 });

Not trying to steal #Banana's rep here but you will find it more efficient to avoid having to rediscover the .topics elements several times over, every time the handler fires. For example :
assign the result of $('.topics') so it can be reused
test .hasClass('active') rather than $('#menu .topics').length
If my understanding is correct, then this should give the same effect as your adaptation of Banana's code :
$('.navbar').waypoint(function() {
var $topics = $('.topics').toggleClass('active');
if($topics.hasClass('active')) {
$topics.appendTo( $('#menu') );
} else {
$topics.appendTo( $('#orginialmenu') );
}
}, { offset: -50 });
Performance optimisations like this can be important for the quality of your site/app's UI/UX, especially when the DOM is extensive.

Related

Cycle through classes on Jquery to animate

enter image description here
I'm trying to create an animation where if you click the button the circles animate around the path and changes size. I'm not sure how i would cycle the classes on the next click ?
http://bluemoontesting.co.uk/bluemoon2016/people.html
I'm using an svg and have targeted the elements with this so far:
<script>
$(".animate-slider").click(function() {
$('.st7').toggleClass("top-left");
$('#XMLID_292_').toggleClass("left");
$('#XMLID_293_').toggleClass("center-right");
$('#XMLID_297_').toggleClass("top-right");
$('#XMLID_301_').toggleClass("top");
$('#XMLID_283_').toggleClass("top-center");
});
</script>
If anyone could help me i'd be very grateful :)
Thanks
I would take a little different approach. Instead of toggling classes, to get it to move to more than two positions, you will need to cycle the classes assigned to each element instead. Storing the class names in an array would allow you to move them in the array to cycle the position that each element moves to next. I created a simplified example.
$(document).ready(function () {
var steps = ['right', 'bottom-right', 'bottom-left', 'left', 'top'],
allClasses = steps.join(' ');
$('#go').click(function() {
$('#a').removeClass(allClasses).addClass(steps[0]);
$('#b').removeClass(allClasses).addClass(steps[1]);
$('#c').removeClass(allClasses).addClass(steps[2]);
$('#d').removeClass(allClasses).addClass(steps[3]);
$('#e').removeClass(allClasses).addClass(steps[4]);
steps.push(steps.shift()); // move first element to the end
// to cycle in the other direction you would pop and unshift instead
// steps.unshift(steps.pop()); // move last element to the beginning
});
});
You could just use setInterval like so:
var $st7 = $('.st7'); //class selectors can be expensive, so cache them
function rotate() {
$st7.toggleClass("top-left");
$('#XMLID_292_').toggleClass("left");
$('#XMLID_293_').toggleClass("center-right");
$('#XMLID_297_').toggleClass("top-right");
$('#XMLID_301_').toggleClass("top");
$('#XMLID_283_').toggleClass("top-center");
}
//2000 is milliseconds, so that's two seconds
var rotateIntervalId = setInterval(rotate, 2000);
//optionally consider stopping/starting the effect on mouse hover/exit
$('#Layer_1').on('hover', function() {
clearInterval(rotateIntervalId);
}).on('blur', function() {
rotateIntervalId = setInterval(rotate, 2000);
});

jquery keeps on selecting element after changing the class

I have a button which must change what it does after meeting some condition.
So I'm selecting the button by it's class and I want to remove that class upon meeting the condition and add a new class to the element and do something else with it. but it's not working.
I just made up an example for my problem.
this is the code:
$('.button-1').click(function(){
$('.box').width(function(){
return $(this).width() + 10;
});
$(this).removeClass('button-1').addClass('button-2');
});
$('.button-2').click(function(){
$('.box').width(function(){
return $(this).width() - 10;
});
$(this).removeClass('button-2').addClass('button-1');
});
and it's Fiddle
I expect this to toggle between increasing and decreasing the black box width, but it keeps on increasing.
That's because the event is bound statically on the button, use event delegation like this:
$(document).on('click','.button-1', function(){
$('.box').width(function(){
return $(this).width() + 10;
});
$(this).removeClass('button-1').addClass('button-2');
});
$(document).on('click','.button-2', function(){
$('.box').width(function(){
return $(this).width() - 10;
});
$(this).removeClass('button-2').addClass('button-1');
});
DEMO
Offcourse you could do it like that...but isn't it easier to add an another variable that checks whether or not there has been a click? The code is much simpler and you can check later on whether or not the box has been enlarged.
This method also seperates style from computing, which is generally regarded as a good idea.
var large = false;
$('body').on('click', '.button', function(){
if (large) {
$('.box').addClass('clicked');
large = false;
} else {
$('.box').removeClass('clicked');
large = true;
}
});
additionally, you need a css class like so:
.clicked {
width: 110px;
}
and I removed that button-1 and button-2 classes, gave the div the class 'button' instead

Div Transitions - IE vs. others

I have a JSFiddle that displays a series of boxes. If one of the boxes is clicked, it expands to cover the other boxes, then displays text. When the now expanded box is clicked, it retracts to its original width and height. This javascript works flawlessly in Firefox, Chrome, and Safari. However, in Internet Explorer (v10), the box expands but fails to retract. Any Insight on why this may be?
JSFiddle: http://jsfiddle.net/QBdDE/
Javascript:
$('div').on('click', function (e) {
if ($(this).hasClass('clicked')) {
setTimeout(function (div) {
return function () { div.css('z-index', '') ; } ;
} ($(this)), 1000) ;
$('.overlay-text').hide();
}
else {
$(this).css('z-index', 400) ;
setTimeout(function(){$('.overlay-text').show();},1000);
}
$(this).toggleClass('clicked') ;
});
What's Going On
Problem:
pointer-events support was added in IE11. IE10 is ignoring this, and because your overlay is on top, the mouse is interacting with it. We can get around this though!
Solution:
We need to remove dependency on that CSS rule. To do this, we need to do two things:
1.) We need to make the hover color stays applied even if the :hover effect isn't happening. We can add another selector to our CSS so that the .clicked class will cause the colors.
2.) We need to address what happens when .overlay_text is clicked, and use that to trigger the shrinking animation.
Code
1.) Hover Effect
We need to add in another select to every place :hover is used:
Old CSS:
.first_box:hover {
...background color rule ...
}
New CSS:
.first_box:hover, .first_box.clicked {
...background color rule ...
}
Duplicate the above for all 4 box rules.
2.) .overlay-text Trigger
We need to cause a click on .overlay-text to trigger the shrinking.
Old JS:
$('div').on('click', function (e) {
if ($(this).hasClass('clicked')) {
setTimeout(function (div) {
return function () { div.css('z-index', '') ; } ;
} ($(this)), 1000) ;
$('.overlay-text').hide();
}
else {
$(this).css('z-index', 400) ;
setTimeout(function(){$('.overlay-text').show();},1000);
}
$(this).toggleClass('clicked') ;
});
New JS:
We have to add a new selector to the .on() code, then we have to add .clicked to both the selected square, add the overlaying section. Finally we have to remove .clicked from both. We can't use .toggleClass() because we are adding to $(this) and removing from all divs.
$('div, .overlay-text').on('click', function (e) {
if ($(this).hasClass('clicked')) {
setTimeout(function (div) {
return function () { div.css('z-index', '') ; } ;
} ($(this)), 1000) ;
$('.overlay-text').hide();
$('div').removeClass('clicked');
$('.overlay-text').removeClass('clicked');
}
else {
$(this).css('z-index', 400) ;
setTimeout(function(){$('.overlay-text').show();},1000);
$(this).addClass('clicked');
$('.overlay-text').addClass('clicked');
}
});
Summary
I've tested in IE10 and it works.
Working Example:
Extra
If I may say, the CSS structure you are using could be improved and your animations will look a lot better. Chrome and IE both flicker during the animation of the two left blocks.
This is because their width AND position is being animated. If you position them from right:0, only their width will animate and it'll look a lot smoother.
I've created a Fiddle for you to address the above. I used absolute positioning. The CSS ends up being shorter, but mainly the animation doesn't flicker. Take a look:
Working Example:
Extra 2
As per comments from OP, we are going to prevent users from double clicking. Since all animations take 1 second, we will disable clicking from triggering anything for 1 second after each click.
It's actually pretty simple to do. In the Extra 1 above, we cleaned up the JS, and it became this:
$('div, .overlay-text').on('click', function (e) {
if ($(this).hasClass('clicked')) {
$('.overlay-text').hide();
$('div').removeClass('clicked');
$('.overlay-text').removeClass('clicked');
}
else {
setTimeout(function(){$('.overlay-text').show();},1000);
$(this).addClass('clicked');
$('.overlay-text').addClass('clicked');
}
});
We just need to add a global variable that starts true. When once the click happens, set it to false immediately, and after 1 second, set it to true. Then we just check to see if it's true, and don't do anything at all if it's false:
var notdouble = 1;
$('div, .overlay-text').on('click', function (e) {
if (notdouble) {
if ($(this).hasClass('clicked')) {
$('.overlay-text').hide();
$('div').removeClass('clicked');
$('.overlay-text').removeClass('clicked');
}
else {
setTimeout(function(){$('.overlay-text').show();},1000);
$(this).addClass('clicked');
$('.overlay-text').addClass('clicked');
}
notdouble=0;
setTimeout(function(){notdouble=1;},1000);
}
});
Working Example:
Note, this builds from the new structure in the Fiddle version 13, so it won't work exactly with the fixed version of the original structure. The concept can be adapted though.
Not working in IE 9 as the div click event never fires. I think it's covered by the section with class="overlay-text". But I've got a workaround by handling the click event of the section and triggering the div click event
$('section').on('click', function (e) {
$('.overlay-text').hide();
$( "div" ).addClass('clicked') ;
$( "div" ).trigger( "click" );
});

How do I show or hide div, based on position of another div

I have the following jquery that slides a div horizontally:
$('.nextcol').click(function() {
$('.innerslide').animate({'left': '-=711px'}, 1000);
});
$('.prevcol').click(function() {
$('.innerslide').animate({'left': '+=711px'}, 1000);
});
What I want to happen is this... if the div.innerslide has a position that is left: 0px then I want to hide div.backarrow. If the position is not left: 0px, then it shows it.
Any help would be greatly appreciated.
EDIT (added HTML Markup)
<div class="backarrow prevcol">
<div id="mainleft" class="overflowhidden">
<div class="innerslide">
<div class="col">my content including next</div>
<div class="col">my content including next</div>
<div class="col">my content including next</div>
</div>
</div>
Try this:
if ($('.innerslide').css("left") == 0) {
$('div.backarrow').hide();
} else {
$('div.backarrow').show();
}
Fix for Double-Click Issue:
From what you described in your comment about the issue when the visitor double-clicks, it sounds like the double-click is causing two of the animation events to fire. To keep this from happening, you can either disable the click handler while the animation is running and re-enable it once it is finished, or you can try to write a new thread to continually check the element's position. One of these solutions is not a good idea - I'll let you figure out which one :) - but the other actually has a very simple solution that requires little change to your existing code (and may actually reduce your overhead by a teeny weeny amount):
$('.nextcol').on("click.next", function() {
$('.innerslide').animate({'left': '-=711px'}, 1000, showHideBack());
$(this).off("click.next");
});
$('.prevcol').on("click.prev", function() {
$('.innerslide').animate({'left': '+=711px'}, 1000, showHideForward());
$(this).off("click.prev");
});
Then add this this line to showHideBack() (and a complementary one to showHideForward() if you are using that):
$('.nextcol').on("click.next".....
I suggest that you write a function to set each click handler and another to remove each one. This will make your live very easy and the whole solution should reduce overhead by removing unnecessary click handlers while the animation is running.
Note: the animation method often calls its callback before the animation finishes. As such, you may wish to use a delay before calling the showHide... method(s).
Let me know if you have any questions. Good luck! :)
UPDATE:
Here is the updated version of the fiddle you gave me with all bugs ironed out. It looks like I misunderstood part of your goal in my original solution, but I straightened it out here. I have also included the updated jQuery, here:
var speed = 1000;
var back = $("div.backarrow");
var next = $(".nextcol");
var prev = $(".prevcol");
var inner = $(".innerslide");
function clickNext(index) {
next.off("click.next");
inner.animate({
'left': '-=711px'
}, speed, function() {
back.show(); //this line will only be hit if there is a previous column to show
next.delay(speed).on("click.next", function() {
clickNext();
});
});
}
function clickPrev() {
prev.off("click.prev");
inner.animate({
'left': '+=711px'
}, speed, function() {
if (inner.css("left") == "0px") {
back.delay(speed).hide();
prev.delay(speed).on("click.prev", function() {
clickPrev();
});
} else {
back.delay(speed).show();
prev.delay(speed).on("click.prev", function() {
clickPrev();
});
}
});
}
next.on("click.next", function() {
clickNext();
});
prev.on("click.prev", function() {
clickPrev();
});​
I was going to also include a condition to check if you were viewing the last column, but, as I don't know what your final implementation will be, I didn't know if it would be applicable. As always, let me know if you need help or clarification on any of this. :)
You could try the step option — a callback function that is fired at each step of the animation:
$('.prevcol').click(function() {
$('.innerslide').animate({ left: '+=711px' },
{
duration: 1000,
step: function(now, fx) {
if (now === 0 ) {
$('div.backarrow').hide();
} else {
$('div.backarrow').show();
}
}
});
});
More examples of usage in this article The jQuery animate() step callback function

Help me move from prototype to jquery

window.onload = function() {
$A($('draggables').getElementsByTagName('p')).each(
function(item) {
new Draggable(
item,
{
revert: true
}
);
}
);
Droppables.add(
'droparea0',
{
hoverclass: 'hoverActive',
onDrop: moveItem
}
);
// Set drop area by default non cleared.
$('droparea0').cleared = false;
}
function moveItem( draggable,droparea){
$(droparea).highlight({startcolor: '#999999', endcolor: '#f3f0ca' });
if (!droparea.cleared) {
droparea.innerHTML = '';
droparea.cleared = true;
}
draggable.parentNode.removeChild(draggable);
droparea.appendChild(draggable);
}
Hi, I'm moving from prototype to Jquery and right now I've being unsuccessfuly able to do the transition and finally need some help. can some pne please help me to translate the above prototype js code to jquery put some comments to it so I can follow? I will really appreciate. Yes, prototype is a bit hard work but until I get my head into jquery completely it will be hard to get that move out of my head.
As already mentioned, jQueryUI is your friend. Given the following HTML:
<div class='draggables'>
<p>Drag1</p>
<p>Drag2</p>
<p>Drag3</p>
</div>
<div id='droparea0'>Drop Zone</div>
You can use the following jQuery and jQueryUI to get something close to what you are doing.
$(document).ready(function() {
$('.draggables p').draggable();
$('#droparea0').droppable({
drop: function(event, ui) {
ui.draggable.detach(); // detach the dragged element from the DOM
$(this).css({'background-color': '#999999'}) // start colour for drop area
.animate({'background-color': '#f3f0ca'}) // animate to final colour
.empty() // clear the contents of the dropzone
.append(ui.draggable); // append the dragged element
ui.draggable.css({top: 0, left: 0}); // reset top/left since it was changed during dragging
}
});
});
Working jsFiddle here: http://jsfiddle.net/2F8YE/
first of all in jQuery you should use $(function(){...}) instead of window.onload (jquery starts here ;D)
just look at the jQueryUI sample http://jqueryui.com/demos/droppable/

Categories