DOM düğüm listesini JavaScript'te bir diziye nasıl dönüştürebilirim?


103

HTML düğümlerinin bir listesini kabul eden bir Javascript işlevim var, ancak bir Javascript dizisi bekliyor (bunun üzerinde bazı Dizi yöntemlerini çalıştırıyor) ve bunu Document.getElementsByTagNamebir DOM düğüm listesi döndüren çıktısını beslemek istiyorum .

Başlangıçta şöyle basit bir şey kullanmayı düşündüm:

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

Ve bu, elbette "JScript nesnesi bekleniyor" hatasını döndüren Internet Explorer dışında tüm tarayıcılarda iyi çalışıyor, çünkü görünüşe göre Document.getElement*yöntemler tarafından döndürülen DOM düğüm listesi , bir işlev çağrısının hedefi olacak kadar bir JScript nesnesi değil.

Uyarılar: Internet Explorer'a özel kod yazmak benim için sorun değil, ancak JQuery gibi herhangi bir Javascript kitaplığını kullanmama izin verilmiyor çünkü 3. taraf web sitesine gömülecek bir pencere öğesi yazıyorum ve şu harici kitaplıkları yükleyemiyorum müşteriler için çatışma yaratacaktır.

Son hendek çabam, DOM düğüm listesini yinelemek ve kendim bir dizi oluşturmaktı, ancak bunu yapmanın daha güzel bir yolu var mı?


Daha da iyisi, DOM düğüm listesinden dönüştürmek için bir işlev oluşturun, ancak bu gerçekten benim çözümüm olurdu, bence doğru anladınız.
Kristoffer Sall-Storgaard

> for (i = 0; i & lt; x.length; i ++) Neden her yinelemede NodeList'in uzunluğunu alıyorsunuz? Bu sadece zaman kaybı değil, aynı zamanda Düğüm Listeleri canlı koleksiyonlar olduğundan, döngünün gövdesindeki herhangi bir şey uzunluğunu değiştirirse, sonsuz döngü yapabilir veya sınırların dışındaki bir dizini vurabilirsiniz. İkincisi, uzunluğu bir değişkene atarsanız meydana gelebilecek en kötü durumdur ve bir hata, sonsuz bir döngüden çok daha iyidir.

Bu gerçekten eski bir sorudur, ancak jQuery özel olarak .noConflict yöntemiyle oluşturulmuştur, bu nedenle diğer kitaplıklarla (hatta kendisiyle) çakışmaya neden olmaz, yani jQuery'nin birden çok sürümünün bir sayfaya yüklenebileceği anlamına gelir. Bununla birlikte, kesinlikle mecbur olmadığınız sürece bir kitaplık kullanmaktan / yüklemekten kaçınmak en iyisidir.
vol7ron

@ vol7ron: 2016'ya ileri sarıldı ve herkes javascript kitaplıklarının sayfaya eklediği boyut konusunda hâlâ gergin. Verildi, JQuery küçültülmüş ve gzip ile sıkıştırılmış 30 KB, hala 30 KB, bir düğüm listesini dönüştürmek için çok fazla :-)
Guss

Yanıtlar:


71

NodeLists ana nesnelerdir , ana bilgisayar nesnelerindeArray.prototype.slice yöntemin kullanılması garanti edilmez, ECMAScript Spesifikasyonu şunu belirtir:

Dilim işlevinin bir ana bilgisayar nesnesine başarıyla uygulanıp uygulanamayacağı uygulamaya bağlıdır.

NodeListVar olan her öğeyi bir diziye eklemek ve üzerinde yinelemek için basit bir işlev yapmanızı öneririm :

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;
}

GÜNCELLEME:

Diğer yanıtların da önerdiği gibi, artık modern ortamlarda yayılma sözdizimini veya Array.fromyöntemi kullanabilirsiniz:

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

Ama düşününce, bir NodeList'i bir Array'e dönüştürmenin en yaygın kullanım durumu, onu yinelemektir ve şimdi NodeList.prototypenesnenin forEachyöntemi yerel olarak vardır , bu nedenle modern bir ortamdaysanız, onu doğrudan kullanabilir veya bir pollyfill.


2
Bu, listenin orijinal sırası tersine çevrilmiş bir dizi oluşturmaktır, ki bu OP'nin istediğini sanmıyorum. array[i] = obj[i]Bunun yerine yapmak mı istedin array.push(obj[i])?
Tim Down

@Tim, doğru, daha önce de öyleydim ama dün gece fark etmeden düzenledim (yerel saatle 03:00), Teşekkürler !.
Christian C. Salvadó

9
Hangi koşullarda obj.lengthtamsayı değerinden başka bir şey olabilir?
Peter

1
Bu kadar karmaşık olduğuna inanamıyorum. Çirkin. Bu, Web / JS programlamada çok yaygın bir ihtiyaçtır. Bir sonraki dil sürümü için yeni bir yöntem mi?
Andrew Koper

1
@AlbertoPerez, rica ederim !. Saludos hasta Madrid!
Christian C. Salvadó

133

Gelen ES6 aşağıdaki gibi sadece kullanabilirsiniz:

  • Yayılma operatörü

     var elements = [... nodelist]
    
  • Kullanma Array.from

     var elements = Array.from(nodelist)
    

https://developer.mozilla.org/en-US/docs/Web/API/NodeList adresinde daha fazla referans


4
ile çok kolay Array.from(): D
Josan Iracheta

