After 5 seconds when a user scrolls to a specific section of a page, I'm collapsing a module (with Bootstrap). The problem is when you manually click the button to collapse again, this function still fires and I don't want that behavior.
Basically I want this function to fire only once after the user scrolls to that specific element on the page. How would I go about doing that?
$(window).on('scroll', function(){
var scroll = $(window).scrollTop();
var objectSelect = $('#whatsNextGhost');
var objectPosition = objectSelect.offset().top;
var collapseAccordion = function() {
if (scroll > objectPosition) {
$('#whatsNextSectionContent').collapse('hide');
}
};
setTimeout(collapseAccordion, 5000);
});
I've created a little fiddle for you:
https://jsfiddle.net/1j790jxb/
On scroll I simply log something to the console:
$(window).on('scroll',function(){
console.log('scroll attached');
});
When I hit a button (with ID button) I detach the scroll:
$('#button').on('click', function(event){
$(window).off('scroll');
});
As you can see in the Fiddle: after you click the button, nothing appears in the console anymore.
You might also want to take a look at: namespacing your (scroll) event and requestAnimationFrame:
https://api.jquery.com/event.namespace/
https://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
You have to assign window to a variable so that it disengages the listener from the same instance of "window" you applied it to since your off handler is in a different context if it outside of the initializing closure.
ie:
$window = $('window');
$window.on('scroll', function(){
var scroll = $window.scrollTop();
var objectSelect = $('#whatsNextGhost');
var objectPosition = objectSelect.offset().top;
var collapseAccordion = function() {
if (scroll > objectPosition) {
$('#whatsNextSectionContent').collapse('hide');
}
};
setTimeout(collapseAccordion, 5000);
});
$window.off('scroll') // this goes inside whatever method you are using to remove the listener.
Edit: grammar
Related
I am trying to figure out why my function stopped working after I changed html code.
I have a div:
<div class="float">
<div class="box" data-speed="3" data-direction="X"><h1>Hola</h1></div>
<div class="box" data-speed="2" data-direction="X"><h1>chau</h1></div>
</div>
And the jquery code :
$(function() {
$('.box').moveIt();
});
//move elements in different speeds
$.fn.moveIt = function () {
var win = $(window);
var it = $(this);
var instances = [];
$(this).each(function (){
instances.push(new moveItItem($(this)));
});
$('.parallax').on('scroll', function() {
instances.forEach(function(inst){
var wrap = inst.el.parents('.float');
var scrol = win.scrollTop()-wrap.offset().top;
inst.update(scrol);
});
});
}
var moveItItem = function(el){
this.el = $(el);
this.speed = parseInt(this.el.attr('data-scroll-speed'));
this.direction = this.el.attr('data-direction');
};
moveItItem.prototype.update = function(scrollTop){
var pos = scrollTop / this.speed;
this.el.css('transform', 'translate'+this.direction+'(' + -pos + 'px)');
};
ok until here everything working, when I scroll the elements .box translate accordingly.
But now I am trying to modify the html in class .float after an ajax call
//after ajax
$.ajax({
url: 'do_content.php'
}).done(function(result) {
//result = <div class="box" data-speed="3" data-direction="X"><h1>Como estas?</h1></div>
$('.float').html(result);
});
After when I fired the scroll again the function appear to look broken and I got this message:
Uncaught TypeError: Cannot read property 'top' of undefined
at http://localhost/ophelia/public/js/control.js?v=1487219951:197:45
at Array.forEach (native)
at HTMLDivElement.<anonymous> (http://localhost/ophelia/public/js/control.js?v=1487219951:195:13)
at HTMLDivElement.dispatch (http://localhost/ophelia/public/utilities/jquery/jquery-3.1.1.min.js:3:10315)
at HTMLDivElement.q.handle (http://localhost/ophelia/public/utilities/jquery/jquery-3.1.1.min.js:3:8342)
I understand that this message appear only if I change the elements with class .box (I tried to change the only the h1 and it doesnt break but I want to change everything to change also the speeds)
How can I re-fire the function?
I tried to call it again with $('.box').moveIt(); but still getting the same error
I know is a long question but didnt find another way to explain my problem
This happens because the html element tied to the listener has been replaced..
Like in this fiddle here.. The alert works but after the html is changed, it doesn't. This is because the old element has been replaced by the new element.
You can use the on function in jQuery to get past this like in this fiddle
As already pointed (but maybe not so clear), the problem is that you attach an event handler using elements existing in the page in a certain moment of time (I think to the instances var). Then you substitute them, but your handler is already set on scroll for element with class .parallax and already registered using that instance of instances and so on.
One way is to rewrite your code using delegate methods.
From http://api.jquery.com/on/
Event handlers are bound only to the currently selected elements; they
must exist at the time your code makes the call to .on().
Delegated events have the advantage that they can process events from
descendant elements that are added to the document at a later time
An event-delegation approach attaches an event handler to only one
element, the tbody, and the event only needs to bubble up one level
(from the clicked tr to tbody):
$( "#dataTable tbody" ).on( "click", "tr", function() {
console.log( $( this ).text() );
});
But It may be complex as you should deeply restructure your code.
Otherwise you could rewrite your function as follows (sorry I can't make fiddles)
$(function() {
$('.parallax').moveIt();
});
//move elements in different speeds
$.fn.moveIt = function () {
var win = $(window);
var it = $(this);
//REMOVED
//var instances = [];
// $(this).each(function (){
// instances.push(new moveItItem($(this)));
// });
$(this).on('scroll', function() {
$('.box').each(function(){
var inst=new moveItItem($(this));
var wrap = inst.el.parents('.float');
var scrol = win.scrollTop()-wrap.offset().top;
inst.update(scrol);
});
});
}
...... and so on
you could bind event on div.float and go through element.children to move every .box
I have a 42" touchdisplay showing a webpage with IE9.
There are a lot of anchors on it. Sometimes a user does not just shortly taps a link but stays on it and nothing happens until he stops touching.
As a first solution I showed the user a message that he should take his finger of the touchpad.
var timeoutLongTouch;
$(".long-touch").on('mousedown.LongTouch', function () {
timeoutLongTouch = setTimeout(function () {
$("#warning").show();
}, 1000);
})
.on('mouseup.LongTouch', function () {
clearTimeout(timeoutLongTouch);
$("#warning").hide();
});
This works fine. But now I'd prefer to change the longtouch event to an immediatly fired click.
I tried several ways - f.e.
$(".long-touch").on('mousedown.LongTouch', function () {
var item = $(this);
setTimeout( function () {
console.log(item);
$(item).click();
}, 300);
});
But whatever I tried click is not fired until user takes his finger of.
Is there a way to convert the longtouch into a immiediate click when user touches a link, not depending of the duration of the topuch?
Could you try to use the following?
$('.long-touch').on('touchstart click', function(event){
event.stopPropagation();
event.preventDefault();
//do your stuff
});
For touch you need to use the touchstart, and we also listen to the click for normal desktop users.
I found a solution.
The idea of #Dirk-Jan together with not triggering a click, but simply refer to href location does it.
$('.long-touch').on('mousedown.LongTouch touchstart click', function(event){
event.stopPropagation();
event.preventDefault();
var item = $(this);
setTimeout( function () {
var dest = item.attr('href');
window.location.href = dest;
}, 300);
});
I have created a nice little javascript menu, which purposely doesn't use data tags as I was having issued with html purifier conflicts (another, long story).
Anyway, after alot of tinkering, the functionality and styling works exactly as I wanted, but with one exception - when I click on each menu item, it opens the content at different points on the screen, seemingly depending on the amount of content. I want it to always open at the top, so that the menu is always visible, along with the top of the content, and you can then scroll down as you wish.
I've been trying to resolve this for a while, so would appreciate any assistance, or amending of the attached fiddle.
Thanks in advance
Paul
https://jsfiddle.net/awcguxs5/
$(document).ready(function () {
var lastItem = null;
$('#listingmenu').on('click', 'a', function () {
newItem = this.getAttribute('href').substring(1);
if (newItem != lastItem) {
$('.current').not($(this).closest('li').addClass('current')).removeClass('current');
// fade out all open subcontents
$('.pbox:visible').hide(600);
// fade in new selected subcontent
$('#' + newItem).show(600);
lastItem = newItem;
}
}).find('a:first').click();
});
The problem is the references are still going to the corresponding ID locations. I've added one line of jquery that will scroll the page back to the top after the click. here is the line added:
$("html, body").animate({ scrollTop: 0 }, 1);
Here is your jsfiddle with this line:
https://jsfiddle.net/awcguxs5/2/
Let me know if this is what you were looking for! :)
$(document).ready(function () {
var lastItem = null;
$('#listingmenu').on('click', 'a', function () {
newItem = this.getAttribute('href').substring(1);
if (newItem != lastItem) {
$('.current').not($(this).closest('li').addClass('current')).removeClass('current');
// fade out all open subcontents
$('.pbox').hide( 600);
// fade in new selected subcontent
setTimeout( function(){$('#' + newItem).show(600)} , 600 );
lastItem = newItem;
}
}).find('a:first').click();
});
The settimeout fixes the issue and gives -i think- a nicer effect than firing the hide and the show simultaneously.
This happens precisely because you did not want to use "data tags".
Add this to onclick block:
$('#listingmenu').on('click', 'a', function (e) {
e.preventDefault();
.... //rest of your code.
e.preventDefault() will stop the default action of a-href, which in your case uses #div1 anchors to jump to that div.
Your updated fiddle here: https://jsfiddle.net/awcguxs5/3/
I can't seem to find the answer.
I have a mouseleave event, in which I want to check, when the event fired, whether the mouse is currently inside the window or not (if not, it can be the tab bar of the browser, back button, etc).
var cursorInPage = false;
$(window).on('mouseout', function() {
cursorInPage = false;
});
$(window).on('mouseover', function() {
cursorInPage = true;
});
$('#some_element').on("mouseleave",function(){
if(cursorInPage === true){
//Code here runs despite mouse not being inside window
}
});
Can I bind to a window mouseleave event? If you leave the outside scope of the document/window, does such an event fire? The above code has a problem since i believe the mouseleave of the element fires before the window
I'm not really sure what you're asking us to put for "what to write here?", but you can simply set a boolean:
var cursorInPage = false;
$(window).on('mouseout', function() {
cursorInPage = false;
});
$(window).on('mouseover', function() {
cursorInPage = true;
});
Then use that boolean to proceed:
if (cursorInPage === true) {
alert('Woo, the cursor is inside the page!');
}
Here's an example JSFiddle which changes the body background colour when the cursor enters or leaves the window area, better displayed when looking at the full-screen result.
just tested this hope it helps. heres the jsFiddle for it.
$(document,window,'html').mouseleave(function(){alert('bye')}).mouseenter(function(){alert('welcome back!')})
You can try :
$('body').mouseout(function() {
alert('Bazzinga...');
});
or
$(window).mouseleave(function() {
alert('Bazzinga...');
});
When mouse is inside element
$('#outer').mouseover(function() {
$('#log').append('<div>Handler for .mouseover() called.</div>');
});
When mouseleave element
$('#outer').mouseleave(function() {
$('#log').append('<div>Handler for .mouseleave() called.</div>');
});
I have a button on a page which I have programmed to scroll to the next div in a large container. However, I also want the same button to scroll the browser window over a certain amount of pixels whenever I scroll to the left or the right of the page.
Here is what I have tried:
This scrolls the container over a div. Then I tried adding a function that said scroll window from "getOffset"
var colwidth = $('#container').width(),
contwidth = $('#contents').width(),
getOffset = function() {
return parseInt($('#container').css('margin-left'));
};
$(".left").click(function(){
if (getOffset() === 0) return false;
function scrollWindow(scrollValue) {
window.scrollBy(getOffset, 0);
}
$("#contents").animate({left: '+=' + colwidth},500);
$("#container").animate({'margin-left': '-=' + colwidth},500);
});
$(".right").click(function(){
if (getOffset() === contwidth - colwidth) return false;
$("#contents").animate({left: '-=' + colwidth},500);
$("#container").animate({'margin-left': '+=' + colwidth},500);
function scrollWindow(scrollValue) {
window.scrollBy(getOffset, 0);
}
});
</script>
Logically to me it seems like it should work, but my page ignores the function to scroll the window over. I would appreciate any help. Where is my logic wrong?
Thanks
To attach event handlers to dynamic elements, use the jQuery .on() method if you're using jQuery 1.7+. If you are using older versions, choose among .bind(), .delegate(), and .live() jQuery methods.