Following leech's answer & some inspiration from David V McKay's impressive 1-liner ... along w/ all the answers utilizing while-loops ...
I used the following for my "drop" events. Since my draggable "tabs" & "item lists" all have different structures w/ different HTML elements, I need to find the parent of my "drop" {target} to make sure that it matches the parent my "dragstart" {target} (for drag & drop, these are 2 different events & 2 different {targets}).
The idea here is to climb back up the HTML DOM until you find the desired node. Enjoy ...
Javascript
document.addEventListener("drop", ({target}) => {
// gotta find the parent to drop orig_itm into
var i = 0;
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* this is the part that answers the OP */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
var target_itm = target;
if(!target_itm.getAttribute('draggable')){
while(!target_itm.parentNode.getAttribute('draggable')){
// since we haven't found the correct node, we move up the tree
target_itm = target_itm.parentNode;
// increment the counter so things don't blow-up on us
i++;
// check the counter & the element's tagName to prevent the while-loop from going WOT
// adjust threshold for your application ... mine gets the job done at 20
if(i > 20){
console.log('i = ' + i);
break;
} else if(target_itm.parentNode.tagName == 'body'){
console.log(i + ' - draggable parent not found. looped back to <body>')
break;
}
}
target_itm = target_itm.parentNode;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
[criteria to make sure selected & target items are on the same parentNode]
[move items around]
});
If you remove the unnecessary remarks (shown to help clarify the code) & consolidate the counter/body check w/ an '&&' operator & only the break;
, this code can be compacted down to about 8-ish lines w/out requiring nested function calls & takes the most direct path w/out having to load all the matching selector elements.
I usually just .getElementById()
& rarely search for parents above the previous tier. I only need this feature for my "draggable" items, but if I want to make it available throughout my code, I can (like others have already answered) simply embed the code in a
function getParentByAttribute(itm,attr){
[relevent code from above]
return target_itm;
}
... then just call reqd_node = getParentByAttribute([desired_elem],[desired_attr]);
from wherever needed.