jQuery Positioning and Animation - javascript

So I have a piece of code:
jQuery.fn.center = function(parent) {
parent = this.parent();
this.css({
"position": "absolute",
"top": ((($(parent).height() - this.outerHeight()) / 2) + $(parent).scrollTop() + "px"),
"left": ((($(parent).width() - this.outerWidth()) / 2) + $(parent).scrollLeft() + "px")
});
return this;
}
This code grabs an element and moves it to the center of the parent. Works nicely. There are a couple of things I would like it to do aside of what it's doing.
I need it to float above all other elements in that parent. A z index more or less. I tried to apply a z-index, but it didn't seem to work. I dunno if I may have been doing it wrong, or if there is a better way.
I need it to move back to the position it was in. The way this works, is the block is gonna be positioned on the page (via CSS), and when the user clicks, it moves it to the center of the page, as the code is already doing. Is there any way that, if a user clicks an exit button, I can program said button to move it back to where it originally was? i don't know if I can pass variables around in jQuery.
This option is more for aesthetics, and i don't deem it necessary, but would like it if you have a little extra time. I would like the block to slide from it's position to the center of the page, and back, as opposed to just popping there. As I stated, this is just aesthetics and i really don't need that to happen.
Much appreciated to anyone that can help me out.

I tried to implement a similar requirement but It should have all the features that you were looking for. Try DEMO
I need it to float above all other elements in that parent. A z index
more or less. I tried to apply a z-index, but it didn't seem to work.
I dunno if I may have been doing it wrong, or if there is a better
way.
I always use z-index in range of 1001 or greater for such cases. Even in the demo, it works fine. Try this and let us know if you still not working for you.
I need it to move back to the position it was in. The way this works,
is the block is gonna be positioned on the page (via CSS), and when
the user clicks, it moves it to the center of the page, as the code is
already doing. Is there any way that, if a user clicks an exit button,
I can program said button to move it back to where it originally was?
i don't know if I can pass variables around in jQuery.
I stored the original position of the element using .data API and restored it back to its original position when close(exit) button is clicked.
This option is more for aesthetics, and i don't deem it necessary, but
would like it if you have a little extra time. I would like the block
to slide from it's position to the center of the page, and back, as
opposed to just popping there. As I stated, this is just aesthetics
and i really don't need that to happen.
You can use .animate instead of CSS which will transcend the element from it original position to the destination position in specified time.

In order for it to float above all other elements, you're right in that you'll need to use z-index for that, but you'll need to make sure that each element is positioned and each element has a z-index value to start with, including the parent. The parent will also need to be positioned.
In order to revert back to their original positions, you'll have to grab their current left and top offsets. In your click event listener, move the element back to its old position.
jQuery.fn.center = function(parent) {
var oldTop = this.css('top');
var oldLeft = this.css('left');
parent = this.parent();
this.css({
"position": "absolute",
"top": ((($(parent).height() - this.outerHeight()) / 2) + $(parent).scrollTop() + "px"),
"left": ((($(parent).width() - this.outerWidth()) / 2) + $(parent).scrollLeft() + "px")
});
this.click(function(e) {
this.css('top', oldTop);
this.css('left', oldLeft);
});
return this;
}
In order to do the animation (instead of just jumping to the position), you can animate the left and top values using jQuery.
this.animate({
'top': ((($(parent).height() - this.outerHeight()) / 2) + $(parent).scrollTop() + "px"),
'left': ((($(parent).width() - this.outerWidth()) / 2) + $(parent).scrollLeft() + "px")
});
EDIT You may want to consider using this.offset(). This returns an object containing the methods left and top.
var offset = this.offset();
var oldTop = offset.top;
var oldLeft = offset.left;

Related

Setting image position with javascript, not always placing correctly

