append content, scrollTo it, hide old content - javascript

I would like to implement the following animation effect;
http://jsfiddle.net/YKmu2/37/
This is the core code;
$("#c1").on( "click", function(event) {
event.preventDefault();
new_site = 'content1';
if(site == new_site) { $('[name="' + new_site + '"]').show(); }
else {
$('[name="' + new_site + '"]').insertAfter('[name="' + site + '"]');
$('[name="' + new_site + '"]').show();
$.scrollTo('[name="' + new_site + '"]', 1000);
}
setTimeout(function() {
$('[name="' + site + '"]').hide();
$.scrollTo('[name="' + new_site + '"]');
site = new_site;
}, 1005);
});
When you click on one of the links, the content associated with this link is appended to the current content and a scroll animation is executed using the scrollTo plugin.
After the scroll animation is finished, the old content is removed/hidden.
That works all fine as long as the user is clicking on a link and is waiting until everything is finished.
However when you start to click on multiple links while the animation is not finished a lot of weird things are happening.
Does someone have an idea how to fix this / make it reliable and error-proven?
Or does somebody know a jquery plugin that already implements such an effect?
Thank you!

This is a relatively common problem of animation queueing and only triggering the action if there are no animations already happening, however in this case you need to be checking if the page is scrolling or not, which is a little more abstract in a sense.
UPDATED BELOW TO FIX SCROLLING ISSUE:
I've updated your fiddle here: http://jsfiddle.net/andyface/77fr6/ to demonstrate checking to see if the page has scrolled to the top before triggering a change in content.
Current working fiddle: http://jsfiddle.net/andyface/77fr6/1/
I've also taken some liberties with making your code a little easier to manage, which I hope is actually helpful. Essentially I've made things work based on classes instead of individual IDs, so you only have to maintain one bit of code and if you need to expand it, you don't have to add more blocks of code, just a few extra lines to get the ID, however this could be made to do it all cleverly enough to not even need that.
The main changes I've made are as follows:
The links now have a class of link and content a class of content.
The IDs of the links are then used to extrapolate the content they are meant to be triggering.
I've also removed the timeout and used the scrollTo() callback feature as this will be more reliable.
Finally I added a check to see if the page had scrolled to the top before allowing the action to trigger
Hope this helps, but let me know if you need anything more explaining
UPDATE:
Seeing I missed the fact that by limiting the action by scroll position this would stop them working if the user scrolled down, I've changed how the action blocking works.
$(document).ready(function() {
// HIDE CONTENT
$('.content').hide();
var site = '',
scrolling = false;
$(".link").on( "click", function(event) {
// THIS IS JUST USED FOR TESTING TO SEE WHAT THE scrollTop() POSTION IS WHEN CONTENT IS SCROLLED - REMOVE console.log() WHEN IN PRODUCTION
console.log($(window).scrollTop());
event.preventDefault();
// ONLY RUN THE SCRIPT IF THE PAGE HAS SCROLLED TO IT'S FINAL POSITION
if( ! scrolling)
{
var new_site = null;
switch($(this).attr('id')) {
case 'c1':
new_site = 'content1';
break;
case 'c2':
new_site = 'content2';
break;
case 'c3':
new_site = 'content3';
break;
}
if(site != new_site) {
// KEEP A TRACK OF IF THE PAGE IS SCROLLING OR NOT
scrolling = true;
$('#' + new_site).insertAfter('#' + site);
$('#' + new_site).show();
$.scrollTo('#' + new_site, 1000, function() {
// CALLBACK TRIGGERED WHEN SCROLLING HAS FINISHED
$('#' + site).hide();
$.scrollTo('#' + new_site);
site = new_site;
// TELL THE PAGE IT'S OK TO DO STUFF COS WE'RE NOT SCROLLING IT ANYMORE
scrolling = false;
});
}
}
});
});
http://jsfiddle.net/andyface/77fr6/1/

Related

JS variables getting "stuck" on new page with turbolinks

First, let me apologize for the complexity of this question, I can't really break it down more simply as the complexity might be part of the cause. But this logic makes the active link for the page light up and flicker.
Using turbolinks, on the first page everything works fine, but when we switch to another page, things start going haywire. The link that previously had the class of active from the previous page will still be saying its passed in argument of active is true, even though it no longer has a class of active.
With turbolinks disabled, everything works as expected.
function flicker(elem, active) {
var delay = Math.random() * (2000) + 130;
console.log($(elem).data('name') + ": " + active + ': ' + $(elem).hasClass('active'));
if (active == true) {
// We need specific timers for each link so each can operate with its own
// separate "thread" and be toggled independently of the others.
// Using window['foo'] to handle dynamic variable names.
window['timer-' + $(elem).data('name')] = setTimeout(function() {
window.setTimeout(function() {
$(elem).addClass('powered');
}, 20) // Controls length of flicker.
flicker(elem, true);
$(elem).removeClass('powered');
}, delay);
} else {
clearTimeout(window['timer-' + $(elem).data('name')]);
}
};
$(document).on('turbolinks:load', function() {
// Initiate the current, active link.
$('.navbar a.active').each(function() {
window['timer-' + $(this).data('name')]; // Instantiating dynamic variable.
flicker($(this), $(this).hasClass('active'));
});
});
Stay with me. So where I have the console log on line 3, on the first page, that will output as expected. For example:
nav-1: true: true
will repeatedly get output with each flicker, indicating that the first link active argument from line 1 is true and that it has a class of true.
But if I click on the second link to go to the next page, then console will repeatedly log:
nav-1: true: true
nav-2: true: true
Even though, in this case, nav-1 does not have a class of active.
Turns out, I was able to fix this just by resetting all flickering effects on page load.
$('.navbar a').each(function() {
flicker($(this), false);
});
Works with turbolinks now.

