Trong Javascript, cách tốt nhất để chuyển đổi NodeList thành một mảng là gì


82

Phương thức DOM document.querySelectorAll()(và một số phương thức khác) trả về a NodeList.

Để hoạt động trên danh sách, ví dụ như sử dụng forEach(), NodeListđầu tiên phải được chuyển đổi thành một Array.

Cách tốt nhất để chuyển đổi NodeListthành một là Arraygì?


1
Tôi nghĩ rằng giá trị trả về của querySelectorAll () về mặt kỹ thuật được gọi là NodeList.
jfriend00

từ mdm "elementList = document.querySelectorAll (selectors);"
cc trẻ

1
elementList là tên biến. Cũng trang đó mô tả cách loại giá trị trả về là NodeList.
jfriend00

cảm ơn vì sự sửa chữa - đã sửa trong câu hỏi
cc young

Câu trả lời:


67

Với ES6, bạn có thể chỉ cần làm:

const spanList = [...document.querySelectorAll("span")];

Điều này mang lại cho tôiType 'NodeListOf<Element>' must have a '[Symbol.iterator]()' method that returns an iterator.ts(2488)
gọi lại vào

Xin chào @callback, đây có vẻ như là một lỗi liên quan đến TypeScript. Bạn có thể đã chọn nhắm mục tiêu biên dịch es6 mà không thêm "es6" trong mảng lib của tệp tsconfig của bạn. Trân trọng
Freezystem

1
Xin chào @Freezystem, bạn nói đúng! Tôi đang nhắm mục tiêu es2015. Cảm ơn!
gọi lại

@callback ES6 và ES2015 giống nhau
DarkNeuron

65

Với ES6 bạn có thể sử dụng Array.from(myNodeList). Sau đó, sử dụng phương pháp mảng yêu thích của bạn.

var myNodeList = document.querySelectorAll('.my-selector');

// ALT 1
Array.from(myNodeList).forEach(function(el) {
  console.log(el);
});

Sử dụng miếng đệm ES6 để làm cho điều này hoạt động trong các trình duyệt cũ hơn.


Nếu bạn đang sử dụng trình chuyển tiếp (ví dụ: Babel), có hai lựa chọn thay thế khác:

var myNodeList = document.querySelectorAll('.my-selector');

// ALT 2
for (var el of myNodeList) {
  el.classList.add('active'); // or some other action
}

// ALT 3
[...myNodeList].forEach((el) => {
  console.log(el);
});

không phải cũng đúng trong es6 rằng nodeList cung cấp một trình vòng lặp?
cc trẻ

4
@ccyoung nhưng trình lặp không hoạt động trong các trình duyệt ES6 không tuân thủ vì bạn không thể cung cấp đối tượng Biểu tượng nên tốt hơn nên sử dụng Array.from(myNodeList)vì nó có thể được cung cấp.
Roc

vì vậy tôi gặp sự cố này trong đó Array.from (el.childNodes) không trả về nút đầu tiên như một phần của mảng.
zinoadidi

49

Bạn có thể chuyển đổi nó thành một mảng bằng cách sử dụng slicephương thức từ Arraynguyên mẫu:

var elList = document.querySelectorAll('.viewcount');
elList = Array.prototype.slice.call(elList, 0);

Hơn nữa, nếu tất cả những gì bạn cần là có forEach, bạn có thể gọi từ Arraynguyên mẫu mà không cần ép buộc nó vào một mảng trước:

var elList = document.querySelectorAll('.viewcount');
Array.prototype.forEach.call(elList, function(el) {
    console.log(el);
});

Trong ES6, bạn có thể sử dụng Array.fromhàm mới để chuyển nó thành một mảng:

Array.from(elList).forEach(function(el) {
    console.log(el);
});

Tính năng này hiện chỉ có trong các trình duyệt hiện đại, nhưng nếu bạn đang sử dụng dịch vụ polyfill, bạn sẽ có quyền truy cập vào chức năng này trên toàn bộ bảng.


Nếu bạn đang sử dụng trình chuyển tiếp ES6 , bạn thậm chí có thể sử dụng một for..ofvòng lặp để thay thế:

for (var element of document.querySelectorAll('.some .elements')) {
  // use element here
}

cảm ơn. theo suy nghĩ javascript mới hơn / hy vọng có một sự ép buộc ngắn gọn hơn.
cc trẻ

6
@cc young - Xin lưu ý, lý do tôi đang sử dụng Array.prototype.forEachthay vì [].forEach, là vì sau này tạo một đối tượng Array mới, điều này hoàn toàn không cần thiết.
Joseph Silber

@JosephSilber ahh cảm ơn bạn - đó là mảng mới được tạo là trống []? Suy nghĩ của tôi là nó sẽ được thu thập rác và tác động đến bộ nhớ là không đáng kể, bất cứ ai có thể bình luận về điều này?
Daniel Sokolowski

@Daniel điều này đúng, nhưng vẫn có sự tính toán của việc tạo và phá hủy mảng.
Brett

21

Tại sao phải chuyển đổi? - chỉ là callchức năng của Mảng trực tiếp trên tập hợp phần tử;)

[].forEach.call( $('a'), function( v, i) {
    // do something
});

giả sử $ là bí danh của bạn cho querySelectorAll , tất nhiên


chỉnh sửa: ES6 cho phép cú pháp ngắn hơn [...$('a')](chỉ hoạt động trong Firefox, kể từ tháng 5 năm 2014 )


Giả sử $querySelectorAll.
c69

3
Câu trả lời của bạn ngụ ý sử dụng jQuery. Nếu đúng như vậy, tomfoolery này là hoàn toàn không cần thiết, cảm ơn .each().
Matt Ball

