Javascript에서 DOM 노드 목록을 배열로 변환하는 방법은 무엇입니까?


96

HTML 노드 목록을 받아들이는 Javascript 함수가 있지만 Javascript 배열을 기대하고 (일부 Array 메서드를 실행합니다) Document.getElementsByTagNameDOM 노드 목록을 반환하는 출력을 제공하고 싶습니다 .

처음에는 다음과 같은 간단한 것을 사용하려고 생각했습니다.

Array.prototype.slice.call(list,0)

그리고 이것은 모든 브라우저에서 잘 작동합니다. 물론 "JScript object expected"라는 오류를 반환하는 Internet Explorer를 제외하고, Document.getElement*메서드에 의해 반환 된 DOM 노드 목록 은 함수 호출의 대상이 될만큼 JScript 개체가 아닙니다.

주의 사항 : Internet Explorer 특정 코드를 작성하는 데는 신경 쓰지 않지만 제 3 자 웹 사이트에 삽입 할 위젯을 작성하고 있기 때문에 JQuery와 같은 Javascript 라이브러리를 사용할 수 없으며 다음과 같은 외부 라이브러리를로드 할 수 없습니다. 클라이언트에 충돌이 발생합니다.

내 마지막 도랑 노력은 DOM 노드 목록을 반복하고 직접 배열을 만드는 것입니다.하지만 더 좋은 방법이 있습니까?


더 나은 방법은 DOM 노드 목록에서 변환하는 함수를 생성하는 것입니다. 그러나 그것이 실제로 제 솔루션이 될 것입니다.
Kristoffer Sall-Storgaard

> for (i = 0; i & lt; x.length; i ++) 매 반복마다 NodeList의 길이를 얻는 이유는 무엇입니까? 시간 낭비 일뿐만 아니라 NodeLists는 라이브 컬렉션이기 때문에 루프 본문의 길이가 변경되면 무한 루프를 반복하거나 인덱스 범위를 벗어납니다. 후자는 길이를 변수에 할당 할 때 발생할 수있는 최악의 상황이며 오류는 무한 루프보다 훨씬 낫습니다.

이것은 정말 오래된 질문이지만 jQuery는 .noConflict 메서드로 특별히 빌드 되었으므로 다른 라이브러리 (자체조차도)와 충돌하지 않습니다. 즉, 여러 버전의 jQuery를 페이지에로드 할 수 있습니다. 즉, 꼭 필요한 경우가 아니라면 라이브러리 사용 /로드를 피하는 것이 가장 좋습니다.
vol7ron

@ vol7ron : 2016 년으로 빨리 감기, 그리고 모든 사람들은 자바 스크립트 라이브러리가 페이지에 추가하는 크기에 대해 여전히 불안합니다. 물론, 축소되고 압축 된 JQuery는 30KB이지만 노드 목록을 변환하기에는 여전히 30KB가 너무 많습니다. :-)
Guss

답변:


64

NodeList는 호스트 객체입니다 . 호스트 객체 에서 Array.prototype.slice메서드를 사용하는 것은 작동이 보장되지 않습니다. ECMAScript 사양은 다음과 같이 말합니다.

슬라이스 기능이 호스트 객체에 성공적으로 적용될 수 있는지 여부는 구현에 따라 다릅니다.

간단한 함수를 만들어 NodeList각 기존 요소를 반복 하고 배열에 추가 하는 것이 좋습니다 .

function toArray(obj) {
  var array = [];
  // iterate backwards ensuring that length is an UInt32
  for (var i = obj.length >>> 0; i--;) { 
    array[i] = obj[i];
  }
  return array;
}

최신 정보:

다른 답변에서 알 수 있듯이 이제 최신 환경에서 스프레드 구문 또는 Array.from방법을 사용할 수 있습니다 .

const array = [ ...nodeList ] // or Array.from(nodeList)

그러나 그것에 대해 생각하면 NodeList를 Array로 변환하는 가장 일반적인 사용 사례는 그것을 반복하는 것입니다. 이제 NodeList.prototype객체는 forEach기본적으로 메소드를 가지고 있으므로 최신 환경에 있다면 직접 사용할 수 있습니다. pollyfill.