Hashchange history breaks iScroll

I have created the following application using iScroll: http://preview.na-software.co.uk/Demo/FutureLearning4/#/section-0
As the user flicks left and right or clicks the arrows in the bottom corners, the application moves the content sections it updates the history by changing the hash so that the user can move back and forth to other sections and bookmark them etc.
However! If you access a hash like: http://preview.na-software.co.uk/Demo/FutureLearning4/#/section-2 and then navigate a few sections and then use the back buttons two issues happen:
1.) It scrolls to the first screen (even though currentSection is correct, and iScroll has been told the correct section).
2.) If you click the back or forward button multiple times, you stop the animation and cause it to become confused and stick in between two sections.
Looking into the code, and seeing that the correct indexes and elements are being passed to iScroll on hashchange, and console logging out the offsets, I've discovered the issue is cause because the offsets are incorrectly set... however just doing refresh() won't fix the issue, as it will then reset the position.
Can anyone see where the problem is or see a way to fix this?
I should note that this bug ONLY happens if you come into the application on a URL that isn't section 0 and then scroll around the application. This is because the offsets will be created correctly by your interactions. But if you come into a URL like section 3, then the offsets will be incorrect and so the hashchanges don't work correctly, if that makes sense.
The hashchange method looks like:
// handle hashchange events
$(window).hashchange( function(){
// read the hash to find out what the new section number is
var nums = location.href.match(/(section)-\d+/g).map(
function(x){ return +x.replace(/\D/g,"") }
);
// set currentSection
currentSection = nums[0];
// if the hashchange was called by user scrolling
if(hashCalledByScroll){
// no need to anything as they have already updated hash and scrolled
hashCalledByScroll = false;
} else {
// find the section to scrollTo
sectionToScrollTo = $('#horizontal > .sections > .section').eq(currentSection).attr('id');
// tell iscroll to scroll to the section
horizontal.scrollToElement( '#' + sectionToScrollTo, null, null, true );
}
// hide the menu on hashchange
hideMenu();
});
Testing your site, I noticed the following: Whenever I access the site via section-3 and then enter the url for section-2, the navigation would instead send me to section-0.
I believe this is the same behaviour as you are experiencing in 1).
So I investigated and came to the following analysis:
In the function horizontal.scrollToElement( '#' + sectionToScrollTo, null, null, true )
iScroll retrieves the utils.offset(el) [iScroll.js#772] for the given el-ement. This offset tells it, where the element to scroll to is.
iScroll goes through the element and all of its offsetParents to add up their offsets. This is where things are breaking: <div class="sections"> has a negative offset to its parent, which imho it should not have.
This, in turn, messes up the scrollTo-coordinates.
To see what I am talking about: document.querySelector('.sections').offsetLeft
This has all just been analysis. My approach to fix this would be to avoid scrollToElement() and instead use scrollTo():
...
} else {
// find the section to scrollTo
sectionToScrollTo = $('#horizontal > .sections > .section').eq(currentSection).attr('id');
// tell iscroll to scroll to the section
var posLeft = -$('#' + sectionToScrollTo)[0].offsetLeft;
var posTop = -$('#' + sectionToScrollTo)[0].offsetTop;
horizontal.scrollTo(posLeft, posTop, 1000);
}
// hide the menu on hashchange
hideMenu();
});
Thus, just calculate the location of the section you want to go to yourself.
About 2) I am not sure if there is much one can do about it. Jumping around quickly breaks a lot of carousels. Maybe a delayed callback to scrollEnd, verifying the validity of the current state.
Another thing I noticed is that you can accidentally stop the transition. Try to click, hold and release the cursor midway a transition - you need to be quick.
Hope this helps.
Found not best solution and it doesn't solve main problem, but it works.
$(window).hashchange(function () {
if (hashCalledByScroll) {
hashCalledByScroll = false;
} else {
var hpage = window.location.hash;
var hpage = hpage.replace('#/section-', ''); //get number of target page
var cpage = currentSection; //number of current page
var count = parseInt(hpage) - parseInt(cpage); //difference
while (count > 0) { //if difference positive: go forward count-times
horizontal.next();
count--;
}
while (count < 0) { //if difference negative: go backward count-times
horizontal.prev();
count++;
}
}
hideMenu();
});
FIDDLE

Why overflowing menu is working bad when there are 0 items in visible original ul?

