javascript scroll object by mouseover - javascript

I'm trying to make it so that a div will scroll when the mouse hovers over the an image but can only scroll so far. Somewhere its just not quite working
$(document).ready(function()
{
$('#test').bind('mouseenter', function()
{
var self = $(this);
var right = parseInt(self.style.left) || 200;
this.iid = setInterval(function()
{
if (right <= 400)
{
right += 200;
}
else
{
right = 400;
}
self.style.left = right + "px";
}, 525);
}).bind('mouseleave', function()
{
this.iid && clearInterval(this.iid);
});
});​
#test {
color:white;
width: 400px;
height: 400px;
left: 200px;
background-color: #000;
position: absolute;
}
<div id="test">hover me please</div>​
or a fiddle here:
http://jsfiddle.net/qjxqC/1/
Thanks for you help

First of all don't attach a JavaScript property (iid) to a DOM reference (this) this.iid. This will cause memory leaks in certain browsers (IE). I'd also recommend a recursive timeout as well.
I'd use setTimeout for this operation. It provides more control given your limit check and easier break from within your function. Here is your code reworked.
$(document).ready(function(){
var timeout; // keep your timeout reference at a higher scope for example
$('#test').bind({
'mouseenter': function(){
var self = $(this);
var incrementScroll = function() {
var left = parseInt(self.css("left"));
if(left <= 400) {
self.css("left", "+=200"); // as of jQuery 1.6 you can do this
// self.css("left", left+200);
timeout = setTimeout(incrementScroll, 525);
}
}
incrementScroll();
},
'mouseleave': function(){
clearTimeout(timeout);
}});
});

Replace
var self = $(this);
By
var self = this;
Because style is a property of a DOM object, not jQuery object, but you do:
self.style.left = right + "px";

Related

Add and Remove class on window scroll [duplicate]

