querySelectorAll을 사용하여 직계 자식 검색


119

나는 이것을 할 수있다 :

<div id="myDiv">
   <div class="foo"></div>
</div>
myDiv = getElementById("myDiv");
myDiv.querySelectorAll("#myDiv > .foo");

즉, myDivclass 가있는 요소 의 모든 직계 자식을 성공적으로 검색 할 수 있습니다 .foo.

문제는 요소 #myDiv에 대해 쿼리를 실행하고 있기 때문에 선택자에를 포함해야한다는 myDiv점입니다 (분명히 중복 됨).

나는 #myDiv꺼둘 수 있어야 하지만 선택자는 >.

누구든지 선택기가 실행되는 요소의 직접 자식을 가져 오는 선택기를 작성하는 방법을 알고 있습니까?



필요한 것을 달성 한 답변이 없습니까? 피드백을 제공하거나 답변을 선택하십시오.
Randy Hall

@mattsh-귀하의 필요에 대한 더 나은 (IMO) 답변을 추가했습니다. 확인하십시오!
csuwldcat 2013-07-29

답변:


85

좋은 질문. 요청을 받았을 당시 "결합 자 기반 쿼리"( John Resig가 호출 한 쿼리)를 수행하는 보편적으로 구현 된 방법 은 존재하지 않았습니다.

이제 : scope 의사 클래스가 도입되었습니다. 그것은되지 지원 에지 또는 IE의 [중고 Chrominum] 버전에 있지만, 이미 몇 년 동안 사파리에서 지원하고있다. 이를 사용하면 코드가 다음과 같이 될 수 있습니다.

let myDiv = getElementById("myDiv");
myDiv.querySelectorAll(":scope > .foo");

어떤 경우에는 .querySelectorAll다른 좋은 구식 DOM API 기능을 건너 뛰고 사용할 수도 있습니다. 예를 들어, 대신에 myDiv.querySelectorAll(":scope > *")를 쓸 수 있습니다 myDiv.children.

그렇지 않으면 아직 의존 할 수 :scope없다면 더 많은 사용자 정의 필터 로직 (예 : find myDiv.getElementsByClassName("foo")who .parentNode === myDiv) 을 추가하지 않고 상황을 처리하는 다른 방법을 생각할 수 없으며 실제로 하나의 코드 경로를 지원하려는 경우 이상적이지 않습니다. 임의의 선택기 문자열을 입력으로 사용하고 일치 목록을 출력으로 사용하려고합니다! 하지만 저처럼 "당신이 가진 모든 것이 망치였다"고 생각했기 때문에이 질문을하게 되었다면 DOM이 제공 하는 다양한 다른 도구 가 있다는 것을 잊지 마십시오 .


3
myDiv.getElementsByClassName("foo")와 동일하지 않으며, 자식이 아닌 모든 자손을 찾는 점에서 (실제로 작동하는) myDiv.querySelectorAll("> .foo")더 비슷 합니다. myDiv.querySelectorAll(".foo").foo
BoltClock

오, 귀찮게! 내 말은 당신 말이 맞아요. OP의 경우에는 그다지 좋지 않다는 것을 더 분명히하기 위해 내 대답을 업데이트했습니다.
natevw

링크 (확실히 대답하는 것 같음)에 감사 드리며 "결합 자 기반 쿼리"라는 용어를 추가해 주셔서 감사합니다.
mattsh

1
John Resig가 "결합 자 기반 쿼리"라고 부르는 것을 선택기 4는 이제 상대 선택 자라고 부릅니다 .
BoltClock

@Andrew Scoped 스타일 시트 (즉 <style scoped>)는 :scope이 답변에 설명 된 의사 선택자 와 관련이 없습니다 .
natevw

124

누구든지 선택기가 실행되는 요소의 직접 자식을 가져 오는 선택기를 작성하는 방법을 알고 있습니까?

현재 요소에 "루팅 된"선택기를 작성하는 올바른 방법은를 사용하는 것 :scope입니다.

var myDiv = getElementById("myDiv");
var fooEls = myDiv.querySelectorAll(":scope > .foo");

그러나 브라우저 지원은 제한적 이며 사용하려면 shim이 필요합니다. 이 목적을 위해 scopedQuerySelectorShim 을 만들었 습니다 .


