I am using drag and drop so that students can take pieces of a jumbled up image and drop them on to a grid to recompose the original image.
Both grids (jumbled up and reconstruction) allow for dropping, and everything works fine.
However, there might be cases where a student drops one object on top of another, and I want to have the code move the original object to the one recently vacated by the selected item.
I have experimented with different concepts and might be going down the wrong route but used the following for testing:
function drop(ev) {
ev.preventDefault();
var tgt = ev.dataTransfer.getData("text");
var currentContents = document.getElementById(tgt);
ev.target.appendChild(document.getElementById(tgt));
var newContents = document.getElementById(tgt);
alert("Current = " + currentContents + " - New = " + newContents);
}
The idea here was that I could simply store the element details itself, or the innerHTML into a variable, allow the drop to happen as it would normally, then assign the captured HTML to the previous div.
However, the code above only reports [object HTMLimageElement] (even though, in this case, the recipient does not contain any image data).
I tried innerHTML but simply received nothing at all.
And I haven't even begun to work out how to identify the original div that the dragged image came from.
Okay, spent some time exploring all of the values I could make sense of.
I ended up with this:
function drop(ev) {
ev.preventDefault();
var prntId = ev.target.parentNode.id;
var tgt = ev.dataTransfer.getData("text");
var tgtId = ev.target.id;
if (prntId === "targetDiv") {
ev.target.appendChild(document.getElementById(tgt));
imgPos[searchList(tgt)] = tgtId;
} else {
var thought = document.getElementById(prntId);
var orgHTML = thought.innerHTML;
var listPos = imgPos[searchList(tgt)];
thought.removeChild(thought.firstChild);
thought.appendChild(document.getElementById(tgt));
document.getElementById(listPos).innerHTML = orgHTML;
imgPos[searchList(tgtId)] = listPos;
imgPos[searchList(tgt)] = prntId;
}
}
function searchList(searchItem) {for (i = 0; i < 24; i++) {if (imgId[i] === searchItem) {return i;}}}
Not the most beautiful piece of code I have written but functional.
I identified that when the DIV was empty, the ID being returned was the DIV's ID, yet when a child was added, the ID being returned was of the image ID. I could get the HTML img tag in its entirety, so I had everything I need.
Basically, if there is no child element to the DIV, the event target.parentNode.id returns "targetDiv". I, therefore, append a child to the DIV (ID returned by the stored data in dataTransfer), and update the image list with its new position.
However, any other value is the ID of the parent DIV. I, therefore, copy the current HTML in the target DIV into a variable (orgHTML), locate the original position that the tgt image has been dragged from, place the orgHTML data in this, now vacated, DIV.
I then continue with the drop part of the process, once I have removed any children from the DIV, updating the new position of the image within the relevant list.
That is as clear as mud (I am tired and it is late), but it might help someone, one day. :)
Related
I am able to add the elements I want, but after I refresh they disappear. How can I get the elements to save permanently while using insertAdjacementHTML or do I need to use another method?
Sample code:
function itemAdder () {
var header = document.querySelector(".list-group");
header.insertAdjacentHTML("beforeend", '<a>Item 1</a>')
};
document.getElementById("circle-add").addEventListener("click", itemAdder)
Every time an item is added, you might save the container's current innerHTML in localStorage, and on page load, if anything exists in localStorage, populate the container with whatever's stored:
var header = document.querySelector(".list-group");
if (localStorage.headerHTML) header.innerHTML = localStorage.headerHTML;
function itemAdder () {
header.insertAdjacentHTML("beforeend", '<a>Item 1</a>')
localStorage.headerHTML = header.innerHTML;
}
document.getElementById("circle-add").addEventListener("click", itemAdder)
(hopefully you have a way to delete items as well - on deletion, use the same line localStorage.headerHTML = header.innerHTML; to save the HTML again)
I have a book type project. I have functions that all users to add annotations to pages, and if necessary drag and drop to make adjustments in their position. The d & d is working successfully. I have a mouseup event bound to the drag div to capture the new location such as left , top etc...
This works fine, later I trigger a function to index all the divs (annotations) present on the page based on class "rect"
$.each('.rect',function(){
addToIndex(this)
})
I store all the div properties in an index so they can be recreated when the user returns to the page. This works fine if the div has not been dragged. All properties are correct and can be recreated on the page later.
However if the div has been dragged the div still retains the original properties not the new dragged-to location.
For some reason the dragged div is not being updated in the DOM.
I can trace the properties for the div after dropping and they are updated
otherwise the div would not be displayed where it was dropped.
How can I force the dropped object to be updated in the DOM using jQuery ?
Do I have to work directly in the DOM in this case instead of jQuery ?
Okay here is more detail
Once the drag object is dropped I capture position and then want to add this as data to the element...snippet here
// capturing the literal position of element after dragging
var lx = $(obj).position().left - offPos.left
var uy = $(obj).position().top
var ux = $(obj).position().left - offPos.left + $(obj).width()
var ly = $(obj).position().top + $(obj).height()
var rb = new Array(lx,uy,ux-lx,ly-uy)
var rbstr = rb.join(",")
// before dragging rbstr value is 593,31,135,40
console.log(rbstr) // outputs 948,13,135,40
//updating the element with new data here
$(obj).attr("data-rb",rbstr)
console.log(rbstr) // outputs 948,13,135,40 which is correct
// everything here indicates the element has been updated
But when I try to read all the elements with the same class to store their properties the updated element's properties are not updated but just as they where before the drag and drop.
$.each('.rect',function(){
var allData = $(this).data()
console.log($(this).data())
// is correct for all elements except the dragged element
pageAnnots[thisDoc.currentPage].push(allData)
})
Hope this gives more detail about the problem.Note I can resize the 'rect' and the data gets updated just fine.
Seems to me when the 'rect' is dropped it is a proxy, and updating the data is not happening on the original dragged 'rect'
UPDATE: I found the problem - I was assigning data using
$(this).attr('data-x',n)
but then retrieving the data using
$(this)data()
Apparently if you assign data using 'attr' you have to likewise retrieve it using the same 'attr' method
HTML
<body>
<div id="draggable" class="ui-widget-content">
<p>Drag me around</p>
</div>
<input type="Button" name="" value="nextPage" id="next">
</body>
jQuery
$(function() {
$("#draggable").draggable();
$("#next").on("click",function() {
var top = $("#draggable").css("top");
var bottom = $("#draggable").css("bottom");
var left = $("#draggable").css("left");
var right = $("#draggable").css("right");
//send them to next page
});
});
Hope this was what you wanted. The dragable function adds a style attribute to div, if that was what you meant by update DOM.
I am adding a bunch of spans to a div layer with appendchild like so:
var newSent = document.createElement("SPAN");
var current = document.createTextNode(sentInput.value);
newSent.style.backgroundColor = sentColor;
newSent.className = "sentSpan";
newSent.id = count - 1;
newSent.appendChild(current);
outputBox.appendChild(newSent);
Where outputBox is the name of the div layer and sentInput is the current value of an input text box. I need each of these spans to be click-able, and then delete their value from an array. Currently I have an event listener for this:
delCount = 0;
newSent.addEventListener("click", function(){
var x = parseInt(newSent.id);
sentCount.splice(x - delCount, 1);
sentNum.splice(x - delCount, 1);
delCount = delCount + 1;
newSent.remove(x);
});
The idea is that every time I delete a span, the delCount goes up to compensate for the now smaller array.
This doesn't work very well after a couple of deletions... I don't seem to be able to figure out the math or the proper method for handling the changing array. After clicking two or three spans I end up deleting the value in the array that was associated with the span next to it, but successfully removing the span itself. This array is displayed on a graph, so it's vital that it's displayed properly.
I temporarily had the arrays replace the deleted values with "0" which worked very well, but I need them to not show up in the array at all. Any help is greatly appreciated!
EDIT: added a jsfiddle http://jsfiddle.net/eL11o6mu/ though the display looks terrible in a window that small...
I want to be able to click on a specific element, and have it send a value to a textarea. However, I want it to append to a specific row/line of the textarea.
What I am trying to build is very similar to what happens when you click the notes of the fret board on this site: http://www.guitartabcreator.com/version2/ In fact, i want it almost exactly the same as this.
But right now I am really just trying to see how I can target the specific row, as it seems doable based on this website.
Currently I am using javascript to send a value based on clicking a specific element.
Here is the js:
<script type="text/javascript">
function addNote0(text,element_id) {
document.getElementById(element_id).value += text;
}
</script>
This is the HTML that represents the clickable element:
<td> x </td>
This is the textarea:
<textarea rows="6" cols="24" id="tabText" name="text">-
-
-
-
-
-</textarea>
This works fine for sending the value. But it obviously just goes to the next available space. I am a total newb when it comes to javascript, so I am just not sure where to begin with trying to target a specific line.
What I have currently can be viewed here: http://aldentec.com/tab/
Working code:
After some help, here is the final code that made this work:
<script>
function addNote0(text,element_id) {
document.getElementById(element_id).value += text;
var tabTextRows = ['','','','','',''];
$('td').click(function(){
var fret = $(this).index() - 1;
var line = $(this).parent().index() -1;
updateNote(fret, line);
});
function updateNote(fret, line){
var i;
for(i=0;i<tabTextRows.length;i++){
if(i == line) tabTextRows[i]+='-'+fret+'-';
else tabTextRows[i]+='---';
$('#tabText').val(tabTextRows.join('\n'));
}
}}
window.onload = function() {
addNote0('', 'tabText');
};
</script>
Tried to solve this only in JS.
What I did here is use an array to model each row of the textfield (note the array length is 6).
Then I used a jQuery selector to trigger any time a <td> element is clicked which calculates the fret and string that was clicked relative to the HTML tree then calls the updateNote function. (If you change the table, the solution will probably break).
In the update note function, I iterate through the tabTextRows array, adding the appropriate note. Finally, I set the value of the <textarea> to the array joined by '\n' (newline char).
Works for me on the site you linked.
This solution is dependant on jQuery however, so make sure that's included.
Also you should consider using a monospaced font so the spacing doesn't get messed up.
var tabTextRows = ['','','','','',''];
$('td').click(function(){
var fret = $(this).index() - 1;
var line = $(this).parent().index() -1;
updateNote(fret, line);
});
function updateNote(fret, line){
var i;
for(i=0;i<tabTextRows.length;i++){
if(i == line) tabTextRows[i]+='-'+fret+'-';
else tabTextRows[i]+='---';
$('#tabText').val(tabTextRows.join('\n'));
}
}
I wrote the guitartabcreator website. Jacob Mattison is correct - I am using the text area for display purposes. Managing the data occurs in the backend. After seeing your site, it looks like you've got the basics of my idea down.
I have a UL of LI's, each containing an IMG and a SPAN containing some brief text, and my code that is intended to display the SPAN if someone mouses over the IMG, is looking and finding only TEXT nodes.
The code, up to a certain point, is:
drag.mousemove = function (event) {
if (event == null) {
event = window.event;
}
var lowest_distance = +Infinity;
var lowest_distance_index = null;
var root = document.getElementById('path1');
if (!root) {
return;
}
var intermediate = root.childNodes;
var images = [];
var best_image = null;
for (var outer = 0; outer < intermediate.length; ++outer) {
console.log('Outer: ' + intermediate[outer].nodeName);
}
}
And the only thing it ever reports is "Outer: #text". The path1 DIV contains any images that are dragged on to it; from a browser behavior perspective it looks like the images have been successfully dragged and dropped and lost the SPANs along the way. But the console doesn't get on to the inner loop of the child nodes of path1; following what I quoted is an "Inner: " logged traversal of childNodes to an outer childNode, and I don't remember ever seeing an "Inner: " in the logs. When I logged the contents of the nodes, they were all a line break and eight spaces, no tab.
And, in the HTML source, the entire contents between the opening and closing DIV tags for path1 is a line break and eight spaces, no tab.
Is there a sensible way to have a drag and drop that works for images and is intended to move (in this case) a containing LI to the target area, but DOM inspection finding only text nodes? Is there a reason, alternately, that would freeze the initial DOM contents as the only thing that will ever be recognized?