Move/Animate Div with Javascript - javascript

I want to move a div using javascript.
For example, how would I move a div to the left 50px using javascript?
I want the function to start when after I click on a link. Anyone know how this could be done?

Here is something that works:
HTML:
<div class="container">
<span id="box"></span>
</div>
Move test element.
CSS:
.container {
position: relative;
width: 100%;
height: 50px;
padding: 10px;
background-color: beige;
margin-bottom: 20px;
}
#box {
position: absolute;
top: 10px;
left: 100px;
height: 30px;
width: 30px;
background-color: blue;
}
Javascript:Please put this within the document ready function.
$("#move-test").on('click', function (e) {
e.preventDefault();
moveBox();
});
var moveBox = function(){
var $box = $("#box");
var left = $box.position().left;
left = left - 20;
$box.css('left', left + 'px');
};
Here is a jsfiddle with it moving continuously:
https://jsfiddle.net/5ncb6szg/

You should use position: relative as negative margins are frowned upon. Set the default to left: -100% which means that the element should be moved left the same amount as it is wide. For the javascript we assign a click handler to the link in the script instead of inline. Assign the variable open=false because it is closed on first load, then when we open it assign the variable to open=true. This gives state to the element without querying the dom.
var hidden = true;
var box = document.getElementById("box");
document.getElementById('toggle').onclick = function() {
if(hidden) hidden = false, box.style.left = 0;
else hidden = true, box.style.left = '-100%';
}
#box {
background: red;
height: 50px;
width: 50px;
position: relative;
left: -100%;
}
<a href="javascript:void(0)" id="toggle">
<h3 id="contact">Contact</h3>
</a>
<div id="box"></div>

Related

Safari selects wrong drag element when elements use transforms instead of left and top

When I have 3 draggable divs using position: absolute and positioning them using left and top the items are correctly draggable on Safari (and other browsers).
But when I set left and top to 0 and instead position them using transform.translateX and transform.translateY and try to drag them, Safari always selects the first item regardless of the mouse position.
The HTML code below shows this problem. Initially the items are positioned using left and top and you can drag any of the divs. When you click the button it moves them all to 0 (left and top) and repositions them using transform.translateX and transform.translateY so visually it looks the same, but when you try to drag them things get strange. It appears it's always selecting the topmost item at the origin (regardless of the cursor position) and it clips some of the content.
Is there a workaround for this, or can items not be dragged on Safari when they have transformations applied?
document.querySelector("button").addEventListener("click", (e) => {
document.querySelectorAll("div[draggable").forEach(elem => {
const style = getComputedStyle(elem);
const left = style.getPropertyValue("left");
const top = style.getPropertyValue("top");
elem.style.left = 0;
elem.style.top = 0;
elem.style.transform = `translateX(${left}) translateY(${top})`;
});
document.querySelector("p:nth-of-type(1)").style.display = "none";
document.querySelector("p:nth-of-type(2)").style.display = "";
e.target.style.display = "none";
});
body {
background-color: #444;
width: 720px;
height: 720px;
}
div[draggable] {
position: absolute;
display: flex;
justify-content: center;
align-items: center;
font-size: 30px;
color: #000;
border-radius: 16px;
width: 200px;
height: 200px;
}
div[draggable]:nth-child(1) {
background-color: #f80;
left: 20px;
top: 50px;
}
div[draggable]:nth-child(2) {
background-color: #8f0;
left: 240px;
top: 50px;
}
div[draggable]:nth-child(3) {
background-color: #08f;
left: 460px;
top: 50px;
}
button {
position: absolute;
left: 20px;
top: 10px;
padding: 4px 12px;
}
p {
color: white;
position: absolute;
left: 200px;
top: 0;
}
<div draggable="true">Item 1</div>
<div draggable="true">Item 2</div>
<div draggable="true">Item 3</div>
<p>Items use 'position: absolute' and are positioned using 'left' and 'top' properties.</p>
<p style="display: none">Items use 'position: absolute' and are positioned using 'transform.translateX' and 'transform.translateY' properties.</p>
<button>Position -> Transform</button>

can't get the click position of a div element