4
Birinin bu yaklaşımı Typescript (ES5'e) ile kullanması durumunda, yalnızca Array.fromTS bunu aktarırken çalışır nodelist.slice- ki bu desteklenmez.
Peter Albert

Senden bir yıl önce aynı cevabı verdim ve sen oylarda beni geçtin mi? Bunu açıklayamam ..
vsync

3
@vsync, cevabınızdan bahsetmiyorArray.from
ESR

@EdmundReed - yani? bu onu nasıl haklı çıkarır? gerçek durumda yazmak daha uzun olduğundan asla kullanılamayacak, sadece spreadkullanılıyor.
vsync

17

Yayılmayı (ES2015) kullanmak şu kadar kolaydır:[...document.querySelectorAll('p')]

(opsiyonel: Kullanım Babel ES5 sözdizimine ES6 Yukarıdaki kodu transpile için)


Tarayıcınızın konsolunda deneyin ve sihri görün:

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

En azından en son chrome, 44, şunu anlıyorum: Yakalanmamış TypeError: document.querySelectorAll bir işlev değil (…)
Nick

@OmidHezaveh - Dediğim gibi, bu ES6 kodu. Chrome 44'ün ES6'yı destekleyip desteklemediğini ve destekleniyorsa hangi kapsamda olduğunu bilmiyorum. Neredeyse bir yıllık bir tarayıcı ve açıkçası bu kodu ES6 yayılmasını destekleyen bir tarayıcıda çalıştırmanız gerekecek.
vsync

Veya yürütmeden önce es5'e aktarın
HelloWorld

8

Bu basit numarayı kullanın

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

Bunun başarı şansı kullanmaktan Array.prototype.slice(veya [].slicesizin deyiminizle) neden daha fazla olduğunu düşündüğünüzü açıklayabilir misiniz ? Bir not olarak, Q'da belgelediğim IE'ye özgü hatanın, mapzaten uygulanmayan IE 8 veya daha düşük sürümlerde gerçekleştiğini belirtmek isterim . IE 9 ( "standartlar modu") veya daha yüksek, her iki sliceve mapaynı şekilde başarılı.
Guss

6

Gerçekten uygun bir şim olmasa da, DOM öğeleriyle çalışmayı gerektirecek bir özellik olmadığından slice(), bu şekilde kullanmanıza izin verecek bir tane yaptım : https://gist.github.com/brettz9/6093105

GÜNCELLEME : Bunu DOM4 spesifikasyonunun düzenleyicisiyle (ana nesnelere kendi kısıtlamalarını ekleyip ekleyemeyeceklerini sorarak (böylece spesifikasyon, uygulayıcıların dizi yöntemleriyle kullanıldığında bu nesneleri düzgün bir şekilde dönüştürmesini gerektirecek) ECMAScript spesifikasyonunun ötesinde sorduğumda uygulama bağımsızlığına izin verilir), "Ana bilgisayar nesneleri ES6 / IDL başına az çok eski" dedi. I başına görmek http://www.w3.org/TR/WebIDL/#es-array özellikleri, ancak "Platform dizi nesneleri" tanımlamak için bu IDL kullanabilirsiniz http://www.w3.org/TR/domcore/ doesn HTMLCollectionGörünüşe göre yeni IDL for kullanılıyor (ancak Element.attributesDOMString ve DOMTimeStamp için WebIDL kullandığını açıkça belirtmesine rağmen bunu yapıyor gibi görünüyor ). Görüyorum[ArrayClass](Array.prototype'dan devralan) için kullanılır NodeList(ve NamedNodeMapartık onu hala kullanan tek öğe lehine kullanımdan kaldırılmıştır Element.attributes). Her durumda, standart hale gelmek gibi görünüyor. ES6 Array.from, bu tür dönüşümler için, belirtmek zorunda olmaktan Array.prototype.sliceve anlamsal olarak daha açık olmaktan daha uygun olabilir [].slice()(ve daha kısa biçim Array.slice()(bir "genel dizi"), bildiğim kadarıyla standart davranış haline gelmemiştir).


Özelliklerin bu davranışı gerektirecek yönde hareket ediyor olabileceğini belirtmek için güncelledim.
Brett Zamir

5

Bugün, 2018'de ECMAScript 2015 (6. Baskı) veya ES6'yı kullanabilirdik, ancak tüm tarayıcılar bunu anlayamaz (örneğin, IE hepsini anlamıyor). İsterseniz ES6'yı aşağıdaki gibi kullanabilirsiniz: var array = [... NodeList];( yayılma operatörü olarak ) veya var array = Array.from(NodeList);.

(Eğer ES6 kullanamıyorsanız) Diğer durumda bir dönüştürmek için en kısa şekilde kullanabilirsiniz NodeListbir etmek Array:

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

Örneğin:

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">

Eğer üzerinde yineleme yapmak istiyorsanız DOMsadece kolay düğüm listesi, o zaman bir dönüştürmek gerekmez NodeListbir etmek Array. Şunları NodeListkullanarak öğeler üzerinde döngü oluşturmak mümkündür :

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">

Listedeki öğeleri kullanmak for...inveya for each...innumaralandırmak için cazip olmayın , çünkü bu aynı zamanda öğenin uzunluk ve öğe özelliklerini de numaralandıracaktır.NodeList komut dosyanız yalnızca öğe nesneleriyle uğraşması gerektiğini varsayarsa hatalara neden olur. Ayrıca, for..inmülkleri belirli bir sırayla ziyaret etmek garanti edilmez. for...ofdöngüler, NodeList nesneleri üzerinde doğru şekilde döngü oluşturacaktır.

Şunu da görün:


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));
  }
}

Bu, tarayıcılar arasında çalışmalı ve size tüm "öğe" düğümlerini almalıdır.


1
Bu, temelde @ CMS'nin cevabı ile aynıdır, ancak sadece eleman düğümlerini istediğimi varsayar - ki istemiyorum.
Guss
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.