Positioning an element relative to another - javascript

I'm making a plugin that makes a dropdown on an element and use a pre-defined element as the dropdown menu outside the applied element.
The markup looks like this
Click
<ul id="dropdown-element">
<li>First item</li>
<li>Second item</li>
</ul>
And using $("#some-menu").dropdown({ placement: 'top' }); will turn #dropdown-element to a dropdown. All good and dandy.
placement decides on how the dropdown is positioned according to the element (top, bottom, right, left) but this should be fairly easy to apply, when I have figured the default positioning out.
I tried using this code from Bootstrap's Tooltip plugin
pos = getPosition($element, inside);
actualWidth = $this[0].offsetWidth;
actualHeight = $this[0].offsetHeight;
switch (inside ? placement.split(' ')[1] : placement) {
case 'bottom':
tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2};
break;
case 'top':
tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2};
break;
case 'left':
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth};
break;
case 'right':
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width};
break;
}
But without luck.
This is what it looks like
And this is the wanted result
I want it horizontally centered to #some-menu and the dropdown to be applied to any element, inlines too and keep both the vertical and horizontal positioning.
Edit:
I found out that the dropdown was created inside a element with position: relative, which is why its offset is wrong. My question is now what the best way to move the element into the body (or perhaps a better solution for this) and keep good performance without touching the DOM more than needed.

This should put the menu in the center.
top = $('#some-menu').offset().top + 8; //Set the vertical position.
left = $('#some-menu').offset().left - $('#dropdown-element').outerWidth/2 + $('#some-menu').outerWidth/2; //Set the menu to the center of the button.
$('#dropdown-element').offset({top: top, left: left});

Related

AngularJS - draggable bootstrap modal