I want to create a minimap. So I have an accurate representation of div elements inside my minimap. I want the user to use the minimap to navigate around the site.
I get the correct position when I click inside my minimap (the gray box), but when I click on a "ghostly" or the green box, I get incorrect dimensions, which leads to an incorrect position setting.
here is a showcase:
function getClickPosition(e) {
// I need the click position of the gray box
// but when I click on the green or red box I get their values
console.log(e.layerX)
}
.minimap {
height: 100px;
width: 140px;
background-color: #999;
position: absolute;
z-index: 100;
}
.viewport-layer {
height: 20px;
width: 35px;
left: 20px;
top: 15px;
box-sizing: border-box;
position: absolute;
border: 2px solid green;
z-index: 101;
max-width: 100px;
}
.ghosty-box {
position: absolute;
top: 60px;
left: 90px;
height: 30px;
width: 40px;
background-color: red;
z-index: 0; // should be in the background
}
<div class="minimap" onclick="getClickPosition(event)">
<!-- user screen: green border -->
<div class="viewport-layer">
</div>
<!-- dynamic size of the minimap -->
<div class="relativeLayer">
<!-- representation of the visible elements -->
<div class="ghosty-box"></div>
</div>
</div>
my console of e says something like:
click { target: div.ghosty-box, ... layerX: 10, layerY: 11 }
or
click { target: div.viewportLayer, ... layerX: 33, layerY: 16 }
I was expecting that a z-index would help.
Do you have any suggestions to get the click position of the .minimap or .relativeLayer with elements behind it?
So the target is always the gray box?
I think you want relative values of clicked elements inside of minimap. This will give you the relative X and Y of the clicked item - as well as the relative mouseX and mouseY (along with the relative percentage position)
function getClickPosition(e) {
// I need the click position of the gray box
// but when I click on the green or red box I get their values
if (e.target.classList.contains('minimap')) {
console.log('clicked on minimap background');
return;
}
let ref = e.target.closest('.minimap').getBoundingClientRect()
let pos = e.target.getBoundingClientRect();
let posY = pos.top - ref.top
let posX = pos.left - ref.left
let mouseY = e.clientY - ref.top
let mouseYPerc = ((mouseY / ref.height) * 100).toFixed(2);
let mouseX = e.clientX - ref.top
let mouseXPerc = ((mouseX / ref.width) * 100).toFixed(2)
console.log('my relative position X:' + posX + ' Y:' + posY);
console.log("relative mouseX:" + mouseX + " (" + mouseXPerc + "%) horiz");
console.log("relative mouseY:" + mouseY + " (" + mouseYPerc + "%) vert");
}
.minimap {
height: 100px;
width: 140px;
background-color: #999;
position: absolute;
z-index: 100;
margin: 50px;
opacity: .2;
}
.viewport-layer {
height: 20px;
width: 35px;
left: 20px;
top: 15px;
box-sizing: border-box;
position: absolute;
border: 2px solid green;
z-index: 101;
max-width: 100px;
}
.ghosty-box {
position: absolute;
top: 60px;
left: 90px;
height: 30px;
width: 40px;
background-color: red;
z-index: 0; // should be in the background
}
<div class="minimap" onclick="getClickPosition(event)">
<!-- user screen: green border -->
<div class="viewport-layer">
</div>
<!-- dynamic size of the minimap -->
<div class="relativeLayer">
<!-- representation of the visible elements -->
<div class="ghosty-box"></div>
</div>
</div>
I'm not absolutely sure what readings you want out of the various layers, but a couple of comments:
According to MDN:
This feature is non-standard and is not on a standards track. Do not use it on production sites facing the Web: it will not work for every user. There may also be large incompatibilities between implementations and the behavior may change in the future.
If you want the position relative to the gray box, as in your example, you may want to look at event.pageX or event.clientX. As in this snippet:
function getClickPosition(e) {
// I need the click position of the gray box
// but when I click on the green or red box I get their values
console.log('pageX = ' + e.pageX);
console.log('clientX = ' + e.clientX);
}
.minimap {
height: 100px;
width: 140px;
background-color: #999;
position: absolute;
z-index: 100;
}
.viewport-layer {
height: 20px;
width: 35px;
left: 20px;
top: 15px;
box-sizing: border-box;
position: absolute;
border: 2px solid green;
z-index: 101;
max-width: 100px;
}
.ghosty-box {
position: absolute;
top: 60px;
left: 90px;
height: 30px;
width: 40px;
background-color: red;
z-index: 0; // should be in the background
}
<div class="minimap" onclick="getClickPosition(event)">
<!-- user screen: green border -->
<div class="viewport-layer">
</div>
<!-- dynamic size of the minimap -->
<div class="relativeLayer">
<!-- representation of the visible elements -->
<div class="ghosty-box"></div>
</div>
</div>
Alternatively, when you process the click event you may want to check which actual element has been clicked when you have nested elements and/or you may or may not want to stop propagation. It's worth looking at an event object console.log(e) for example to see what the target is and the other settings you are given to get the right ones.
if you don't really need to click the elements inside a good solution could be placing an absolute postioned empty layer covering the whole minimap just to capture the clicks. I added it to your snippet with the class .position-layer
function getClickPosition(e) {
// I need the click position of the gray box
// but when I click on the green or red box I get their values
console.log(e.layerX);
console.log(e.layerY);
}
.minimap {
height: 100px;
width: 140px;
background-color: #999;
position: absolute;
z-index: 100;
}
.viewport-layer {
height: 20px;
width: 35px;
left: 20px;
top: 15px;
box-sizing: border-box;
position: absolute;
border: 2px solid green;
z-index: 101;
max-width: 100px;
}
.ghosty-box {
position: absolute;
top: 60px;
left: 90px;
height: 30px;
width: 40px;
background-color: red;
z-index: 0; // should be in the background
}
.position-layer {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index:1000;
}
<div class="minimap">
<!-- user screen: green border -->
<div class="viewport-layer">
</div>
<!-- dynamic size of the minimap -->
<div class="relativeLayer">
<!-- representation of the visible elements -->
<div class="ghosty-box"></div>
</div>
<div class="position-layer" onclick="getClickPosition(event)"></div>
</div>
MDN says (I'm paraphrasing) that layerX is position of the mouse cursor relative to the clicked element or one of it's parents that is absolutely positioned element
Your ghosty-box is position: absolute, which means that is it's clicked, the layerX is relative to it.
If you could position it relatively or using margins, that would solve the issue.
Other option is using pageX or screenX and computing the offset yourself, or positioning an overlay element over the minimap and catching the click on that.

Save position of the dragged widget

Using jquery ui's draggable widget to move an image up/down to show the desired part of the image inside the container (when image is too large to fit inside container). The Image can be dragged up/down to display the ideal part inside the container (initially the center region of image is visible inside the container). The position value is then saved for future reloads.
When image is dragged a yellow button appears at the bottom showing the current position set by function dragCover(). This is the value that is stored for future. For testing, when you click on that button, it updates the position of the image by setting the css top prop. If value is correct, image should not move because the value would be equal to the current position, since dragCover is setting an incorrect value, image moves away from its current position. Try dragging the image all the way down and then clicking on yellow box, it moves back up.
https://codepen.io/apprence/pen/KKVwKBN
It's a little unclear what you're going for in the end, in your click callback. I created the following pseudo view port type of example to see if this is the type of function / result you're looking to get.
$(function() {
function calcPerc(el, vp) {
el = $(el), vp = $(vp);
var y1 = el.position().top;
var h = el.height();
return parseFloat(((y1 / h) * 100).toFixed(2));
}
function showData(t, p) {
$(".top").html(t);
$(".perc").html(p);
}
$(".item").draggable({
scroll: false,
axis: "y",
cursor: "move",
containment: [0, -899, 0, 1],
drag: function(e, ui) {
showData(ui.position.top, calcPerc(this, $(".viewport")));
}
});
$(".perc").click(function() {
var offsetVal = parseFloat($(this).text()) * 10;
console.log(offsetVal);
$(".item").css({
top: offsetVal + "%"
});
});
$(".buttons button").click(function() {
$(".item").css("top", $(this).html());
showData($(".item").position().top, calcPerc($(".item"), $(".viewport")));
});
});
.viewport {
position: absolute;
border: 1px dashed #222;
top: 0;
}
.small {
width: 200px;
height: 100px;
}
.tall {
width: 201px;
height: 1000px;
}
.item {
background-color: #ccf;
position: absolute;
top: -299px;
}
.item span {
padding-bottom: 80px;
display: block;
text-align: center;
border-bottom: 1px solid #222;
}
.show {
position: absolute;
border: 1px solid #000;
border-radius: 3px;
width: 3em;
padding: 3px;
}
.show.buttons {
width: 100px;
}
.top {
top: 20px;
left: 220px;
}
.perc {
top: 50px;
left: 220px;
}
.buttons {
top: 80px;
left: 220px;
}
.buttons button {
width: 100%;
}
.ghost {
width: 201px;
background-color: #fff;
opacity: 0.75;
height: 900px;
position: absolute;
}
.after {
top: 101px;
}
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<div class="container">
<div class="small viewport">
<div class="tall item">
<span>0</span>
<span>1</span>
<span>2</span>
<span>3</span>
<span>4</span>
<span>5</span>
<span>6</span>
<span>7</span>
<span>8</span>
<span>9</span>
</div>
</div>
<div class="ghost after"></div>
<div class="show top">-299</div>
<div class="show perc">-29.9</div>
<div class="show buttons">
<button>0%</button>
<button>-25%</button>
<button>-500%</button>
<button>-75%</button>
<button>-900%</button>
</div>
</div>
https://developer.mozilla.org/en-US/docs/Web/CSS/top
When position is set to absolute or fixed, the top property specifies the distance between the element's top edge and the top edge of its containing block.
A <percentage> of the containing block's height.
So, for my example, the containing block is 100px tall. Therefore, a top set to -25%, we have to calculate that:
top = 100 * -.25
top = -25
Therefore, we need to increase this exponentially. To get the proper location, -29.12%, needs to be -291.2%.
top = 100 * -2.912
top = -291.2
This should help you with your image movement and view port. Personally, I would store and reload the exact pixel value, but if you desire to store the percentage, you can do so.

Hiding a div when clicking out of it

I added code to make a div, #pending-friend-list-dropdown, close when clicking outside of it. This works fine, but now when clicking on my image div, friend-icon, the drop-down div will not close now.
As you can see in my snippet, the image div is what opens the drop-down box. I am just trying to figure out how that image div can be used to open and close the drop-down, while using the mouseup function to close the drop-down div as well.
//Hiding Pending Friend Drop-down when clicking out of it
$(document).mouseup(function (e)
{
var container = $("#pending-friend-list-dropdown");
var friend_icon = $("#friend-icon");
if (!container.is(e.target) // if the target of the click isn't the container...
&& container.has(e.target).length === 0) // ... nor a descendant of the container
{
container.hide();
}
else if (friend_icon.has(e.target)) {
container.hide();
}
});
//Toggle Pending Friend List
$("#friend-icon").click(function() {
$('#pending-friend-list-dropdown').toggle(100);
});
#main-bar {
width: 85%;
height: 60px;
position: relative;
margin-left: 15%;
background: red;
padding: 3px 0;
}
#main-bar-container {
border: 1px solid black;
margin: 0 10px;
position: relative;
width: 95%;
height: 56px;
left: 2%;
}
/*---- Pending Friends List----*/
#friend-icon {
display: inline-block;
cursor: pointer;
position: absolute;
right: 20%;
top: 15px;
}
#friend-icon img {
height: 30px;
width: 30px;
}
#pending-friend-list-dropdown {
height: 500px;
width: 400px;
overflow: scroll;
z-index: 100000;
position: absolute;
left: 70%;
top: 70px;
background: blue;
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="main-bar">
<div id="main-bar-container">
<div id="friend-icon"><img src="../icons/collection/social.png" alt="Pending Friends"></div>
</div>
</div>
<div id="pending-friend-list-dropdown">
</div>
You can achieve this more simply by running the code whenever someone clicks on the html-element (the entire page).
Then check if the click is located on certain elements.
There is also no need to give the instructions in two places for what to be done when clicking on "#friend-icon". I have removed the second instance of this in the below code, and just moved the .toggle up to the if statement.
It now works like a charm:
$("html").click(function(event)
{
var container = "#pending-friend-list-dropdown";
var friend_icon = '#friend-icon, #friend-icon img';
if ( $(event.target).is(friend_icon) ) // clicking on the toggler-div or the img it contains
{
$(container).toggle(100);
}
else if (!$(event.target).is(friend_icon) // clicking outside of the toggler
&& !$(event.target).is(container)) // and outside of the toggled div itself
{
$(container).hide();
}
});
Here's a jsfiddle: https://jsfiddle.net/r54ardcz/2/
I'll give a third option just so that all the ones I know are on this site. This is the option that Office Fabric UI uses (https://dev.office.com/fabric#/components/contextualmenu) where I think #zheer-mchammer-husain's answer is more along the Twitter Bootstrap model.
Essentially you create a layer over your whole page (height: 100vh and width: 100vw; position: fixed) and then put your dropdown content inside that layer. When the user clicks that layer, it closes the whole layer at once disappears and all is done.