3
:scope사양이 현재 "작업 초안"이므로 변경 될 수 있다는 점을 언급 할 가치가 있습니다. 그것이 채택된다면 그것은 여전히 ​​이와 같이 작동 할 것입니다.
Zach Lysobey

2
그것처럼 보인다는 그 동안 사용되지 않습니다
아드리안 Moisa을

1
3 년 후 당신은 나의 대둔근을 구합니다!
Mehrad

5
@Adrian Moisa : : scope는 어디에도 사용되지 않습니다. 범위가 지정된 속성과 혼동 될 수 있습니다.
BoltClock

6

다음은 바닐라 JS로 작성된 유연한 메서드로, 요소의 직접 자식에 대해서만 CSS 선택기 쿼리를 실행할 수 있습니다.

var count = 0;
function queryChildren(element, selector) {
  var id = element.id,
      guid = element.id = id || 'query_children_' + count++,
      attr = '#' + guid + ' > ',
      selector = attr + (selector + '').replace(',', ',' + attr, 'g');
  var result = element.parentNode.querySelectorAll(selector);
  if (!id) element.removeAttribute('id');
  return result;
}

생성하는 ID에 일종의 타임 스탬프 또는 카운터를 추가하는 것이 좋습니다. Math.random().toString(36).substr(2, 10)동일한 토큰을 두 번 이상 생성 할 가능성이 0이 아닌 (작지만) 가능성이 있습니다.
Frédéric Hamidi

반복 해시 확률이 얼마나 적을 지 잘 모르겠습니다. ID는 밀리 초 미만의 시간 동안 만 일시적으로 적용되며 모든 복제는 동일한 부모 노드의 자식이어야합니다. 기본적으로 기회는 천문학적입니다. 어쨌든 카운터를 추가하는 것은 괜찮아 보입니다.
csuwldcat 2013

의 의미를 잘 모르겠습니다 any dupe would need to also be a child of the same parent node.id 속성은 문서 전체에 있습니다. 당신이 바로 확률은 여전히 매우 무시할 수 있지만, 카운터 : 높은 도로를 복용하고 추가 주셔서 감사하고
프레데릭 Hamidi

8
JavaScript는 병렬로 실행되지 않으므로 실제로 기회가 없습니다.
WGH