I'm using the following code to position images on my page:
var adjustImages = function(){
var monsters = [$("#m3"), $("#m4")];
monsters[0].css('right', monsters[0].width() * -0.4 + "px");
monsters[0].css('top', $("#divider-green").height() + $("#divider-orange").height() + (monsters[3].height() / 6) + "px");
monsters[1].css('left', monsters[1].width() * -0.385 + "px");
monsters[1].css('top', $("#divider-green").height() + $("#divider-orange").height() + $("#divider-red").height() + "px");
}
I'm then calling this function when the page loads, and when it's resized:
$(document).ready(function(){
adjustImages();
});
window.onresize = function(event) {
adjustImages();
};
The images are meant to be positioned on the window border (as in, part of the image is off the screen, part it off). This is done by setting right/left to a negative number (relative to the image size).
Sometimes when I refresh the page the images are placed correctly, however other times they are not over the border (but are rather positioned against the border (as if no left/right adjustment was applied). Does anyone know what the cause of this might be?
It looks like you aren't doing any check to see if the image is loaded before doing math based on it's width. That's probably what's causing the inconsistent behavior.
When an image is first created in the DOM, before it's loaded (and if it doesn't have one set directly), it's width and height are 0. What I would do is add a load event listener on your images and then call adjust.
You also want to call adjustImages() if one of the images already has a width greater than 0, because that means it's already loaded by the time the document is ready.
$(document).ready(function(){
$('#m3, #m4').one('load', function () {
adjustImages();
});
if ($('#m3').width > 0 || $('#m4').width > 0) {
adjustImages();
}
});
Now, that code snippet has a bit of a bug because it'll fire once either is loaded, not when both are loaded. You'll want to tweak it, and probably even separate them out so you're doing things individually for each, but it should give you the idea.

Unable to calculate the hover element's exact position

I am just trying to get the mouse hover div's position at the right according to the space around. Somehow I am able to do this in first two columns but not for other columns. May be my calculations while writing the condition state are wrong.
Can anyone please help?
JS Fiddle URL:
http://jsfiddle.net/mufeedahmad/2u1zr11f/7/
JS Code:
$('.thumb-over-team li').find('.cover').css({opacity:0});
$('.thumb-over-team li').on('mouseenter', function(){
var $this = $(this),
thisoffset = $this.position().left,
openDivId = $(this).find('.cover'),
thumbContainer = '.thumb-over-team',
speedanim = 200;
if(thisoffset + openDivId.outerWidth() >= $(thumbContainer).outerWidth()){
//thisoffset = $(thumbContainer).outerWidth() - openDivId.outerWidth() - 212;
thisoffset = thisoffset - openDivId.outerWidth()+10;
openDivId.stop().css({'z-index':'9999'}).animate({'opacity':'1', 'left':-thisoffset}, 200);
}else{
openDivId.stop().css({'z-index':'9999'}).animate({'opacity':'1', 'left':'212px'}, 200);
}
}).on('mouseleave', function(){
$(this).find('.cover').stop().css({'z-index':'-1'}).animate({'opacity':'0', 'left':'200px'}, 200);
});
$('.close-parent').on('click', function(){
$(this).parents('.cover').stop().css({'z-index':'-1'}).animate({'opacity':'0', 'left':'200px'}, 200);
});;
In your first conditional, try to calculate the position of the offset as:
thisoffset = ($(thumbContainer).outerWidth() - openDivId.outerWidth() - thisoffset);
That way, you're adjusting the appearing square (.cover) when it doesn't fit inside the container, to be as close possible to its rightmost edge: (maximum width - appearing square width - current li position)
Calculated this way, you can animate it with the new offset in positive:
openDivId.stop().css({'z-index':'9999'}).animate({'opacity':'1', 'left':thisoffset}, 200);
See it working here.
For elements that "almost" fit, the current system isn't completely precise because of what I already pointed out in my previous comment: the appearing square, even if it were at 0 opacity, would still be inside the containing element (($(thumbContainer)) or .thumb-over-team) and it would add its width to the total width of the container.
So your conditional may think that there's enough available space in the container to make the expandable element fit, but that would go out of the screen. As an example, notice that there's a horizontal scrollbar from the very beginning, caused by this effect, where your containing .thumb-over-team element doesn't fit in the screen.
But I would say that more precision in this point would require a fresh new approach to your system where the appearing .cover elements were out of the containing ul .thumb-over-team
Fresh take on the problem, essentially based on the main issue: the expandable text block (.cover) used to add its width to the container (.thumb-over-team). This altered the calculations on available container space, and made the text containers go off screen.
The solution is to make sure the expandable .cover elements aren't contained inside the .thumb-over-team element, so they won't impact the calculations on available width.
Here is a JSFiddle containing this new approach: link.
Explanation of how it works:
The idea was to create a separate element called .cover-container and let's put all the expandable .cover elements in there.
We want to associate every image in the li elements in .thumb-over-team with their appropriate .cover (so the first image triggers the first .cover to show, the second image would show the second cover, and so on.) We achieve is by finding out the index of the element that triggered the event:
thisLiIndex = $this.index() + 1
And then selecting the cover in the matching position:
openDivId = $('.cover-container .cover:nth-child(' + thisLiIndex + ')')
The expandable covers shouldn't interfere with the mouseenter or mouseleave events of .thumb-over-team, so we make it to ignore mouse events via CSS:
.cover-container{pointer-events:none;}
Changing from one image to another would automatically trigger new events, so the expanding covers stay visible when the mouse stays on the images, but close automatically when the mouse exits them.
Since the covers are now outside of $(thumbContainer), openDivID.outerWidth() does not alter $(thumbContainer).outerWidth(), and we can use that safely in our positioning.
If I understand the placement that you want, for covers that fit, the position is the current offset (position of the li element that triggered the event) plus the width of the image and some subtle margin
imageWidth + rightSeparation + thisoffset
And for covers that won't fit inside of the screen, we keep them just inside of the screen
thisoffset = $(thumbContainer).outerWidth() - openDivId.outerWidth();

