2014-02-28 66 views
14

這是我試圖查詢的標記。所以給出的標記:DOM /純JavaScript解決方案jQuery.closest()實現?

<table class="non-unique-identifier table"> 
<tr><td><div id="unique-identifier"></div></td></tr> 
</table> 

我查詢#唯一標識符:

var myDiv = document.getElementById('#unique-identifier'); 

然後我試圖選擇表。問題是,我想使代碼不易碎,所以我並不需要這樣做:

var myDiv = document.getElementById('#unique-identifier'), 
    myTable = myDiv.parentNode.parentNode.parentNode.parentNode; 

我的問題

是否有當前DOM實現了jQuery相當於$的( ).closest()?不嵌套for循環的效率最高的實現將是首選。

限制

我需要不使用jQuery或嘶嘶對於此問題或引入任何新庫。代碼也很古老。因此,這就是造成這種限制的原因以及<tables>的存在。

+0

困難的事情將是選擇。你打算只找到類,或者通過特定的語法找到某些東西? – MasNotsram

+0

你可以使用[Sizzle](http://sizzlejs.com/)嗎? – leaf

+0

不允許使用嘶嘶聲。但我希望我能。 – chrisjlee

回答

32

你不能沒有一個循環做到這一點:

function closest (el, predicate) { 
    do if (predicate(el)) return el; 
    while (el = el && el.parentNode); 
} 

嗯,其實你可以使用遞歸性(一種變相的環):

function closest(el, predicate) { 
    return predicate(el) ? el : (
    el && closest(el.parentNode, predicate) 
); 
} 

的演示(使用Sizzle爲DOM查詢):

// s = selectors 
 
// n = number of selectors 
 
// get closest s[i+1] from s[i] 
 
// where 0 <= i < n and i % 2 = 0 
 

 
function main (s) { 
 
    var i, el, from; 
 
    var n = s.length; 
 
    for (i = 0; i < n; i += 2) { 
 
    from = Sizzle(s[i])[0]; 
 
    el = closest(from, function (el) { 
 
     return !!el && el !== document && (
 
     Sizzle.matchesSelector(el, s[i + 1]) 
 
    ); 
 
    }); 
 
    console.log(el); 
 
    } 
 
} 
 

 
function closest (el, predicate) { 
 
    do if (predicate(el)) return el; 
 
    while (el = el && el.parentNode); 
 
} 
 

 
main([ 
 
    "#winner" , "b", 
 
    "#winner" , "p", 
 
    "#winner" , "div", 
 
    "#winner" , "div:not(#trump)", 
 
    "#winner" , "#clinton", 
 
    "#looser" , "html" 
 
]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/sizzle/1.10.18/sizzle.min.js"></script> 
 

 
<div id="main"> 
 
    <div id="trump"> 
 
    <p>Donald <b id="winner">Trump</b></p> 
 
    </div> 
 
    <div id="clinton"> 
 
    <p>Hillary <b>Clinton</b></p> 
 
    </div> 
 
</div>

+0

'return el.classList.contains('item')'在有多個className的情況下會更好。 – korsun

2

function closestById(el, id) { 
 
    while (el.id != id) { 
 
    el = el.parentNode; 
 
    if (!el) { 
 
     return null; 
 
    } 
 
    } 
 
    return el; 
 
} 
 

 
// Use it like: 
 

 
yourTarget = closestById(document.getElementById('unique-identifier'),'targetId') 
 
alert(yourTarget.id);
<div id="targetId"> 
 
    Finish 
 
    <div> 
 
    <div id="unique-identifier"> 
 
     Start 
 
    </div> 
 
    </div> 
 
</div>

此向上搜索,直到一定的ID找到。您還可以修改代碼以查找某些類。

2

另一種是遞歸函數。這與我搜索孩子時最接近的略有不同,我不確定是否最接近。

function closest(elem) { 
    if(elem.className.indexOf("non-unique-identifier")) { 
     return elem; 
    } 

    var parent = elem.parentNode; 

    for(var i = 0; i< parent.children.length; i++) { 
     if(parent.children[i].className.indexOf("non-unique-identifier")!=-1) { 
      return parent.children[i]; 
     } 
    } 

    return closest(parent); 
} 



var elem = document.getElementById('unique-identifier'); 

var cl = closest(elem); 

console.log(cl); 

非兒童搜索例如(更像是最接近):

function closest(elem) { 
    if(elem.className.indexOf("non-unique-identifier")) { 
     return elem; 
    } 

    var parent = elem.parentNode; 

    if(parent.className.indexOf("non-unique-identifier")!=-1) { 
     return parent; 
    }  

    return closest(parent); 
} 



var elem = document.getElementById('unique-identifier'); 

var cl = closest(elem); 

console.log(cl); 
1
<div class="item"> 
    <div class="itemed"> 
    <p>Hello <b class="itemed" id="world">World</b></p> 
    </div> 
</div> 

function closest(el, classname) { 
    if(el.parentNode){ 
     if(el.parentNode.className == classname){ 
     return el.parentNode; 
     } 
     else{ 
     return closest(el.parentNode, classname); 
     } 
    } 
    else{ 
    return false; 
    } 
} 

var world = document.getElementById('world'); 
var closest = closest(world, 'item'); 
console.log(closest); 
2

簡潔和快速(與Benchmark.js測試)的方式來尋找任何CSS選擇最接近的元素:

var ep = Element.prototype; 
ep.matches = ep.matches || ep.webkitMatchesSelector || ep.msMatchesSelector || ep.mozMatchesSelector; 

function getClosest(elem, selector) { 
    while (elem !== document.body) { 
     elem = elem.parentElement; 
     if (elem.matches(selector)) return elem; 
    } 
} 

支持IE9 +和其他瀏覽器喲你可以期待關心。

1

我在打字稿項目之一寫了這個簡單的功能,所以我用parentNode querySelector這樣你就可以傳遞給函數classidtag name

findParentNode(el, selector:string):Element | null { 
    let found = null; 
    let child = el; 
    let childSelector = guessSelector(child); 

    while(child !== document && found === null) { 
    child = child.parentNode; 
    childSelector = guessSelector(child); 
    found = childSelector ? child.parentNode.querySelector(`${childSelector} > ${selector}`) : null; 
    } 

    return found; 

    function guessSelector(child:any):string { 
    childSelector = child.className ? `.${child.className.replace(' ', '.')}` : null; 

    if (typeof child.getAttribute === 'function') { 
     childSelector = !childSelector ? 
     (child.getAttribute('id') ? `#${child.getAttribute('id')}` : null) : childSelector; 

     childSelector = !childSelector ? 
     child.tagName.toLowerCase() : childSelector; 
    } 

    return childSelector; 
    } 
} 

例子:

如果您想找到最接近的父母target其中有.param-input班,你可以這樣做:

document.body.addEventListener('click', (e) => { 
    console.log(findParentNode(e.target, '.param-input')); 
});