Hello ,
Here is my ‘almost work’ example:
https://vmars.us/Guitar/GuitarScales-FORUM.html
Node gets attached to "div id=“two” ’ instead of to ‘div id=“container”’ .
I’ve tried all kinds of combinations , but so far no-go .
I’ve given up on several approaches . Please help !
When I get it working it will look like this:
https://vmars.us/Guitar/GuitarScales-Beginning.html
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
<title>Clone Elements</title>
<style>
body {
margin: 20px;
}
#container {
width: 1375px;
height: 450px;
background-color: #DCDCDC;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
border-radius: 7px;
touch-action: none;
z-index: 0
}
.tr{
height: 55px;
}
.item {
border-radius: 50%;
touch-action: none;
user-select: none;
position: relative;
}
.two {
width: 32px;
height: 32px;
background-color: #F5F5F5; // whitesmoke
font-family: Arial, Helvetica, sans-serif;
text-align:center;
font-size:28px;
top: -40%;
left: -10%;
z-index: 2
}
#p1 {
position: absolute;
top: 275px;
left: 50px;
width: 90%;
border-style: solid;
}
.item:active {
opacity: .75;
}
.item:hover {
cursor: pointer;
}
h1 {
margin-bottom: 10px;
}
</style>
</head>
<body>
<!-- <h1>Drag Multiple Elements</h1> -->
<div id="outerContainer">
<div id="container">
<br> <!-- <div id="myNote" onclick="createNewNote()" class="circle";
<div id="myNote" onclick="createNewNote()" class="circle";
-->
<div class="item two" id="two" onclick="cloneTwo()" >2</div>
</div> <!-- id="container" -->
</div> <!-- id="outerContainer -->
<!-- Console-error says: "two.cloneNode is not a function:
and: "Cannot read properties of null (reading 'cloneNode')"
-->
<script>
//const init =
function cloneTwo() {
let t2 ;
let temp ;
let divTwo
let cln
t2 = document.getElementById('two');
divTwo = document.querySelector('two');
t2.appendChild(two.cloneNode(true) );
//}
temp = document.querySelector('[type="text/html"]');
cln = temp.cloneNode(true);
divTwo = cln.textContent;
t2.innerHTML = divTwo;
}
// document.addEventListener('DOMContentLoaded', init)
</script>
<script>
var container = document.querySelector("#container");
var activeItem = null;
var active = false;
container.addEventListener("touchstart", dragStart, false);
container.addEventListener("touchend", dragEnd, false);
container.addEventListener("touchmove", drag, false);
container.addEventListener("mousedown", dragStart, false);
container.addEventListener("mouseup", dragEnd, false);
container.addEventListener("mousemove", drag, false);
function dragStart(e) {
if (e.target !== e.currentTarget) {
active = true;
// this is the item we are interacting with
activeItem = e.target;
if (activeItem !== null) {
if (!activeItem.xOffset) {
activeItem.xOffset = 0;
}
if (!activeItem.yOffset) {
activeItem.yOffset = 0;
}
if (e.type === "touchstart") {
activeItem.initialX = e.touches[0].clientX - activeItem.xOffset;
activeItem.initialY = e.touches[0].clientY - activeItem.yOffset;
} else {
console.log("doing something!");
activeItem.initialX = e.clientX - activeItem.xOffset;
activeItem.initialY = e.clientY - activeItem.yOffset;
}
}
}
}
function dragEnd(e) {
if (activeItem !== null) {
activeItem.initialX = activeItem.currentX;
activeItem.initialY = activeItem.currentY;
}
active = false;
activeItem = null;
}
function drag(e) {
if (active) {
if (e.type === "touchmove") {
e.preventDefault();
activeItem.currentX = e.touches[0].clientX - activeItem.initialX;
activeItem.currentY = e.touches[0].clientY - activeItem.initialY;
} else {
activeItem.currentX = e.clientX - activeItem.initialX;
activeItem.currentY = e.clientY - activeItem.initialY;
}
activeItem.xOffset = activeItem.currentX;
activeItem.yOffset = activeItem.currentY;
setTranslate(activeItem.currentX, activeItem.currentY, activeItem);
}
}
function setTranslate(xPos, yPos, el) {
el.style.transform = "translate3d(" + xPos + "px, " + yPos + "px, 0)";
}
</script>
</body>
</html>
Thanks
You have a couple errors here.
querySelector uses a css selector syntax, so you’ll need to use .two or #two in it.
two can’t be cloned because it hasn’t been initialized yet.
I stripped it down a bit, and this does what I think you’re looking for. Styling and such it up to you but the basics are here. This assumes:
The parent object is display flex, set to show the content in a row
The item class is only on these objects - if you use it elsewhere, the query selector needs to be tweaked (something like “#container .item”
Thanks Dave ,
Very cool indeed , I added it to my “study this” Folder .
Sorry I didn’t explain my Post very well .
Here’s what the container will look like :
<div id="container">
<br>
<div class="item one" id="one">1</div>
<div class="item two" id="two">2</div>
<div class="item two" id="three">3</div>
<div class="item two" id="four" >4</div>
<div class="item two" id="five" >5</div>
<div class="item two" id="six">6</div>
<div class="item two" id="seven">7</div>
<div class="item two" id="nine">9</div>
</div> <!-- id="container" -->
When I click on 1 , a new 1 should be cloned .
When I click on 2 , a new 2 should be cloned …and so on .
You can get an idea of what I am up to here :
https://vmars.us/Guitar/GuitarScales-Beginning.html
Thanks
Ok Dave , I think I’m getting the hang of it : )
<!DOCTYPE html>
<html>
<title>
https://vmars.us/Guitar/myDrink-Clone.html
</title>
<body>
<h1>The Element Object</h1>
<h2>The cloneNode() Method</h2>
<button onclick="cloneOne()">Coffee</button>
<br>
<button onclick="cloneTwo()">Tea</button>
<div class="item one" id="one"><p>Coffee</p></div>
<div class="item two" id="two"><p>Tea</p></div>
<script>
function cloneOne() {
const node = document.getElementById("one").lastChild;
const clone = node.cloneNode(true);
document.getElementById("one").appendChild(clone);
}
</script>
<script>
function cloneTwo() {
const node = document.getElementById("two").lastChild;
const clone = node.cloneNode(true);
document.getElementById("two").appendChild(clone);
}
</script>
</body>
</html>
You don’t need a separate method for each div as it will quickly become a maintenance nightmare - the version I gave you is still a better approach. I’ve tweaked it to create a copy and put it right after the clicked one. It does not change the inner text.
One unfortunate side effect here is this creates two object with the same id, which is a no-no, and will be noticeable if you change the text of the cloned object and try to clone that one. Because getElementById will pull the first instance of an id, it’ll always grab the first instance, which may not be what you want.
So you’re going to need to find a way to create unique ids which work for you if you want to clone and change objects.
DaveMaxwell:
One unfortunate side effect here is this creates two object with the same id, which is a no-no, and will be noticeable if you change the text of the cloned object and try to clone that one. Because getElementById will pull the first instance of an id, it’ll always grab the first instance, which may not be what you want.
Just having a play with this. The following could do with some refactoring
function clone(evt) {
// evt.target is the clickedObject
const clickedObject = evt.target;
const parent = clickedObject.parentElement;
const id = clickedObject.id;
const existingClones = parent.querySelectorAll(`[data-clone-id='${id}']`);
const numOfClones = existingClones.length
const cloneId = `${id}_${String(numOfClones + 1).padStart(2, '0')}`
const clonedObject = clickedObject.cloneNode(true);
clonedObject.dataset.cloneId = id
clonedObject.setAttribute('id', cloneId)
clonedObject.addEventListener('click', clone);
// if clones exist insert after the last clone
const afterElement = (numOfClones > 0)
? existingClones[numOfClones - 1]
: clickedObject
// add cloned object directly after clicked object
afterElement.after(clonedObject);
}
For each clone a dataset attribute is set with a reference to the source element e.g.
<div class="item" id="two">2</div>
<div class="item" id="two_01" data-clone-id="two">2</div>
In addition the id is set with a suffix which is created based on the number of clones of that source element.
Just an idea
A bit of a tidy up
window.addEventListener('DOMContentLoaded', () => {
const getCloneId = (id, num, pad = 2) => (`${id}_${String(num).padStart(pad, '0')}`)
const getClone = (sourceElem, numOfClones = 0) => {
const clonedObject = sourceElem.cloneNode(true);
const id = sourceElem.id;
clonedObject.dataset.cloneId = id;
clonedObject.id = getCloneId(id, numOfClones + 1);
clonedObject.addEventListener('click', clone);
return clonedObject;
}
function clone(evt) {
const sourceElem = evt.target;
const parent = sourceElem.parentElement;
const existingClones = parent.querySelectorAll(`[data-clone-id='${sourceElem.id}']`);
const numOfClones = existingClones.length
const clonedObject = getClone(sourceElem, numOfClones)
// if clones exist insert after the last clone
const elementToFollow = (numOfClones > 0)
? existingClones[numOfClones - 1]
: sourceElem
// add cloned object directly after clicked object
elementToFollow.after(clonedObject);
}
document.querySelectorAll('.item')
.forEach((item) => item.addEventListener('click', clone));
})
Thanks Dave ,
I love the Vanilla JS .
I have changed three things :
<div class="item" id="one" style="background-color: #F50000;" >1</div>
item.addEventListener("dblclick", clone, true);
// clone it, add click event to it.
let clonedObject = clickedObject.cloneNode(true);
clonedObject.addEventListener("dblclick", clone);
Ie., “dblclick” to clone , and single click to move balls around .
Thanks rpg_digital ,
Now I know how to setAttribute ,
and happy to know that a cloned element inherits all attributes ,
and can be changed .
clonedObject.setAttribute('id', cloneId)
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
I am having issues with
positioning : the round elements show as stacked , I want them inline .
the Notes element is specified as draggable=“false” , but it is draggable .
the biggest problem is that when a ball is cloned and moved into position ,
and then a new ball is cloned , it repositions all the balls .
I am thinking that I could pre-make 10 balls of each type
then stack each type on top of themselves ;
When the top ball is dragged off , there is another one waiting underneath .
I know that that is low-tech , but it might work .
I don’t want to wear you folks out ,
so should I start a new Post for each issue ?
Here is where things stand so far .
https://vmars.us/Guitar/Guitar-onDBLclick-RED-add-Table-Need-Help.html
Thanks
Your code makes no distinction or exception for this attribute. It’s not using the HTML Drag and Drop API, so draggable
has no weight here (unless you add something to give it effect).
Thanks m_hutley for your reply :
Still , it is draggable .
Why is it draggable
and how can I stop it from being draggable ??
https://vmars.us/Guitar/Guitar-onDBLclick-RED-add-Table-Need-Help.html
Thanks
<div>s
Well I have made some headway .
Stacking <div>s , so far , is the only way I came up with
to separate <div>s movements from affecting other <div>s .
If anyone has a more high-tech way , I would sure like to know it .
Here’s what things look like now
https://vmars.us/Guitar/Guitar-7x2-Circles-DragDrop-1-Row-Table.html
I am still troubled that <p> is still Draggable .
So I will open up a new Post for that .
Thanks All
system
Closed
February 7, 2023, 3:49am
14
This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.