Popover appearing in the wrong place

JS Fiddle here: http://jsfiddle.net/Xw9QK/1/
I am making a popover, and had it working, but then when I started loading the content though ajax and adding a spinner to appear first it now appears in the wrong place.
I think the problem is something to do with the fact the spinner makes a small popover appear and when the content loads it uses the same div to add the ajax content then resizes the box before moving it.
Now sometimes it works, on my site it seems to not work first time, but when closing and opening it again it snaps back to the correct position!
Can anyone see whats going wrong here?
I am using this function to reset the position:
function reset_popover_position(user_id) {
var position = $('#link_' + user_id).position();
console.log(position);
var top_position = (position.top - $('.new_tooltip').outerHeight()) - 10;
console.log(top_position);
var left_position = (position.left - ($('.new_tooltip').outerWidth() / 2) + ($('#link_' + user_id).outerWidth() / 2));
console.log(left_position);
$('.new_tooltip').css({
top: top_position + "px",
left: left_position + "px"
});
return true;
}
A setTimeout fixed the issue. Fiddle.
It is a trick that I use when I deal with dynamic contents height & width.
I think it setTimeout of 1 gives the browser enough time to render the html & so, when setTimeout triggers the actual code that, calculates dimensions, everything gives proper results.
I added setTimeout inside toggle_popover.
setTimeout(function() {
reset_popover_position(user_id);
}, 1);

Javascript height sums don't match

I have a div, absolutely positioned, originally non-visible that is shown at the position of an element being clicked rendering its preview (top position of the preview is lined to the top of the element clicked).
When the element being clicked is positioned low, the preview is render somewhat below the original page border, and scrolling is necessary. I want to move the preview upward to have its bottom edge on the previous page bottom limit. The problem is the code I use doesn't return what is expected for the page height (it is greater than the sum of the preview height and the clicked-element top position).
Here's the code:
file 1:
jQuery('elementClicked').live('click',function(){
...
jQuery("previewDiv").setTopAtClickedElement(jQuery(this));
...
}
file 2:
jQuery.fn.setTopAtClickedElement = function (element) {
//original positioning
this.css('top', element.offset().top + 'px');
// the troublesome part where the eventual correction should be done
if (element.offset().top + this.height() > jQuery(document).height())
{
this.css('top', jQuery(document).height() - this.height() + 'px');
}
}
Similar happens when I use
Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight)
for the measure of the document height as suggested on a link
Do you have any suggestions on how I should implement this troublesome part of the code?
Please tell if I wasn't clear enough,
Thank you,
Instead of .height() Try using jQuery's outer.height() - api docs, which will take into account any padding (and optionally marign) you have on the page.
A jsfiddle or codepen will help us all out in solving your problem.