Here is how problem looks like:
While it should look like:
Here is function used to create poping up overflowing menu:
function updateMenu(){
var lastItemIndex = Number.POSITIVE_INFINITY;
var lastItemText = "";
var maxWidth = 0;
var overflowCount=0;
var navHeight = $('.nav').height();
$('.nav li').each(function(i,e){
console.log($(e).position().top);
if($(e).position().top>=navHeight){
if(i<lastItemIndex) lastItemIndex=i-1;
if($(e).width()>maxWidth) maxWidth = $(e).width();
overflowCount++;
}
});
maxWidth = Math.max(maxWidth,$('.nav li:eq('+(lastItemIndex)+')').width());
var moreHeight = (overflowCount+2)*navHeight;
$('#moreMenu').remove();
if(overflowCount>0){
$('<ul id="moreMenu"/>').appendTo('body').width(maxWidth+16).height(navHeight);
$('#moreMenu').offset($('.nav li:eq('+(lastItemIndex)+')').offset());
$('#moreMenu').append('<li>More...</li>');
$('.nav li:gt('+(lastItemIndex-1)+')').each(function(i,e){
$('#moreMenu').append('<li>'+$(e).html()+'</li>');
});
$('#moreMenu').hover(
function(){$(this).height(moreHeight);},
function(){$(this).height(navHeight);});
}
}
And here is life jsfiddle demo of this bug (I use chrome for testing).
I wonder what is wrong with my updateMenu function, why when all menu items shall be shown in a pop up menu none is actually shown (and no html pushed into pop up menu object)?
Update fire fox also shows no items for me:
One problem is that the selector you form with "lastItemIndex", when that's zero, is invalid:
$('.nav li:gt(' + (lastItemIndex - 1) + ')') ...
When it's zero, that'll look like .nav li:gt(-1) and that's not valid (or at least it doesn't work). If you change it to:
$('.nav li:gt(' + Math.max(0, lastItemIndex - 1) + ')')
then the <li> elements are transferred to the "More" box.
As a separate note, it's probably not the best idea to do the updates in a "resize" handler directly. Browsers fire that event very rapidly, and doing all that DOM work is going to result in sluggish behavior. Instead, what you can do is have the "resize" handler start a timer for maybe 50 or 100 milliseconds, canceling any previous timer when it does so. When the user slows down the resize enough, the timer event will fire and you can do the DOM work in there.
Here is the jsfiddle updated with the selector fix (no change to the event handling).

disable address bar and back and forward button of browser

how can I disable or hide address bar and back and forward buttons in ie and firefox
i tried lots of links and solutions but non of them worked
for example for disabling back button:
<script type = "text/javascript" >
function changeHashOnLoad() {
window.location.href += "#";
setTimeout("changeHashAgain()", "50");
}
function changeHashAgain() {
window.location.href += "1";
}
var storedHash = window.location.hash;
window.setInterval(function () {
if (window.location.hash != storedHash) {
window.location.hash = storedHash;
}
}, 50);
but it seems that it goes to previous page then it returns
and i trid :
window.scrollTo(0, 0); // reset in case prev not scrolled
var nPageH = $(document).height();
var nViewH = window.outerHeight;
if (nViewH > nPageH) {
nViewH -= 250;
$('BODY').css('height', nViewH + 'px');
}
window.scrollTo(0, 1);
}
for disabling menu bar but it didnt work
what can i do
Instead of disabling the back button, try to make your page that supports users to going back. It will increase the usability of your application.
Even you can impliment it for the ajax activities also.
Don't think you can disable buttons on browser. I mean, otherwise, we'd seen it on spyware infected sites...
In terms of hiding them, I've seen banks use a full screen popup without those buttons (but hardware button on mouse or hitting backspace still works).
Not tested but you can bind to the window's hashchange event.
For example, in jQuery it very easy to do:
$(window).bind('hashchange', function(e)
{
e.stopImmediatePropagation();
e.stopPropagation();
e.preventDefault();
return false; // stop event
});
The result is that the back button not changes the page when an anchor is in the url. But i agree with the first answer also (it is not a real answer i think, it is an advice).
You can also override the last entry in window.history, so user can't go back.

Is it possible to create a div element with jquery onclick of a link

I have a navigation list. The effect I am looking for is when the user clicks on a link, an accordion style div is built and displayed by jQuery. Then if the user clicks the same screen, the is deleted from the screen.
Here's some cod that will create an DIV if it is not already there, load it with some HTML from the URL contained in a link's HREF attribute, then turn it into an accordion. If the DIV already exists, it removes it.
$('.navLink').click( function() {
var accordion_id = 'accordion_' + this.id;
var accordion = $('#' + accordion_id);
if (accordion.length > 0) {
accodion.remove();
}
else {
$('<div id="' + accordion_id + '"></div>')
.appendTo('#someDiv')
.load( $(this).attr('href') )
.accordion();
}
return false; // cancel default action of link
});
Yes, I'm quite sure that this is possible. It looks like there may be plugins and third-party tools out there that can help you with this task. This one looks promising: http://jqueryui.com/demos/accordion/

Categories