Consider the following markup and script:
<div id="container">
<div draggable="true">
<button type="button" id="button">Drag me!</button>
</div>
<div draggable="true"></div>
<div draggable="true"></div>
<div draggable="true"></div>
<div draggable="true"></div>
</div>
let dragged;
document.getElementById("button").addEventListener("dragstart", function(e)
{
dragged = e.parentNode;
// What to do here?
});
let divs = document.querySelectorAll("#container div");
for (let i=0; i<div.length; i++)
{
divs[i].addEventListener("dragleave", function(e)
{
if (this.nextSibling == dragged) // move downwards
{
this.parentNode.insertBefore(dragged, this);
}
else // move upwards
{
this.parentNode.insertBefore(dragged, this.nextSibling);
}
});
}
I have several of these div elements below each other inside a container. The button element is only appended to one div at a time.
1) I want the div element with the button inside it to pop out of its place and follow the mouse cursor on drag.
2) When the div moves on top of a neighbor div, I want that div to slide into the empty space.
Is there a vanilla JS solution?
From MDN, posting here because OP owner cannot access.
var dragged;
/* events fired on the draggable target */
document.addEventListener("drag", function(event) {
}, false);
document.addEventListener("dragstart", function(event) {
// store a ref. on the dragged elem
dragged = event.target;
// make it half transparent
event.target.style.opacity = .5;
}, false);
document.addEventListener("dragend", function(event) {
// reset the transparency
event.target.style.opacity = "";
}, false);
/* events fired on the drop targets */
document.addEventListener("dragover", function(event) {
// prevent default to allow drop
event.preventDefault();
}, false);
document.addEventListener("dragenter", function(event) {
// highlight potential drop target when the draggable element enters it
if (event.target.className == "dropzone") {
event.target.style.background = "purple";
}
}, false);
document.addEventListener("dragleave", function(event) {
// reset background of potential drop target when the draggable element leaves it
if (event.target.className == "dropzone") {
event.target.style.background = "";
}
}, false);
document.addEventListener("drop", function(event) {
// prevent default action (open as link for some elements)
event.preventDefault();
// move dragged elem to the selected drop target
if (event.target.className == "dropzone") {
event.target.style.background = "";
dragged.parentNode.removeChild(dragged);
event.target.appendChild(dragged);
}
}, false);
#draggable {
width: 200px;
height: 20px;
text-align: center;
background: white;
}
.dropzone {
width: 200px;
height: 20px;
background: blueviolet;
margin-bottom: 10px;
padding: 10px;
}
<div class="dropzone">
<div id="draggable" draggable="true" ondragstart="event.dataTransfer.setData('text/plain',null)">
This div is draggable
</div>
</div>
<div class="dropzone"></div>
<div class="dropzone"></div>
<div class="dropzone"></div>
Related
Everything works here but I need to keep moving act up and down while mouse button is pressed, without repeated clicks.
Any help?
$('button').on('click', function(){
let a = $('.act');
a.insertBefore(a.prev());
});
$('button').on('contextmenu', function(e){
e.preventDefault();
let a = $('.act');
a.insertAfter(a.next());
});
.act{background:gold;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<div class='title act'>LOREM</div>
<div class='title'>IPSUM</div>
<div class='title'>DOLOR</div>
<div class='title'>SIT</div>
<div class='title'>AMET</div>
</div>
<br>
<button>CLICK</button>
Instead of the click and contextmenu events you'll have to use mouse events, here is an example:
let intervalId;
const a = $('.act');
$('button').on('mousedown', function(event) {
function fn () {
if (event.button == 0) {
a.insertBefore(a.prev());
} else if (event.button == 2) {
a.insertAfter(a.next());
}
return fn;
};
intervalId = setInterval(fn(), 500);
});
$(document).on('mouseup', function(event) {
clearInterval(intervalId);
});
$('button').on('contextmenu', function(event) {
event.preventDefault();
});
.act {
background: gold;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<div class='title act'>LOREM</div>
<div class='title'>IPSUM</div>
<div class='title'>DOLOR</div>
<div class='title'>SIT</div>
<div class='title'>AMET</div>
</div>
<br>
<button>CLICK</button>
In this example, I'm using an interval to move the element every 500 milliseconds while the mouse pointer is down, I'm also preventing the contextmenu event so that the context menu will not consume the mouseup event itself.
I've made a fiddle to illustrate:
https://jsfiddle.net/10cgxohk/
html:
<p class="">x</p>
css:
.move {
animation: MoveUpDown 1s linear infinite;
position: relative;
left: 0;
bottom: 0;
}
#keyframes MoveUpDown {
0%, 100% {
bottom: 0;
}
50% {
bottom: 15px;
}
}
javascript:
$('body').on('mousedown', function() {
$('p').addClass('move');
$('body').on('mouseup', function() {
$('p').removeClass('move');
$('body').off('mouseup');
console.log('here');
})
});
This is really rough and creates an issue if you have other 'mouseup' callbacks on the body, but if that's not a worry for you then it should work. The javascript is adding a class to the element, and the class is animated in css
I'm working on javascript piano app, I need when user click on key 83 the piano button hover but after the user releases the key the button get back to its original color
I tried to add the style. background = 'green'; but it changes the button color forever
var a = document.getElementById("mydo"); /* div 'piano button' */
if(key===83){
DO();
a.style.background = 'gold'; /* This Wrong Change color forver */
}
I need something like hover when I use the mouse to click the button which means the color change while I hold the key and back transparent again after I finish
You can use the mousedown and mouseup events to change the background-color of your item
const keys = document.getElementsByClassName("key");
for (let i = 0; i < keys.length; i++) {
const key = keys.item(i);
key.addEventListener("mousedown", function() {
this.style.backgroundColor = "gold";
});
key.addEventListener("mouseup", function() {
this.style.backgroundColor = "ghostwhite";
});
}
.key {
display: inline-block;
width: 50px;
height: 175px;
background-color: ghostwhite;
border: 1px solid black;
text-align: center;
}
<div id="do" class="key">do</div>
<div id="re" class="key">re</div>
<div id="mi" class="key">mi</div>
<div id="fa" class="key">fa</div>
<div id="sol" class="key">sol</div>
<div id="la" class="key">la</div>
<div id="si" class="key">si</div>
Use event keydown and key up to set desired values of the styling.
var a = document.getElementById("mydo");
document.onkeydown = function(event){
if(event.keyCode === 83){
a.style.background = 'gold'
};
};
document.onkeyup = function(event){
if(event.keyCode === 83){
a.style.background = 'transparent'}; /* the starting color */
}
I know the question of closing a pop-up by clicking outside of it has been asked before. I have a somewhat complex pop-up and the solution offered by Phillip Walton isn't working for me.
His code simply made my page blurry but stopped the popup from appearing.
$(document).on('click', function(event) {
if (!$(event.target).closest('.maincontainer').length) {
popup.classList.remove('popup--open');
popup.style.display = 'none';
popupAccessory.style.display = 'none';
popupAccessory.classList.remove('popup--accessory--open');
maincontainer.classList.remove('blurfilter');
}
});
I also tried:
window.addEventListener('click', function(event) {
if (event.target != popup) {
popup.classList.remove('popup--open');
popup.style.display = 'none';
popupAccessory.style.display = 'none';
popupAccessory.classList.remove('popup--accessory--open');
maincontainer.classList.remove('blurfilter');
}
}, true);
This closes the popup when I click anywhere, including on the popup itself. I want it to close only when I click on part of the screen that isn't the popup.
The code to open the popup:
function openpopup() {
popup.style.display = 'initial';
setTimeout(function(){
popup.classList.add('popup--open');
popup.style.boxShadow = '0 0 45px 2px white';
maincontainer.classList.add('blurfilter')}, 10);
for (let i = 0; i < listitems.length; i++ ) {
setTimeout(function() {
listitems[i].classList.add('visible');
}, 100);
}
}
I added the event listener to a button
popupOpenbtn.addEventListener('click', openpopup);
The HTML structure:-
<div class="maincontainer>
...all my page content...
</div>
<div class="popup">
...popup contents...
</div
I would suggest using only css classes to style your popup and use JS only to add, remove and toggle that class. Not sure how close to your working exercise is this fiddle but I've prepared this to show how the document/window click event can be checked to successfully open/close the popup window.
var popupOverlay = document.querySelector('#popup__overlay');
var popupOpenButton = document.querySelector('#popupOpenButton');
var popupCloseButton = document.querySelector('#popupCloseButton');
var mainContainer = document.querySelector('main');
function closestById(el, id) {
while (el.id != id) {
el = el.parentNode;
if (!el) {
return null;
}
}
return el;
}
popupOpenButton.addEventListener('click', function(event) {
popupOverlay.classList.toggle('isVisible');
});
popupCloseButton.addEventListener('click', function(event) {
popupOverlay.classList.toggle('isVisible');
});
mainContainer.addEventListener('click', function(event) {
if (popupOverlay.classList.contains('isVisible') && !closestById(event.target, 'popup__overlay') && event.target !== popupOpenButton) {
popupOverlay.classList.toggle('isVisible');
}
});
#popup__overlay {
display: none;
background-color: rgba(180, 180, 180, 0.5);
position: absolute;
top: 100px;
bottom: 100px;
left: 100px;
right: 100px;
z-index: 9999;
text-align: center;
}
#popup__overlay.isVisible {
display: block;
}
main {
height: 100vh;
}
<aside id="popup__overlay">
<div class="popup">
<h2>Popup title</h2>
<p>
Lorem ipsum
</p>
<button id="popupCloseButton">Close popup</button>
</div>
</aside>
<main>
<div class="buttonWrapper">
<button id="popupOpenButton">Open popup</button>
</div>
</main>
My goal is to have #box2 appear when I click on #box1 but when you click on something other than #box2, it will display none and only #box1 will show.
Here are my 2 boxes, they are just 2 styled divs:
var condition;
$(document).click(function() {
if (condition === 'block') {
$(":not(#box2)").click(function() {
$("#box2").hide();
});
}
})
$('#box1').click(function(e) {
$('#box2').css('display', 'block');
condition = 'block';
});
$('#box2').click(function(e) {
$('#box2').css('display', 'none');
condition = 'none';
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="box1" style="width: 300px; height: 300px; background-color: red; margin-left: 100px; margin-bottom: 50px; position: absolute;">
</div>
<div id="box2" style="width: 300px; height: 300px; background-color: blue; margin-left: 150px; display: none; position: absolute;">
</div>
This current code works correctly the first time but after that, it wont run again. I am just wondering if there is a reset function or where I am going wrong?
Really what I want to do is make this work on an ipad so when the user clicks/taps away from the box, it will close. If there are better ways to do this on the Ipad tablet, please let me know!!
Any ideas?
Don't overcomplicate things. This is all the javascript you need, get rid of everything else:
$(document).click(function () {
$('#box2').hide();
});
$('#box1').click(function (e) {
e.stopPropagation();
$('#box2').show();
});
You could just filter event target at document level:
$(document).on('click', function(e) {
$('#box2').toggle(!!$(e.target).closest('#box1').length);
});
-jsFiddle-
You can listen to all click events of the document and then use the event.target to detect which element is being clicked. if the clicked element is box1 and box2 is not being shown then display it to the user. in any other condition we can hide the box2 if it's not the element being clicked. here is the vanilla JavaScript code to achieve this:
<html>
<body>
<div id='box1'>BOX ONE</div>
<div id='box2' style="display: none;">BOX TWO</div>
<script>
document.addEventListener('click', function(event) {
var secondBox = document.getElementById('box2')
if(event.target.id === 'box1' && secondBox.style.display === 'none'){
secondBox.style.display = 'block'
} else if (event.target.id !== 'box2') {
secondBox.style.display = 'none'
}
})
</script>
</body>
</html>
And if you are into DRY (Do not repeat yourself), you can define a function for this task. Take look at this modified version of the script:
function addOpenHandler(handler, target){
document.addEventListener('click', function(event) {
if(event.target === handler && target.style.display === 'none'){
target.style.display = 'block'
} else if (event.target !== target) {
target.style.display = 'none'
}
})
}
addOpenHandler( document.getElementById('box1'), document.getElementById('box2') )
$(document).click(function () {
if (condition === 'block')
{
$(":not(#box2)").click(function () {
$("#box2").hide();
});
}
})
The line $("#box2").hide(); is firing after every click
Have a problem and can't get to solve it. Tried to use QuerySelectorAll and comma separating with GetElementsByClassName, but that didn't work, so I am wondering how to solve this problem.
I have this HTML:
<div class="area">Test title
<div class="some content" style="display: none">blablbala
<input></input>
</div>
<div class="two">This should be clickable too</div>
</div>
<div class="area">
Test title
<div class="some content">
blablbala
<input></input>
</div>
<div class="two">This should be clickable too</div>
</div>
JS:
function areaCollapse() {
var next = this.querySelector(".content");
if (this.classList.contains("open")) {
next.style.display = "none";
this.classList.remove("open");
} else {
next.style.display = "block";
this.classList.add("open");
}
}
var classname = document.getElementsByClassName("area");
for (var i = 0; i < classname.length; i++) {
classname[i].addEventListener('click', areaCollapse, true);
}
http://jsfiddle.net/1BJK903/nb1ao39k/6/
CSS:
.two {
position: absolute;
top: 0;
}
So now, the div with classname "area" is clickable. I positioned the div with class "two" absolute and now the whole div is clickable, except where this other div is. If you click on the div with classname "two", it doesn't work (it does not collapse or open the contents). How can I make this work, without changing the structure?
One way is using a global handler, where you can handle more than one item by checking its id or class or some other property or attribute.
Below snippet finds the "area" div and pass it as a param to the areaCollapse function. It also check so it is only the two or the area div (colored lime/yellow) that was clicked before calling the areaCollapse.
Also the original code didn't have the "open" class already added to it (the second div group), which mean one need to click twice, so I change the areaCollapse function to check for the display property instead.
function areaCollapse(elem) {
var next = elem.querySelector(".content");
if (next.style.display != "none") {
next.style.display = "none";
} else {
next.style.display = "block";
}
}
window.addEventListener('click', function(e) {
//temp alert to check which element were clicked
//alert(e.target.className);
if (hasClass(e.target,"area")) {
areaCollapse(e.target);
} else {
//delete next line if all children are clickable
if (hasClass(e.target,"two")) {
var el = e.target;
while ((el = el.parentElement) && !hasClass(el,"area"));
if (targetInParent(e.target,el)) {
areaCollapse(el);
}
//delete next line if all children are clickable
}
}
});
function hasClass(elm,cln) {
return (" " + elm.className + " " ).indexOf( " "+cln+" " ) > -1;
}
function targetInParent(trg,pnt) {
return (trg === pnt) ? false : pnt.contains(trg);
}
.area {
background-color: lime;
}
.two {
background-color: yellow;
}
.area:hover, .two:hover {
background-color: green;
}
.some {
background-color: white;
}
.some:hover {
background-color: white;
}
<div class="area">Test title clickable 1
<div class="some content" style="display: none">blablbala NOT clickable 1
</div>
<div class="two">This should be clickable too 1</div>
</div>
<div class="area">Test title clickable 2
<div class="some content">blablbala NOT clickable 2
</div>
<div class="two">This should be clickable too 2</div>
</div>
<div class="other">This should NOT be clickable</div>
You need to find your two elements while you're binding classname, and bind that as well.
var classname = document.getElementsByClassName("area");
for(var i=0; i < classname.length; i++){
classname[i].addEventListener('click', areaCollapse, true);
var twoEl = classname[i].getElementsByClassName("two")[0];
twoEl.addEventListener('click', function(e) { console.log('two clicked'); });
}
If you want to use jQuery:
$('.two').click(function(){
//action here
});