Muliple div creation, jquery/javascript, performance/best practice

Im trying to figure out best practices in regard to performance when creating multiple DIV's at an insane rate. For example, on every .mousemove event...
$('head').append("<style>.draw {width: 20px; height: 20px; position:fixed;</style>");
$(document).mousemove(function(mouseMOVE) {
//current mouse position
var mouseXcurrent = mouseMOVE.pageX;
var mouseYcurrent = mouseMOVE.pageY;
//function to create div
function mouseTRAIL(mouseX, mouseY, COLOR) {
$('body').append("<div class='draw' style='top:" + mouseY + "px; left:" + mouseX + "px; background: " + COLOR + ";'></div>");
}
// function call to create <div> at current mouse positiion
mouseTRAIL(mouseXcurrent, mouseYcurrent, '#00F');
// Remove <div>
setTimeout(function() {
$('.draw:first-child').remove();
}, 250);
});
So, this works all nice and dandy but it's mega inefficient (especially so when I try filling in the space between each mouse move position). Here's an example...
$('head').append("<style>.draw {width: 20px; height: 20px; position:fixed;</style>");
$(document).mousemove(function(mouseMOVE) {
//current mouse position
var mouseXcurrent = mouseMOVE.pageX;
var mouseYcurrent = mouseMOVE.pageY;
// function to create div
function mouseTRAIL(mouseX, mouseY, COLOR) {
$('body').append("<div class='draw' style='top:" + mouseY + "px; left:" + mouseX + "px; background: " + COLOR + ";'></div>");
}
// function call to create <div> at current mouse positiion
mouseTRAIL(mouseXcurrent, mouseYcurrent, '#00F');
// variabls to calculate position between current and last mouse position
var num = ($('.draw').length) - 3;
var mouseXold = parseInt($('.draw:eq(' + num + ')').css('left'), 10);
var mouseYold = parseInt($('.draw:eq(' + num + ')').css('top'), 10);
var mouseXfill = (mouseXcurrent + mouseXold) / 2;
var mouseYfill = (mouseYcurrent + mouseYold) / 2;
// if first and last mouse postion exist, function call to create a div between them
if ($('.draw').length > 2) {
mouseTRAIL(mouseXfill, mouseYfill, '#F80');
}
// Remove <div>
setTimeout(function() {
$('.draw:first-child').remove();
$('.draw:nth-child(2)').remove();
}, 250);
});
I really cant figure out how to improve things. Believe me, Ive tried researching but it hasn't done much good... What I'm looking for is some suggestions, examples, or links to better practices...
Please note that I'm teaching myself to code. I'm a Graphic Design student and this is how I'm spending my summer out of class... Making little projects to teach myself JavasSript, fun stuff :)
Ive set up some jsfiddles to show what Im working on...
Mouse Trail, More Elements - Very Very Slow
Mouse Trail, Less Elements - Very Slow
Mouse Trail, Bare Bones - Slow
There are multiple bad practices going on here:
Using elements instead of Canvas
Using those elements via jQuery
Abusing that jQuery as if you were trying to make it slow on purpose
Stuffing all of the above inside a mousemove handler
The root issue here really is using elements instead of canvas. After fixing that, the interaction with DOM should become minimal and thus
fix the other points as well.
Also, those who claim that this works fine didn't check their CPU usage. On my Core I5-2500K one core is instantly maxed up which is ridiculous and unacceptable for something trivial like rendering a mouse trail on screen.
I can very well imagine this being very very slow on an older computer. So yes, it's smooth but at the cost of using amount of resources enough for 10-20+ tabs to do the same properly.
This takes 7-14% cpu for me when moving mouse around fast, this takes full 25%.
You should be careful not to cause a reflow and stick only to a repaint. -> When does reflow happen in a DOM environment?
So creating <div>s is no option. - But you don't need to :)
Just create as many <div>s as you will need in future and then reposition them. If you have them in an array you'd only need an integer that points to the current most one and on each mouse movement you'd increase that value (set it to 0 once it reaches the array lenght) and reposition the <div> that's pointed to by that number.

Categories