So basically I'd like to remove the class from 'header' after the user scrolls down a little and add another class to change it's look.
Trying to figure out the simplest way of doing this but I can't make it work.
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll <= 500) {
$(".clearheader").removeClass("clearHeader").addClass("darkHeader");
}
}
CSS
.clearHeader{
height: 200px;
background-color: rgba(107,107,107,0.66);
position: fixed;
top:200;
width: 100%;
}
.darkHeader { height: 100px; }
.wrapper {
height:2000px;
}
HTML
<header class="clearHeader"> </header>
<div class="wrapper"> </div>
I'm sure I'm doing something very elementary wrong.
$(window).scroll(function() {
var scroll = $(window).scrollTop();
//>=, not <=
if (scroll >= 500) {
//clearHeader, not clearheader - caps H
$(".clearHeader").addClass("darkHeader");
}
}); //missing );
Fiddle
Also, by removing the clearHeader class, you're removing the position:fixed; from the element as well as the ability of re-selecting it through the $(".clearHeader") selector. I'd suggest not removing that class and adding a new CSS class on top of it for styling purposes.
And if you want to "reset" the class addition when the users scrolls back up:
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll >= 500) {
$(".clearHeader").addClass("darkHeader");
} else {
$(".clearHeader").removeClass("darkHeader");
}
});
Fiddle
edit: Here's version caching the header selector - better performance as it won't query the DOM every time you scroll and you can safely remove/add any class to the header element without losing the reference:
$(function() {
//caches a jQuery object containing the header element
var header = $(".clearHeader");
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll >= 500) {
header.removeClass('clearHeader').addClass("darkHeader");
} else {
header.removeClass("darkHeader").addClass('clearHeader');
}
});
});
Fiddle
Pure javascript
Here's javascript-only example of handling classes during scrolling.
const navbar = document.getElementById('navbar')
// OnScroll event handler
const onScroll = () => {
// Get scroll value
const scroll = document.documentElement.scrollTop
// If scroll value is more than 0 - add class
if (scroll > 0) {
navbar.classList.add("scrolled");
} else {
navbar.classList.remove("scrolled")
}
}
// Use the function
window.addEventListener('scroll', onScroll)
#navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
width: 100%;
height: 60px;
background-color: #89d0f7;
box-shadow: 0px 5px 0px rgba(0, 0, 0, 0);
transition: box-shadow 500ms;
}
#navbar.scrolled {
box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.25);
}
#content {
height: 3000px;
margin-top: 60px;
}
<!-- Optional - lodash library, used for throttlin onScroll handler-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
<header id="navbar"></header>
<div id="content"></div>
Some improvements
You'd probably want to throttle handling scroll events, more so as handler logic gets more complex, in that case throttle from lodash lib comes in handy.
And if you're doing spa, keep in mind that you need to clear event listeners with removeEventListener once they're not needed (eg during onDestroy lifecycle hook of your component, like destroyed() for Vue, or maybe return function of useEffect hook for React).
Example throttling with lodash:
// Throttling onScroll handler at 100ms with lodash
const throttledOnScroll = _.throttle(onScroll, 100, {})
// Use
window.addEventListener('scroll', throttledOnScroll)
Add some transition effect to it if you like:
http://jsbin.com/boreme/17/edit?html,css,js
.clearHeader {
height:50px;
background:lightblue;
position:fixed;
top:0;
left:0;
width:100%;
-webkit-transition: background 2s; /* For Safari 3.1 to 6.0 */
transition: background 2s;
}
.clearHeader.darkHeader {
background:#000;
}
Its my code
jQuery(document).ready(function(e) {
var WindowHeight = jQuery(window).height();
var load_element = 0;
//position of element
var scroll_position = jQuery('.product-bottom').offset().top;
var screen_height = jQuery(window).height();
var activation_offset = 0;
var max_scroll_height = jQuery('body').height() + screen_height;
var scroll_activation_point = scroll_position - (screen_height * activation_offset);
jQuery(window).on('scroll', function(e) {
var y_scroll_pos = window.pageYOffset;
var element_in_view = y_scroll_pos > scroll_activation_point;
var has_reached_bottom_of_page = max_scroll_height <= y_scroll_pos && !element_in_view;
if (element_in_view || has_reached_bottom_of_page) {
jQuery('.product-bottom').addClass("change");
} else {
jQuery('.product-bottom').removeClass("change");
}
});
});
Its working Fine
Is this value intended? if (scroll <= 500) { ... This means it's happening from 0 to 500, and not 500 and greater. In the original post you said "after the user scrolls down a little"
In a similar case, I wanted to avoid always calling addClass or removeClass due to performance issues. I've split the scroll handler function into two individual functions, used according to the current state. I also added a debounce functionality according to this article: https://developers.google.com/web/fundamentals/performance/rendering/debounce-your-input-handlers
var $header = jQuery( ".clearHeader" );
var appScroll = appScrollForward;
var appScrollPosition = 0;
var scheduledAnimationFrame = false;
function appScrollReverse() {
scheduledAnimationFrame = false;
if ( appScrollPosition > 500 )
return;
$header.removeClass( "darkHeader" );
appScroll = appScrollForward;
}
function appScrollForward() {
scheduledAnimationFrame = false;
if ( appScrollPosition < 500 )
return;
$header.addClass( "darkHeader" );
appScroll = appScrollReverse;
}
function appScrollHandler() {
appScrollPosition = window.pageYOffset;
if ( scheduledAnimationFrame )
return;
scheduledAnimationFrame = true;
requestAnimationFrame( appScroll );
}
jQuery( window ).scroll( appScrollHandler );
Maybe someone finds this helpful.
For Android mobile $(window).scroll(function() and $(document).scroll(function() may or may not work. So instead use the following.
jQuery(document.body).scroll(function() {
var scroll = jQuery(document.body).scrollTop();
if (scroll >= 300) {
//alert();
header.addClass("sticky");
} else {
header.removeClass('sticky');
}
});
This code worked for me. Hope it will help you.
This is based of of #shahzad-yousuf's answer, but I only needed to compress a menu when the user scrolled down. I used the reference point of the top container rolling "off screen" to initiate the "squish"
<script type="text/javascript">
$(document).ready(function (e) {
//position of element
var scroll_position = $('div.mainContainer').offset().top;
var scroll_activation_point = scroll_position;
$(window).on('scroll', function (e) {
var y_scroll_pos = window.pageYOffset;
var element_in_view = scroll_activation_point < y_scroll_pos;
if (element_in_view) {
$('body').addClass("toolbar-compressed ");
$('div.toolbar').addClass("toolbar-compressed ");
} else {
$('body').removeClass("toolbar-compressed ");
$('div.toolbar').removeClass("toolbar-compressed ");
}
});
}); </script>

Function within requestanimationframe() called too many times, how to call only once?

I currently have a script in which I am tracking the .offset() position of an element. When this element reaches the top, left, bottom, or right edge of the window, I need to call a function, which should only be called once, and would then be called again the next time it hits the edge of the window.
Right now I have this:
var exceededBounds = false
function checkPosition(element) {
var pos = element.offset();
var maxBoundsX = $(window).width();
var maxBoundsY = $(window).height();
var minBoundsX = 0
var minBoundsY = 0
// if the element hits one of the edges of the window
if (pos.left >= maxBoundsX || pos.top >= maxBoundsY || pos.left <= minBoundsX || pos.top <= minBoundsY) {
if (!exceededBounds) {
exceededBounds = true
}
else {
exceededBounds = false
}
}
else {
exceededBounds = exceededBounds;
}
}
function something() {
// do something here, which only happens once
}
function init() {
checkPosition(element);
if (exceededBounds) {
something()
}
requestAnimationFrame(init);
}
requestAnimationFrame(init);
The problem is, the something() function is called multiple times, I believe due to the frame rate of requestanimationframe() ? I need to use requestanimationframe, however I only want to call the something() function essentially when the variable exceededBounds changes. I thought about trying to implement some kind of observe thing here, but it felt too complex for what I actually need.
Thanks
You need to keep two booleans:
exceeding to know if your element is currently out of bounds.
changed to know if at last check it was already exceeding or not.
var element = $('#el');
var changed = false;
var exceeding = false;
function checkPosition(element) {
var pos = element.offset();
var maxBoundsX = $(window).width();
var maxBoundsY = $(window).height();
var minBoundsX = 0
var minBoundsY = 0
// if the element hits one of the edges of the window
if (pos.left >= maxBoundsX || pos.top >= maxBoundsY || pos.left <= minBoundsX || pos.top <= minBoundsY) {
if (!exceeding) {
changed = true
} else {
changed = false;
}
exceeding = true;
} else {
if (exceeding) {
changed = true;
} else {
changed = false;
}
exceeding = false;
}
}
function something() {
console.log('changed:', exceeding ? 'hidden' : 'visible');
// do something here, which only happens once
}
function init() {
checkPosition(element);
if (changed) {
something()
}
requestAnimationFrame(init);
}
requestAnimationFrame(init);
#el {
margin-top: calc(100vh - 30px);
margin-bottom: 100vh;
}
#cont {
width: 100vw;
height: 100vh;
overflow: auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="cont">
<div id="el">Scroll me</div>
</div>
Now, I don't know what is causing your element to move in your scenario, but don't poll like that. Instead, listen to the event that may cause this change. E.G, in my snippet, you would listen to a throttled scroll event.
var element = $('#el');
var changed = false;
var exceeding = false;
cont.onscroll = throttle(init);
function checkPosition(element) {
var pos = element.offset();
var maxBoundsX = $(window).width();
var maxBoundsY = $(window).height();
var minBoundsX = 0
var minBoundsY = 0
// if the element hits one of the edges of the window
if (pos.left >= maxBoundsX || pos.top >= maxBoundsY || pos.left <= minBoundsX || pos.top <= minBoundsY) {
// our if else blocks can also be replaced by
changed = !exceeding;
exceeding = true;
} else {
changed = !!exceeding;
exceeding = false;
}
}
function something() {
console.log('changed:', exceeding ? 'hidden' : 'visible');
// do something here, which only happens once
}
function init() {
checkPosition(element);
if (changed) {
something()
}
}
// wait for next screen refresh before triggering event's callback
function throttle(callback) {
if (typeof callback !== 'function')
throw 'A callback function must be passed';
var active = false;
var evt;
function handler() {
active = false;
callback(evt);
};
return function handleEvent(e) {
evt = e;
if (!active) {
active = true;
requestAnimationFrame(handler);
}
};
}
#el {
margin-top: calc(100vh - 30px);
margin-bottom: 100vh;
}
#cont {
width: 100vw;
height: 100vh;
overflow: auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="cont">
<div id="el">Scroll me</div>
</div>
something() will be called multiple times if the element is ever has exceededBounds, unless your something immediately changes the element immediately so that it's within bounds - that's how recursively calling requestAnimationFrame works, it'll just keep getting called over and over again.
I'd suggest altering something such that the element is immediately altered so as to make it within bounds again - then, something will only run once (until it goes out of bounds again).

animating a background image with jquery

I am having a view problems with animating a background to give the impression that a window is opening, I have the animation actually animating but it just animates like the images are in sequence. What I am wanting is to give the impression of a movie or animated gif.
You can see my current attempt here,
http://jsfiddle.net/8nj4934w/
I have so far done the following javascript,
$('a').bind('mouseenter', function() {
var self = $(this);
this.iid = setInterval(function() {
self.animate({ 'background-position-y': '-=500px' });
}, 300);
}).bind('mouseleave', function(){
this.iid && clearInterval(this.iid);
});
and the kind of effect I am going for here,
http://www.jeld-wen.com/catalog/exterior-doors
just hover of a door image.
Update (for jQuery solution handling two way sliding)
function slide(that, increment, limit){
var self = $(that);
that.iid && clearInterval( that.iid );
that.pos = that.pos || 0;
return setInterval(function () {
that.pos = that.pos += increment;
if (that.pos === limit){
clearInterval(that.iid);
} else {
self.css({
'background-position': '0 '+ that.pos + 'px'
});
}
}, 50);
}
$('a').bind('mouseenter', function () {
this.iid = slide( this, -500, -11500 );
}).bind('mouseleave', function () {
this.iid = slide(this, 500, 0);
});
Demo at http://jsfiddle.net/g8cypadx/
Original answer
I would suggest that you use CSS transitions with steps.
a {
background-image: url('https://dl.dropboxusercontent.com/u/58586640/9100_FRONT_STRIP.png');
background-repeat: no-repeat;
height: 500px;
width: 500px;
display: block;
background-position: 0 0;
transition: background-position 1s steps(23);
}
a:hover {
background-position: 0 -11500px; /* number of steps times the height */
}
If you have to do it through jQuery then you should animate both properties but with 0 duration for the animation, and small delay for the interval
$('a').bind('mouseenter', function () {
var self = $(this);
this.iid = setInterval(function () {
self.animate({
'background-position': '-=0 -=500px'
}, 0);
}, 50);
}).bind('mouseleave', function () {
this.iid && clearInterval(this.iid);
});
Demo at http://jsfiddle.net/8nj4934w/2/
(the problem with this solution is that it will not stop at the end)

How can I create a click and drag to change the width of an object with jQuery?

I have an object that I want to change the width of when you click on it and drag right or left. Adding to the width or taking away from it as you move the mouse (or finger).
<style>
#changeMe {width:300px; height: 200px;}
</style>
<div id="changeMe">
Some Content
</div>
<script>
$('#changeMe').mousedown(function(){
//Some Code?????
});
</script>
What you want to do is track the x co-ords of the mouse. If greater than they were before, increase size, if lower, decrease the size.
Not tested but the below should be inspiration.
var oldXPos = 0;
var isDragging = false;
$(document).mousemove(function(event) {
if (isDragging)
{
var difference = event.pageX - oldXPos;
// Do something with this value, will be an int related to how much it's moved
// ie $('#changeMe').css.width += difference;
}
oldXPos = event.pageX;
});
$('#changeMe').mousedown(function() {
isDragging = true;
})
$('#changeMe').mouseup(function() {
isDragging = false;
})
You just need a resizable({}) effect .
$('#changeMe').resizable({});
You can look at this article
Resizable
$(function() {
$('.testSubject').resizable();
});
or
#changeMe {width:300px; height: 200px; border: 1px solid black;}
<body>
<div id="changeMe">
Some Content
</div>
</body>
$('#changeMe').resizable({});
Or
$(function(){
$('img[src*=small]').toggle(
function() {
$(this).attr('src',
$(this).attr('src').replace(/small/,'medium'));
},
function() {
$(this).attr('src',
$(this).attr('src').replace(/medium/,'large'));
},
function() {
$(this).attr('src',
$(this).attr('src').replace(/large/,'small'));
}
);
});
If I understand your question correctly, that you want to be able to dynamically increase/decrease the size of the object as it moves along the x-axis, I recommend that you use $('#changeMe').offset() inside the jQuery UI drag event.
You can create a formula that you want to use based on an initial offset, and the new offset to size your container by $('#changeMe').css({ width: X, height: Y });, and insert whatever your X and Y values are in pixels.
Edited for further elaboration with code:
var initX = 0;
var initW = 0;
$('#changeMe').draggable({
start: function() {
initX = $(this).offset().left;
initW = $(this).width();
},
drag: function() {
var deltaX = initX - $(this).offset().left;
$(this).css({ width: initW + deltaX });
}
});
If you used that as your base, it's a very nice and simple solution for your application.

Disable click event until animation completes

In my game I have a grid populated with words. To spell the words the user has to click on the letters on the side called "drag". When a letter is clicked it animates into position on the grid.
The problem I am having is that the user can click letters rapidly, which crashes the program. To solve this I want to disable the click events until the animation completes.
In the past I have used a setTimeOut function before but it is not a reliable method as the timing all depends on browser speeds.
Here is the click event:
$('.drag').on('click', function (e) {
e.preventDefault();
var target = $('.highlight-problem .drop-box:not(.occupied):first');
var targetPos = target.parents('td').position();
console.log(targetPos);
var currentPos = $(this).offset();
var b = $(this);
if (target.length) {
target.addClass("occupied");
$(".occupied").parent(".flip-wrapper").addClass("flipped");
var clonedObject = b.clone()
if (b.data("letter") == target.parents('td').data("letter")) {
clonedObject.addClass("right-letter");
target.addClass('right-letter');
setTimeout(function () {
hitSound.play();
}, 550);
} else {
clonedObject.addClass("wrong-letter");
target.addClass('wrong-letter');
setTimeout(function () {
missSound.play();
}, 550);
}
clonedObject.appendTo("table").css({
position: "absolute",
top: currentPos.top,
left: currentPos.left
}).stop().animate({
top: targetPos.top,
left: targetPos.left
}, "slow", function () {
$(this).prop('disabled', false).css({
top: 0,
left: 0
}).appendTo(target);
var spellWord = $('.highlight-problem .drop-box');
if (!spellWord.filter(':not(.occupied)').length) {
var wordIsCorrect = 0;
spellWord.each(function () {
if ($(this).parents('td').data("letter") == $(this).find("div").data("letter")) {
console.log('letter is: ' + $(this).find("div").data("letter"))
wordIsCorrect++;
}
});
You can do it in simple 3 steps.
Declare a global variable called animationComplete and set it to false.
var animationComplete = false;
In your .animate({...}); function, toggle the value after the animation is complete.
.animate({
top: targetPos.top,
left: targetPos.left
}, "slow", function () {
...
animationComplete = true;
});
Check for the completeness using the global variable.
if (animationComplete)
{
// Do something!
}
Full Code
JavaScript
animationComplete = true;
$(document).ready(function(){
animationComplete = false;
$(".background").animate({
height: 50,
width: 50
}, 10000, function(){
animationComplete = true;
});
$("button").click(function(){
if (animationComplete)
$(".background").animate({
height: 200,
width: 200
}, 10000, function(){
animationComplete = false;
});
else
alert("Please wait till the animation is complete!");
});
});
HTML
<div class="background"></div>
<button>Click me!</button>​
CSS
.background {background: red;}
Fiddle: http://jsfiddle.net/KAryP/
Fiddle for your code here: http://jsfiddle.net/smilburn/Dxxmh/94/
Can't test code atm, but checking if the element is not animated within click function should do
if(!$('selector:animated')){
}

Categories