1
haha tại sao ? không có gì cấm bạn tạo bí danh như thế nào function $ ( s ) { return document.querySelectorAll(s); }.
c69

5
Nếu bạn đang đi để sử dụng jQuery, sau đó là giải pháp succint hơn là:$('a').each(function(i, v) {...});
jfriend00

2
offtopic
c69

9

Nó có phải được forEachkhông? Bạn có thể chỉ cần sử dụng một forvòng lặp để lặp lại danh sách:

for (var i = 0; i < elementList.length; i++) {
    doSomethingWith(elementlist.item(i));
}

1
+1 để sử dụng giải pháp đơn giản không thêm chuyển đổi mảng không cần thiết. FYI, Thay vì elementList.item(i), bạn chỉ có thể sử dụng elementList[i].
jfriend00

4
Cá nhân tôi tìm thấy forEach()một phong cách lập trình tốt hơn và ít dài dòng hơn - ymmv
cc young

@cc young: Thực ra, tôi đồng ý với bạn. Ngoại trừ những trường hợp như thế này, nơi tôi cần chạy một chuyển đổi để có thể sử dụng mẫu yêu thích của mình. Điều đó làm cho nó trở nên lộn xộn và trông giống như: "Khi tất cả những gì bạn có là một cái búa, mọi thứ bắt đầu giống như một cái đinh."
nfechner

Và đây là một cách nuttier :) for (var oElement, i = 0; oElement = aMenuItemsElements; i++ { console.log(oElement); }
Daniel Sokolowski

Vấn đề ở đây là bạn không thể lồng một for (var i…)vòng lặp khác vì vòng lặp for không tạo phạm vi riêng của nó (giống như trong C / C ++ bây giờ). Và sau đó ibị trộn lẫn.
Jens

9

Cập nhật năm 2020: nodeList.forEach () hiện là tiêu chuẩn chính thức và được hỗ trợ trong tất cả các trình duyệt hiện tại.

Các trình duyệt cũ hơn có thể sử dụng polyfill bên dưới.

Để hoạt động trên danh sách trong javascript, ví dụ: sử dụng forEach (), NodeList phải được chuyển đổi thành Mảng.

Đo không phải sự thật. .forEach()hoạt động trong các trình duyệt hiện tại. Nếu thiếu, bạn có thể thêm .forEach () từ Array vào NodeList và nó hoạt động tốt:

if ( ! NodeList.prototype.forEach ) {
  NodeList.prototype.forEach = Array.prototype.forEach;
}

Bây giờ bạn có thể chạy:

myNodeList.forEach(function(node){...})

Để lặp lại các NodeLists giống như Mảng.

Điều này tạo ra mã ngắn hơn và sạch hơn nhiều so với .call () ở mọi nơi.


1
Câu trả lời này chính xác là những gì tôi cần và 3 dòng này đã tiết kiệm rất nhiều thời gian. Tất cả các câu trả lời khác sẽ yêu cầu thay đổi một loạt mã và tôi không hiểu tại sao.
Scribblemacher

downvotes có lẽ vì khỉ vá built-in nguyên mẫu được coi là xấu thực hành
DuBistKomisch

@DuBistKomisch Đây là một polyfill, chỉ được áp dụng nếu NodeList.foreach () chuẩn không tồn tại.
mikemaccana

1
à thật tệ, không nhận ra họ thực sự đã thêm forEachcụ thể, tôi đến đây để tìm kiếmfilter
DuBistKomisch

@DuBistKomisch bạn có thể sử dụng kỹ thuật tương tự filter, nhưng bạn có thể đặt tên không chính thức NodeList.prototype.dbkFilterhoặc tương tự cho nó nếu bạn lo lắng về một tiêu chuẩn trong tương lai sử dụng cách triển khai khác.
mikemaccana

2

ES6 cho phép những cách thú vị như thế var nodeArray = Array.from(nodeList)nhưng tôi thích nhất là toán tử spread mới.

var nodeArray = Array(...nodeList);

Một giải pháp hoàn hảo! Giải pháp này cũng áp dụng cho Typecript.
Nirus

Tôi muốn nói thêm rằng điều này chỉ hoạt động với TypeScript miễn là bạn không chuyển sang ES5 hoặc thấp hơn.
Rudey

2

Điều đó đã làm việc với tôi trong ES6

Giả sử bạn có một danh sách ghi chú như thế

<ul>
  <li data-time="5:17">Flexbox video</li>
  <li data-time="8:22">Flexbox video</li>
  <li data-time="3:24">Redux video</li>
  <li data-time="5:17">Flexbox video</li>
  <li data-time="7:17">Flexbox video</li>
  <li data-time="4:17">Flexbox video</li>
  <li data-time="2:17">Redux video</li>
  <li data-time="7:17">Flexbox video</li>
  <li data-time="9:54">Flexbox video</li>
  <li data-time="5:53">Flexbox video</li>
  <li data-time="7:32">Flexbox video</li>
  <li data-time="2:47">Redux video</li>
  <li data-time="9:17">Flexbox video</li>

</ul>


const items = Array.from(document.querySelectorAll('[data-time]'));

console.log(items);

2

Tôi sử dụng phần sau vì tôi nghĩ nó dễ đọc nhất:

const elements = document.getElementsByClassName('element');
[...elements].forEach((element) => {
   // code
});


1

Chà, điều này cũng phù hợp với tôi:

const elements = Object.values( document.querySelector(your selector here) )

Object.values()trả về Arraycác giá trị của đối tượng đã cho. NodeListlà đối tượng, cũng như mọi thứ trong JS.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/values

Nhưng nó không tương thích với IE, vì vậy tôi đoán Array.prototype.*array_method*.call(yourNodeList)là lựa chọn tốt nhất. Với điều này, bạn có thể gọi bất kỳ phương thức mảng nào trênNodeList


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.