Div Transitions - IE vs. others - javascript

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

Related

Toggling appendTo() using jQuery Waypoints

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.

jQuery using variables so one function performs multiple tasks

I'm a real noob and every time I've tried to implement any of these things it just stops working altogether...
I have 4 boxes on my page that should each expand and contract in the direction the little blue tabs are facing.
The thing I'd like to know, which I tried to implement but just have no idea about, was if there was a way I could input some variables so the same function could be performed by the other boxes but in different directions...
.exp1 needs to be replaced so a variable with value 1-4 goes in place of the number
eg/ .exp(variable value from 1 to 4)
Depending on which value .exp takes, the other classes variable numbers need to change further down in the code
eg/ .box3 would need to be .box(variable value from 1 to 4)
.miniBox3 would be .miniBox(variable value from 1 to 4)
and lastly .con1 would be .con(variable value from 1 to 4)
The values and properties in animate would also need to change
eg/ instead of being .animate({bottom... it could be .animate({left... with a new va;lue like 30px instead of 10px
In the expandFunction() the rules are:
if it's .exp1... then .box3 closes replaced by .miniBox3, .box1 expands and .exp1 is switched to .con1
if it's .exp2... then .box1 closes replaced by .miniBox1, .box2 expands and .exp2 is switched to .con2
if it's .exp3... then .box4 closes replaced by .miniBox4, .box3 expands and .exp3 is switched to .con3
if it's .exp4... then .box2 closes replaced by .miniBox2, .box4 expands and .exp4 is switched to .con4
In the contractFunction() the .box, .exp and .con numbers are all the same.
Here's the code:
$(document).ready(function () {
//function declared expand
$('.exp1').click(function(){
expandFunction();
});
});
//expand function properties
function expandFunction(){
if($(".box3").is(":visible"))
{
$('.box3').animate({left:'100%', top:'70px', width:'0px', height:'0px'},
"slow", function(){
$(this).switchClass("box3", "miniBox3", "slow");
$('.exp3').hide();$('.miniBox3').show("fast");//hide blue bar, show box in sidebar
$('.box1').animate({bottom:'10px'}, "slow", function(){ //opens box right
$('.exp1').unbind('click').removeClass('exp1').addClass('con1')
.click(function(){
contractFunction();
});
});
});
}
else
{
$('.box1').animate({bottom:'10px'}, "slow", function(){ //opens box right
$('.exp1').unbind('click').removeClass('exp1').addClass('con1')
.click(function(){
contractFunction();
});
});
}
}
//};
function contractFunction(){
$('.box1').animate({bottom:'46.5%'}, "slow", function(){
$('.box1 div').unbind('click').removeClass('con1').addClass('exp1').click(function(){
expandFunction();
});
});
}
Here's a fiddle
(My first problem was that the 1st box (top left) expands once, contracts once and then doesn't do anymore. It should continually expand and contract to infinity. SOLVED WITH IF ELSE STATEMENT)
Thank you very much in advance for any pointers and help you can give me.
i've updated your fiddle with just a few things.
i get rid of the div.miniBox, i thought they weren't necessary for achiving your needs.
i rewrited the css classes you used so i can perform the animations just adding and removing classNames and each box now has a unique id.
i added to the trigger divs a data- attribute (thanks html5) to store the id of the related box to hide/show, so i can retrive that value with ease with the jQuery.data() function.
here a sample of html
<div id="a1" class="box">
<div class="exp" data-related="a3"></div>
1
</div>
and here the code i used
$(function () {
$('.exp').click(function () {
var exp = $(this); //this is the clicked trigger
var parent = exp.parent(); //this is the parent box
var related = $('#' + exp.data('related')); //this is the related box
if (exp.is('.con')) { // check if the box is expanded
// i can do the same with parent.is('.maxi')
//expanded
parent.removeClass('maxi' /* shrink the box */,
'slow',
function () {
exp.removeClass('con'); //now i know the parent box is no more expanded
related.removeClass('mini', 'slow'); //restore the related box
});
} else {
//collapsed
related.addClass('mini' /* minimize the related box */,
'slow',
function () {
exp.addClass('con'); //this to know if parent is expanded
parent.addClass('maxi', 'slow'); //expand the parent box
});
}
});
});
you can check the full code in this fiddle
EDIT: so, to answer your question (how to do this with variables) i say you can use the state of your elements as variables themself.

One div visible and other hidden

I am no expert on programing with jQuery but I have a little bit knowledge about the language, the thing is that I want one div to be visible and the other one hidden, as soon as you click the other div it should slide down and the first one should be hidden.
The bug is that if you press one div atm is messes up.
$(document).ready(function () {
$('#link').click(function () {
if ($('.todo-post').is(":hidden")) {
$('#date-visible').slideUp("slow");
$('#date-hidden').slideDown("slow");
$('#tick-hidden').slideDown("slow");
$('.todo-post').slideDown("slow");
} else {
$('.todo-post').slideUp("slow");
$('#date-hidden').slideUp("slow");
$('#tick-hidden').slideUp("slow");
$('#date-visible').slideDown("slow");
}
});
});
That's the code I'm using at the moment, It works for one div there is text everywhere if I add another div, it gets messy. I believe that the code can be re-made so it works properly but sadly I do not know how and I have been searching the web for a while now.
LINK TO MY WEBSITE
You can do this with less code
$(document).ready(function () {
$('#link').on('click', function () {
$('#date-visible, #date-hidden, #tick-hidden, .todo-post').slideToggle("slow");
});
});
Basically what is happening is that our elements have position absolute so if you add this css it will work:
div.todo-avatar-date-hidden {
position: static;
}
div.todo-tick {
position: static;
}
div.todo-post {
position: static;
}
Also you need to put it relatively near the bottom of your css or it will be overridden by the previous code so I advise to go to each element in the css that I have shown and removing the line that makes the element absolute
Edit
$('#link').click(function () {
if($('#date-visible').is(':hidden')) {
if(!($('#date-visible-2').is(':hidden'))) {
$('#date-visible-2, #date-hidden-2, #tick-hidden-2, .todo-post-2').slideToggle("slow");
}
}
$('#date-visible, #date-hidden, #tick-hidden, .todo-post').slideToggle("slow");
});
$('#link-2').on('click', function () {
if($('#date-visible-2').is(':hidden')) {
if(!($('#date-visible').is(':hidden'))) {
$('#date-visible, #date-hidden, #tick-hidden, .todo-post').slideToggle("slow");
}
}
$('#date-visible-2, #date-hidden-2, #tick-hidden-2, .todo-post-2').slideToggle("slow");
});
IDs should be unique, no two elements can have the same id in a same page. You are using same id like "date-visible" in your HTML page. Change them and then code accordingly.

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

jquery: bookmarking Paragraph function, is checking for class on every single Paragraph hover efficient? is there a better way?

Im creating a feature using jquery on my site that allows people to place a bookmark on a paragraph when they're viewing an article, so if they want to come back later, they'll know where they stopped.
When the user clicks on "PLACE A BOOKMARK", it adds the class .placing to that link. When the user hovers over a paragraph, the script checks to see if the link has the .placing class, if it does, it fades in a div on hover that says "bookmark this paragraph", on hover off it fades that div back out.
Right now my code is placed within a $('P').click(function... that is within the hover function, it's caused links on the page to not fire their hover animation. So links inside paragraphs wont animate on hover because when i hover over a paragraph it's checking for the .placing class and doing the bookmark hover function.
How would i get the hover animation to work again on links that exist within paragraphs?
and is this the best way to do it? to check for the .placing class every single time i hover over a paragraph? There's gotta be a better way to do this, like a switch that only allows the bookmark paragraph hovering function to work when the switch is on, and not checking with every single paragraph hover.
$('#placeBookmark').click(function () {
$(this).toggleClass('placing')
})
$('p').hoverIntent(function () {
// HOVER ON
if ($('#placeBookmark').hasClass('placing')) {
$(this).append('<span id="bookmarkThis">BOOKMARK THIS</span>')
$(this).find('#bookmarkThis').stop().animate({opacity: 1.0}, 400)
$('p').click(function (e) {
var newbm = $('#bookmark').offset().top
var offset = $(this).offset();
var top = offset.top
var phrases = ["SUCCESS", "SWEEEEEEET MANNN", "AWESOMELY AWESOME", "NEAT-OH!", "EXCELLENT, BOOKMARK PLACED", "BOOKMARK SAVED", "YOU DID IT KID!", "WOOOHOOO", "YOU ARE WONDERFUL"];
if ($('#placeBookmark').hasClass('placing')) {
//$('#placeBookmark').trigger('click')
$('#placeBookmark').removeClass('placing')
if (top == newbm) {
// bookmarkColorDelay = 2000
$('#bookmarkThis').html('<span>ITS ALREADY BOOKMARKED</span> SILLY PERSON').delay(2000).fadeOut(400, function () {
$(this).remove()//html('BOOKMARK THIS')
})
} else {
// bookmarkColorDelay = 1000
var selectedPhrase = phrases[Math.floor(Math.random() * phrases.length)];
$('#bookmarkThis').html(selectedPhrase).delay(1000).fadeOut(400, function () {
$(this).remove()//html('BOOKMARK THIS')
})
$('#bookmark').fadeOut(300, function () {
$(this).css({
left: offset.left - 30,
top: top
}).fadeIn(300);
})
}
$(this).stop().delay(bookmarkColorDelay).animate({
color: "#333",
backgroundColor: "#fff"
}, 200)
}
});
}
}, function () {
// HOVER OFF
if ($('#placeBookmark').hasClass('placing')) {
$(this).find('#bookmarkThis').stop().fadeOut(400, function(){
$(this).remove()
})
}
});
Use a class selector! :-)
$('.placing').live('click', function(event) {
// Whatever you want to do
});
$('.placing').live('hover', function(event) {
// Whatever you want to do
});
It will work even though the class .placing is added later.
I would take the code by Andrea and optimise it slightly as
$('p.placing').live('click', function(event) {
// Whatever you want to do
});
$('p.placing').live('hover', function(event) {
// Whatever you want to do
});

Categories