onmouseover event misbehaving

I was trying to make somewhat of a curtain type effect and applied onmouseover to a parent div id. I am not able to understand why is the effect taking place when I mouse over on the child div, other than the parent div.
var left = document.getElementById("left");
var right = document.getElementById("right");
function curtain() {
left.style.transform = "rotate(30deg)";
right.style.transform = "rotate(-30deg)";
}
function back() {
left.style.transform = "rotate(0deg)";
right.style.transform = "rotate(0deg)";
}
CSS:
#animate {
width: 400px;
margin: auto;
position: relative;
}
img {
width: 100%;
}
#left {
position: absolute;
top: 0;
right: 50%;
padding: 0;
margin: 0;
width: 50%;
transform-origin: 0 0;
}
#right {
position: absolute;
top: 0;
right: 0%;
padding: 0;
margin: 0;
width: 50%;
transform-origin: 100% 0;
}
HTML:
<div id="animate" onmouseover="curtain()" onmouseout="back()">
<div id="left">
<img src="http://www.uwphotographyguide.com/images/Articles/image-overlay-3107.jpg">
</div>
<div id="right">
<img src="http://s3.freefoto.com/images/15/78/15_78_19_web.jpg">
</div>
</div>
I think the answer is that you made the position of the childs absolute. With that they are not longer "part" of the parent div. In your case the outer div has no height because the children are put absolute on the page.
The mouseover property delegates down. So the parent listens and all it's children. Read up on this one for more information how a event listener works https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
This was happen because your outer div height was small compare to image size. so when you move mouse outside outer div back event is called. just give some minimum height to outer div. here is working code
var left = document.getElementById("left");
var right = document.getElementById("right");
function curtain() {
left.style.transform = "rotate(30deg)";
right.style.transform = "rotate(-30deg)";
}
function back() {
left.style.transform = "rotate(0deg)";
right.style.transform = "rotate(0deg)";
}
#animate {
width: 250px;
margin: auto;
position: relative;
height: 225px;
border: 1px solid;
}
img {
width: 100%;
}
#left {
position: absolute;
top: 0;
right: 50%;
padding: 0;
margin: 0;
width: 50%;
transform-origin: 0 0;
}
#right {
position: absolute;
top: 0;
right: 0%;
padding: 0;
margin: 0;
width: 50%;
transform-origin: 100% 0;
}
<div id="animate" onmouseover="curtain()" onmouseout="back()">
<div id="left">
<img src="http://www.uwphotographyguide.com/images/Articles/image-overlay-3107.jpg">
</div>
<div id="right">
<img src="http://s3.freefoto.com/images/15/78/15_78_19_web.jpg">
</div>
</div>
As per my understanding, I think you want that if you mouse over on child div no effects will be on child div, It should happens only when you mouse over on parent div.
This is happening because child div is inside the scope of parent div.
So you have to manage these div. Adjust the height of parent div and it will works fine.

Categories