2
이것은 목록의 원래 순서가 반대로 된 배열을 만드는 것입니다. OP가 원하는 것이 아니라고 생각합니다. array[i] = obj[i]대신 하려고 했습니까 array.push(obj[i])?
Tim Down

@Tim, 맞아요, 전에 그렇게했지만 어제 밤에 눈치 채지 않고 편집했습니다 (3AM 현지 시간 :), 감사합니다!.
CMS

9
어떤 상황 obj.length에서 정수 값 이외의 것은 무엇입니까?
Peter

1
그렇게 복잡하다는 게 믿기지 않아요. 추한. 이것은 Web / JS 프로그래밍에서 매우 일반적인 요구 사항입니다. 다음 언어 릴리스를위한 새로운 방법?
Andrew Koper 2013 년

1
@AlbertoPerez, 천만에요!. 살루 도스 하 스타 마드리드!
CMS

126

es6 에서는 다음과 같이 사용할 수 있습니다.

  • 스프레드 연산자

     var elements = [... nodelist]
  • 사용 Array.from

     var elements = Array.from(nodelist)

https://developer.mozilla.org/en-US/docs/Web/API/NodeList 에서 더 많은 참조


4
너무 쉬운 Array.from(): D
Josan Iracheta

4
누군가가 Typescript (ES5로)와 함께이 접근 방식을 사용하는 경우 Array.fromTS nodelist.slice가 이것을 지원하지 않기 때문에 작동합니다 .
Peter Albert

내가 1 년 전에 똑같은 대답 을했고 당신은 투표에서 나를 통과 시켰습니까? 나는 .. 이것을 설명 할 수 없다
수직 동기화

3
@vsync, 당신의 대답은 언급하지 않습니다Array.from
ESR

@EdmundReed-그래서? 그것이 그것을 어떻게 정당화합니까? 작성하는 것이 더 길기 때문에 실제 상황에서는 절대 사용되지 않고 spread사용됩니다.
vsync

16

스프레드 사용 (ES2015) 과 같이 쉽습니다.[...document.querySelectorAll('p')]

(선택 사항 : Babel 을 사용 하여 위의 ES6 코드를 ES5 구문으로 변환)


브라우저의 콘솔에서 시도해보고 마법을 확인하십시오.

for( links of [...document.links] )
  console.log(links);

적어도 최신 크롬, 44, 나는 이것을 얻는다 : Uncaught TypeError : document.querySelectorAll is not a function (…)
Nick

@OmidHezaveh-내가 말했듯이 이것은 ES6 코드입니다. Chrome 44가 ES6를 지원하는지, 그렇다면 어떤 범위에서 지원하는지 모르겠습니다. 거의 1 년 된 브라우저이며 분명히 ES6 스프레드를 지원하는 브라우저에서이 코드를 실행해야합니다.
vsync

또는 실행하기 전에 es5로 트랜스 파일
HelloWorld

8

이 간단한 트릭 사용

<Your array> = [].map.call(<Your dom array>, function(el) {
    return el;
})

당신이 사용하는 것보다 성공의 더 나은 기회가 있다고 생각 왜 설명해 주시겠습니까 Array.prototype.slice(또는 [].slice당신이 그것을 넣어)? 참고로 Q에서 문서화 한 IE 특정 오류는 IE 8 이하에서 발생하며 map어쨌든 구현되지 않는다는 점에 대해 언급하고 싶습니다 . IE 9 ( "표준 모드") 이상, 모두 slicemap같은 방식으로 성공.
Guss

6

실제로 적절한 shim은 아니지만 DOM 요소 작업을 요구하는 사양이 없기 때문에 다음과 slice()같은 방식으로 사용할 수 있도록 하나를 만들었습니다 . https://gist.github.com/brettz9/6093105