1
@WGH 와우, 믿을 수가 없네요. 저는 그런 간단한 요점에서 방귀를 뀌는 것이 정말 놀랍습니다 (저는 Mozilla에서
일하고이

5

요소가 고유한지 확인하는 경우 (예 : ID가있는 케이스) :

myDiv.parentElement.querySelectorAll("#myDiv > .foo");

보다 "글로벌"솔루션 : ( matchsSelector shim 사용 )

function getDirectChildren(elm, sel){
    var ret = [], i = 0, l = elm.childNodes.length;
    for (var i; i < l; ++i){
        if (elm.childNodes[i].matchesSelector(sel)){
            ret.push(elm.childNodes[i]);
        }
    }
    return ret;
}

elm부모 요소는 어디에 sel있고 선택자는 있습니다. 프로토 타입으로도 사용할 수 있습니다.


@lazd는 질문의 일부가 아닙니다.
Randy Hall

귀하의 대답은 "글로벌"솔루션이 아닙니다. 주목해야 할 특정 제한 사항이 있으므로 내 의견입니다.
lazd

질문에 대답하는 @lazd. 그렇다면 왜 반대표를 던졌습니까? 아니면 1 년 전의 답변에 대해 반대표를 던진 후 몇 초 이내에 댓글을 달았습니까?
Randy Hall

이 솔루션은 matchesSelectorunprefixed (최신 버전의 Chrome에서도 작동하지 않음)를 사용하고 전역 네임 스페이스를 오염 시키며 (ret 선언되지 않음) querySelectorMethods가 예상하는 것과 같은 NodeList를 반환하지 않습니다. 나는 그것이 좋은 해결책이라고 생각하지 않습니다.
lazd

@lazd-원래 질문은 접두사가없는 함수와 전역 네임 스페이스를 사용합니다. 주어진 질문에 대한 완벽하게 훌륭한 해결 방법입니다. 좋은 해결책이 아니라고 생각되면 사용하지 않거나 편집을 제안하십시오. 기능적으로 부정확하거나 스팸이라면 반대 투표를 고려하십시오.
Randy Hall

2

다음 솔루션은 지금까지 제안 된 솔루션과 다르며 저에게 효과적입니다.

이유는 일치하는 모든 하위 항목을 먼저 선택한 다음 직계 하위 항목이 아닌 항목을 필터링하는 것입니다. 동일한 선택자를 가진 일치하는 상위가없는 경우 하위는 직계 하위입니다.

function queryDirectChildren(parent, selector) {
    const nodes = parent.querySelectorAll(selector);
    const filteredNodes = [].slice.call(nodes).filter(n => 
        n.parentNode.closest(selector) === parent.closest(selector)
    );
    return filteredNodes;
}

HTH!


나는 간결함과 접근 방식의 단순성 때문에 이것을 좋아하지만, 큰 나무와 지나치게 욕심 많은 선택기로 높은 빈도로 호출하면 잘 수행되지 않을 가능성이 큽니다.
브레 난영 dec

1

이 상황을 처리하는 함수를 만들었고 공유 할 것이라고 생각했습니다.

getDirectDecendent(elem, selector, all){
    const tempID = randomString(10) //use your randomString function here.
    elem.dataset.tempid = tempID;

    let returnObj;
    if(all)
        returnObj = elem.parentElement.querySelectorAll(`[data-tempid="${tempID}"] > ${selector}`);
    else
        returnObj = elem.parentElement.querySelector(`[data-tempid="${tempID}"] > ${selector}`);

    elem.dataset.tempid = '';
    return returnObj;
}

본질적으로 당신이하고있는 것은 임의의 문자열을 생성하는 것입니다 (여기서 randomString 함수는 가져온 npm 모듈이지만 직접 만들 수 있습니다.) 그런 다음 임의의 문자열을 사용하여 선택기에서 기대하는 요소를 얻도록 보장합니다. 그런 다음 그 >후에 자유롭게 사용할 수 있습니다 .

id 속성을 사용하지 않는 이유는 id 속성이 이미 사용 중일 수 있으며이를 재정의하고 싶지 않기 때문입니다.


0

를 사용하여 요소의 모든 직계 자식을 쉽게 가져올 수 있고를 사용하여 childNodes특정 클래스의 조상을 선택할 수 querySelectorAll있으므로 둘 다 가져 와서 둘을 비교하는 새로운 함수를 만들 수 있다고 상상하기 어렵지 않습니다.

HTMLElement.prototype.queryDirectChildren = function(selector){
  var direct = [].slice.call(this.directNodes || []); // Cast to Array
  var queried = [].slice.call(this.querySelectorAll(selector) || []); // Cast to Array
  var both = [];
  // I choose to loop through the direct children because it is guaranteed to be smaller
  for(var i=0; i<direct.length; i++){
    if(queried.indexOf(direct[i])){
      both.push(direct[i]);
    }
  }
  return both;
}

참고 : 이것은 NodeList가 아닌 Nodes의 배열을 반환합니다.

용법

 document.getElementById("myDiv").queryDirectChildren(".foo");

0

현재 노드에 임시 속성을 할당하여 : scope 의 호환성을 확장 할 수 있다는 점을 추가하고 싶습니다 .

let node = [...];
let result;

node.setAttribute("foo", "");
result = window.document.querySelectorAll("[foo] > .bar");
// And, of course, you can also use other combinators.
result = window.document.querySelectorAll("[foo] + .bar");
result = window.document.querySelectorAll("[foo] ~ .bar");
node.removeAttribute("foo");

-1

나는 함께 갔을 것이다

var myFoo = document.querySelectorAll("#myDiv > .foo");
var myDiv = myFoo.parentNode;

-2

나는 시도조차하지 않고 이것을하고 있습니다. 작동할까요?

myDiv = getElementById("myDiv");
myDiv.querySelectorAll(this.id + " > .foo");

시도해보세요. 아마도 작동하지 않을 수도 있습니다. Apolovies, 그러나 나는 지금 그것을 시도하기 위해 컴퓨터를 사용하지 않습니다 (내 iPhone에서 응답).


3
QSA는 호출되는 요소로 범위가 지정되므로 myDiv와 동일한 ID를 가진 myDiv 내 다른 요소를 찾은 다음 .foo가있는 하위 요소를 찾습니다. `document.querySelectorAll ( '#'+ myDiv.id + '> .foo');와 같이 할 수 있습니다.
아마도
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.