I am using an AngularJS directive for modals, to make them draggable.
This is the directive.
In the demo, you can clearly see that if you drag it (especially left and right) it is slower than your mouse. I understand why this happens (the JavaScript calculates position relative to it's starting position, so in my 1920x1080 screen it goes from -1200px to 1920px on the x axis). And I understand there is a need to use offset instead of position, but after many tries I failed to make it that.
This is the relevant JavaScript code:
element.on('mousedown', function (event) {
// Prevent default dragging of selected content
event.preventDefault();
startX = event.screenX - x;
startY = event.screenY - y;
$document.on('mousemove', function mousemove(event) {
y = event.screenY - startY;
x = event.screenX - startX;
element.css({
top: y + 'px',
left: x + 'px'
});
});
});
How can I make it rely on the offset and move together with the mouse and not slower?
Try this one: http://plnkr.co/edit/QxIdGj . I have hardcoded 2 values, which you shouldn't do. Your "mistake" was that you were putting the draggable directive in the wrong element. I added the draggable directive to <div class="modal-content"> which is what I believe is the element that you want moved.
I also changed your element.css({ to
element.css({
top: event.clientY - 30 + 'px',
left: event.clientX - 10 + 'px'
});
It's using .clientX/Y which is the actual position of the mouse, without the need of further calculations.

I can't change tooltip bootstrap position with jQuery

I can't change tooltip bootstrap position with jQuery.
Example on http://jsfiddle.net/2cast8g8/
If I enter a bigger value in text1 my function ck() need to change the position of tooltip.
Also it is possible to change the color of tooltip in red with jQuery?
<input type="text" id="text1" name="title" value="" data-toggle="tooltip" data-placement="top"title="this need to be less">
<input type="text" id="text2" name="title" value="" data-toggle="tooltip" data-placement="bottom"title="bigger ">
 
$('#text1').change(function () {
ck()
});
$('#text2').change(function () {
ck()
});
function ck() {
text1 = document.getElementById("text1").value;
text2 = document.getElementById("text2").value;
if (Number(text1) > Number(text2)) {
$("#text2").attr("data-original-title", "This value need to be bigger than ");
$("#text1").attr("data-placement", "top");
$(function () {
$('#text2').tooltip('hide').tooltip('show');
});
} else {
$("#text2").attr("data-original-title", "bigger");
}
}
$(function () {
$('[data-toggle="tooltip"]').tooltip('show')
})
$('body').tooltip({
placement: function(tip, element) {
//code to calculate the position here, return values: "left", "right", "top", "bottom"
},
selector: '[data-toggle="tooltip"]'
});
The Bootstrap tooltip plugin has a 'placement' option which controls the general position (top,bottom,left,right) of the tooltip, but it calculates the exact position in this function:
Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } :
placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } :
placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
/* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width }
}
You could override this function to provide your own logic (perhaps by adding a 'custom' placement type. Note that the plugin applies the position type as a class to the element in addition to setting the css position properties. These classes are removed in the setContent function so you'd have to adjust that function to remove your new custom placement type class. It's likely there are other issues/considerations you'd have to account for as well - this wouldn't be a simple option. But it would be an interesting project and might even be worthy of a pull request :)
An alternative would be to simply move/override the position after the bootstrap plugin was done. There is a 'shown' event made available, but it is only triggered after the css transitions have been applied, so it may not suitable.
An example might be something like this (untested code):
$('#text2').on('shown.bs.tooltip', function () {
var left = $(this).css('left');
$(this).css({
left: left + 50
});
})
You might want to look at the jQueryUI tooltip widget - it has more features for positioning the tooltip (including adding custom offsets). http://api.jqueryui.com/tooltip/#option-position

Change position of div based on mouse pointer

I am trying to position a div based on mouse position, I managed to get it to work 50%.
The problem is that DIV always seems to be much lower than the actual mouse position, I try to minus the offset, no luck.
Basically what I want is to float the div(the NEXT link in jsfiddle) vertically, but the DIV should not be able to go outside of the container it is in(the div that has the image in the jsfiddle)
here is the jsfiddle: http://jsfiddle.net/LYmVH/7/
below is the JS, which is also in the jsfiddle:
$('.post-single-content').mousemove(function(e){
var height=e.pageY,
height1 =$('.content-top').height();
$('.btnNext').css({top: (e.pageY + 50) + "px"});
});
You need measure against the top of the parent element since it's absolutely positioned in it.
Try changing your JS to:
$('.post-single-content').mousemove(function(e){
var top = (e.pageY - $(this).offset().top) + 'px';
$('.btnNext').css({ top: top });
});
Upon reading some comments lemme update, by making use some basic math and create "collision". Somthing like:
$('.post-single-content').mousemove(function(e){
var y = e.pageY, // mouse y axis position
tHeight = $(this).height(), // container height
bHeight = $('.btnNext').height(), // button height
oTop = $(this).offset().top, // offset top position of container
abBot = tHeight - $('.btnNext').height(), // absolute top of button when at bottom
bHalf = bHeight / 2, // half button height
top = y - oTop - bHalf, // initial top pos of button
bot = y - oTop + bHalf; // bottom of button while moving
if (top < 0) top = 0; // ensure button doesn't go to far north
else if (bot > tHeight) top = abBot; // ensure it cant go past south
$('.btnNext').css({ top: top }); // 'px' not neccesary
});
jsFiddle

Javascript (jQuery) Scale an Image to Its Container's Center Point

This seems like it should be quite simple, but for some reason I can't quite wrap my brain around it. I have an image inside a "viewport" div, of which the overflow property is set to hidden.
I've implemented a simple zooming and panning with jQuery UI, however I am having trouble getting the zoom to appear to originate from the center of the viewport. I did a little screencast from Photoshop the effect I'm trying to reproduce: http://dl.dropbox.com/u/107346/share/reference-point-zoom.mov
In PS you can adjust the scaling reference point an the object will scale from that point. Obviously this is not possible with HTML/CSS/JS, so I'm trying to find the appropriate left and top CSS values to mimic the effect.
Here is the code in question, with a few unnecessary bits removed:
html
<div id="viewport">
<img id="map" src="http://dl.dropbox.com/u/107346/share/fake-map.png" alt="" />
</div>
<div id="zoom-control"></div>
javascript
$('#zoom-control').slider({
min: 300,
max: 1020,
value: 300,
step: 24,
slide: function(event, ui) {
var old_width = $('#map').width();
var new_width = ui.value;
var width_change = new_width - old_width;
$('#map').css({
width: new_width,
// this is where I'm stuck...
// dividing by 2 makes the map zoom
// from the center, but if I've panned
// the map to a different location I'd
// like that reference point to change.
// So instead of zooming relative to
// the map image center point, it would
// appear to zoom relative to the center
// of the viewport.
left: "-=" + (width_change / 2),
top: "-=" + (width_change / 2)
});
}
});
Here is the project on JSFiddle: http://jsfiddle.net/christiannaths/W4seR/
Here's the working solution. I will explain the logic at the next edit.
Function Logic:
Summary: Remember the center position of the image, relatively.
The calculations for width and height are similar, I will only explain the height calculationThe detailled explanation is just an example of function logic. The real code, with different variable names can be found at the bottom of the answer.
Calculate the center (x,y) of the #map, relative to #viewport. This can be done by using the offset(), height() and width() methods.
// Absolute difference between the top border of #map and #viewport
var differenceY = viewport.offset().top - map.offset().top;
// We want to get the center position, so add it.
var centerPosition = differenceY + viewport.height() * 0.5;
// Don't forget about the border (3px per CSS)
centerPosition += 3;
// Calculate the relative center position of #map
var relativeCenterY = centerPosition / map.height();
// RESULT: A relative offset. When initialized, the center of #map is at
// the center of #viewport, so 50% (= 0.5)
// Same method for relativeCenterX
Calculate the new top and left offsets:
// Calculate the effect of zooming (example: zoom 1->2 = 2)
var relativeChange = new_width / old_width;
// Calculate the new height
var new_height = relativeChange * old_height;
// Calculate the `top` and `left` CSS properties.
// These must be negative if the upperleft corner is outside he viewport
// Add 50% of the #viewport's height to correctly position #map
// (otherwise, the center will be at the upperleft corner)
var newTopCss = -relativeCenterY * new_height + 0.5 * viewport.height();
Change the CSS property
map.css("top", newTopCss);
Demo: http://jsfiddle.net/W4seR/12/
var map = $('#map');
var viewport = $('#viewport');
// Cache the size of the viewport (300x300)
var viewport_size = {
x: viewport.width(),
y: viewport.height()
};
map.draggable();
$('#zoom-control').slider({
min: 300,
max: 1020,
value: 300,
step: 24,
create: function() {
map.css({
'width': 300,
'left': 0,
'top': 0
});
},
slide: function(event, ui) {
var old_width = map.width();
var old_height = map.height();
var viewport_offset = viewport.offset();
var offset = map.offset();
offset = {
top: viewport_offset.top - offset.top + .5*viewport_size.y +3,
left: viewport_offset.left - offset.left + .5*viewport_size.x +3
};
// Relative offsets, relative to the center!
offset.top = offset.top / old_height;
offset.left = offset.left / old_width;
var new_width = ui.value;
var relative = new_width / old_width;
var new_height = relative * old_height;
offset = {
top: -offset.top * new_height + .5*viewport_size.y,
left: -offset.left * new_width + .5*viewport_size.x
};
var css_properties = {
width: new_width,
left: offset.left,
top: offset.top
};
map.css(css_properties);
trace((map.position().left));
}
});
I have always relied on the kindness of strangers. Pertinent changes:
// Calculate the offset as a percentage, accounting for the height of the window
var x_offset = ((map.position().left-150))/(old_width/2);
var y_offset = ((map.position().top-150))/(old_width/2);
var css_properties = {
width: new_width,
// Set the offset based on the existing percentage rather than 1/2
// then readjust for the height of the window
left: (new_width * x_offset /2 ) + 150 + "px",
top: (new_width * y_offset /2 ) + 150 + "px"
};
Replace the hardcoded 150 with a variable set on viewport instantiation if necessary.
Here is a quick working version:
http://jsfiddle.net/flabbyrabbit/chLkZ/
Probably not the neatest solution but seems to work nicely, hope it helps.
Update: sorry this only works if zoom is 0 when the map is moved.

Making an overlay window with jquery css(3) and HTML(5)

I am looking to create an overlay effect (modal) type effect on my website, I have one working already that is a fixed width and height, but I want it to fill 85% of the available screen space?
How can I achieve this?
My old code looks like this,
$('#overlay').fadeIn('fast');
$('#lightbox').css({
position:'fixed',
left: ($(window).width() - $('#lightbox').outerWidth())/2,
top: ($(window).height() - $('#lightbox').outerHeight())/2
});
You want the overlay to fill 85% of the screen space? Then you need to calculate the required width/height & x/y coordinates for that.
var targetProcent = 85;
var targetWidth = $(window).width() * (targetProcent / 100);
var targetHeight = $(window).height() * (targetProcent / 100);
var targetX = ($(window).width() - targetWidth) / 2;
var targetY = ($(window).height() - targetHeight) / 2;
$('#overlay').width(targetWidth);
$('#overlay').height(targetHeight);
$('#overlay').css({
"position": "absolute",
"top": targetY+"px",
"left": targetX+"px"
});
Alot of the variable assignments could probably be cut out, but left them in for clarity.

Categories

Resources