업데이트 : 내가 DOM4 사양의 편집기로 이것을 제기했을 때 (그들이 호스트 객체에 자신의 제한을 추가 할 수 있는지 묻습니다 (사양은 구현자가 배열 메서드와 함께 사용할 때 이러한 객체를 올바르게 변환해야 함) 그는 "호스트 객체는 ES6 / IDL에 따라 다소 쓸모가 없습니다."라고 답했습니다. 나는 당을 참조 http://www.w3.org/TR/WebIDL/#es-array 사양은 그러나 "플랫폼 배열 객체"를 정의하기 위해 IDL을 사용할 수 http://www.w3.org/TR/domcore/ 아무튼 새 IDL을 사용하지 않는 것 같습니다 HTMLCollection( Element.attributesDOMString 및 DOMTimeStamp에 대해 WebIDL을 사용하고 있음을 명시 적으로 만 명시하지만 그렇게하는 것처럼 보이지만). 나는 봅니다[ArrayClass](Array.prototype에서 상 속됨)이 사용됩니다 NodeList( NamedNodeMap이제 여전히 사용하는 유일한 항목을 위해 더 이상 사용되지 않습니다 Element.attributes). 어쨌든 표준이 될 것 같습니다. ES6 Array.from는 또한 지정해야하는 것보다 더 편리 할 수도 있고 더 Array.prototype.slice의미 론적으로 명확 할 수도 있습니다 [].slice()(그리고 더 짧은 형식 Array.slice()( "배열 제네릭")이 내가 아는 한 표준 동작이되지 않음).


사양이이 동작을 요구하는 방향으로 움직일 수 있음을 나타내도록 업데이트했습니다.
Brett Zamir 2013-08-14

5

오늘날 2018 년에는 ECMAScript 2015 (6th Edition) 또는 ES6를 사용할 수 있지만 모든 브라우저가이를 이해할 수있는 것은 아닙니다 (예 : IE가 모든 것을 이해하지 못함). 원하는 경우 다음과 같이 ES6를 사용할 수 있습니다 : var array = [... NodeList];( 확산 연산자로 ) 또는 var array = Array.from(NodeList);.

다른 경우 (ES6를 사용할 수없는 경우) 가장 짧은 방법을 사용하여 a NodeListArray .

var array = [].slice.call(NodeList, 0);.

예를 들면 :

var nodeList = document.querySelectorAll('input');
//we use "{}.toString.call(Object).slice(8, -1)" to find the class name of object
console.log({}.toString.call(nodeList).slice(8, -1)); //NodeList

var array = [].slice.call(nodeList, 0);
console.log({}.toString.call(array).slice(8, -1)); //Array

var result = array.filter(function(item){return item.value.length > 5});

for(var i in result)
  console.log(result[i].value); //credit, confidence
<input type="text" value="trust"><br><br>
<input type="text" value="credit"><br><br>
<input type="text" value="confidence">

그러나 DOM노드 목록을 쉽게 반복하려는 경우 a NodeListArray. 다음을 NodeList사용하여 항목을 반복 할 수 있습니다 .

var nodeList = document.querySelectorAll('input');
// Calling nodeList.item(i) isn't necessary in JavaScript
for(var i = 0; i < nodeList.length; i++)
    console.log(nodeList[i].value); //trust, credit, confidence
<input type="text" value="trust"><br><br>
<input type="text" value="credit"><br><br>
<input type="text" value="confidence">

목록의 항목 을 사용 for...in하거나 for each...in열거 하려고하지 마십시오 . 목록의 길이와 항목 속성도 열거되므로NodeList 스크립트에서 요소 개체 만 처리해야한다고 가정하면 오류가 발생하기 때문입니다. 또한 for..in특정 순서로 숙소 방문을 보장하지 않습니다. for...of루프는 NodeList 객체를 올바르게 반복합니다.

참조 :


3
var arr = new Array();
var x= ... get your nodes;

for (i=0;i<x.length;i++)
{
  if (x.item(i).nodeType==1)
  {
    arr.push(x.item(i));
  }
}

이것은 작동해야합니다. 크로스 브라우저를 사용하면 모든 "요소"노드를 얻을 수 있습니다.


1
이것은 기본적으로 @CMS의 대답과 동일하지만 요소 노드 만 원한다고 가정한다는 점을 제외하고는 그렇지 않습니다.
Guss 2012
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.