Document.querySelectorAll neden gerçek bir Array yerine StaticNodeList döndürüyor?


103

document.querySelectorAll(...).map(...)Firefox 3.6'da bile yapamamak canımı sıkıyor ve hala bir cevap bulamıyorum, bu yüzden SO'ya bu blogdaki soruyu çapraz göndermeyi düşündüm:

http://blowery.org/2008/08/29/yay-for-queryselectorall-boo-for-staticnodelist/

Bir Dizi almamanızın teknik bir nedeni bilen var mı? Ya da bir StaticNodeList sen kullanabilirsiniz şekilde bir Array devralan neden açılmadığını map, concatvb?

(BTW, istediğiniz işlevlerden yalnızca biriyse, şöyle bir şey yapabilirsiniz NodeList.prototype.map = Array.prototype.map;... ama yine, neden bu işlev (kasıtlı olarak?) İlk etapta engellendi?)


3
Aslında getElementsByTagName bir Dizi değil, bir koleksiyon döndürür ve bunu bir Dizi gibi (concat vb. Yöntemlerle) kullanmak istiyorsanız, bir döngü yaparak bu koleksiyonu bir Diziye dönüştürmeniz ve dizinin her öğesini kopyalamanız gerekir. Bir Dizi içine toplama. Bundan kimse şikayet etmedi.
Marco Demaio

Yanıtlar:


82

Bunun W3C'nin felsefi bir kararı olduğuna inanıyorum. W3C DOM [spec] tasarımı, DOM'nin platform ve dilden bağımsız olması gerektiği için JavaScript tasarımına oldukça ortogonaldir .

" getElementsByFoo()Sıralı döndürür NodeList" veya "a querySelectorAll()döndürür " gibi kararlar StaticNodeListçok bilinçlidir, bu nedenle uygulamaların, döndürülen veri yapılarını dile bağlı uygulamalara göre hizalama konusunda endişelenmelerine gerek kalmaz ( .mapJavaScript ve Ruby Dizilerinde mevcut olma gibi , ancak C # Listelerinde değil ).

W3C düşük hedefliyor: NodeLista ' nın unsigned long türünde salt okunur bir .lengthözellik içermesi gerektiğini söyleyecekler çünkü her uygulamanın en azından bunu destekleyebileceğine inanıyorlar , ancak []dizin operatörünün konumsal öğeleri almayı desteklemek için aşırı yüklenmesi gerektiğini açıkça söylemiyorlar , çünkü uygulamak isteyen getElementsByFoo()ancak operatörün aşırı yüklenmesini destekleyemeyen bazı zayıf küçük dilleri engellemek istemiyorlar . Spesifikasyonun çoğunda mevcut olan yaygın bir felsefedir.

John Resig, sizinkine benzer bir seçeneği dile getirdi ve ekliyor :

Benim argümanım o kadar NodeIteratorçok DOM'a benzemiyor, JavaScript'e pek benzemiyor. JavaScript dilinde mevcut olan özelliklerden yararlanmaz ve bunları elinden geldiğince kullanmaz ...

Ben biraz empati kuruyorum. DOM özellikle JavaScript özellikleri göz önünde bulundurularak yazılmış olsaydı, çok daha az garip ve kullanımı daha sezgisel olurdu. Aynı zamanda W3C'nin tasarım kararlarını da anlıyorum.


Teşekkürler, bu durumu anlamama yardımcı oluyor.
Kev

@Kev: O blog makale sayfasında StaticNodeList, diziyi bir diziye nasıl çevireceğinizi sorgulayan yorumunuzu gördüm . @ Mck89'un cevabını a NodeList/ ' StaticNodeListyi yerel bir Diziye dönüştürmenin yolu olarak kabul ediyorum , ancak bu IE'de (8 obv) JScript hatasıyla başarısız olacak, çünkü bu nesneler barındırılıyor / "özel".
Crescent Fresh

Doğru, bu yüzden ona oy verdim. Yine de başka biri + 1'imi iptal etti. Barındırılan / özel ile neyi kastediyorsunuz?
Kev

1
@Kev: barındırılan değişkenler, "ana bilgisayar" ortamı (örneğin bir web tarayıcısı) tarafından sağlanan değişkenlerdir. Örneğin document, windowvb genellikle bu "özel", bazen, küçük ve ince şekilde, normal kullanım için uygun olmayan bu (COM nesneler olarak, örneğin) uygular IE Array.prototype.slice.callbombalama verilen StaticNodeList);
Crescent Taze

201

ES2015 (ES6) yayma operatörünü kullanabilirsiniz :

[...document.querySelectorAll('div')]

StaticNodeList öğesini Array of items'e dönüştürür.

İşte nasıl kullanılacağına dair bir örnek.

[...document.querySelectorAll('div')].map(x => console.log(x.innerHTML))
<div>Text 1</div>
<div>Text 2</div>


24
Bir başka yolu kullanmaktır Array.from () :Array.from(document.querySelectorAll('div')).map(x => console.log(x.innerHTML))
Michael Berdyshev

42

Neden bir dizi yerine düğüm listesi döndürdüğünü bilmiyorum, belki getElementsByTagName gibi DOM'u güncellediğinizde sonucu güncelleyeceği içindir. Her neyse, bu sonucu basit bir diziye dönüştürmek için çok basit bir yöntem şudur:

Array.prototype.slice.call(document.querySelectorAll(...));

ve sonra şunları yapabilirsiniz:

Array.prototype.slice.call(document.querySelectorAll(...)).map(...);

3
Aslında, DOM'u güncellediğinizde sonucu güncellemez - dolayısıyla 'statik'. Sonucu güncellemek için qSA'yı manuel olarak tekrar aramanız gerekir. sliceHat için +1 olsa.
Kev

1
Evet, Kev'in dediği gibi: qSA sonuç kümesi statik, getElementsByTagName () sonuç kümesi dinamiktir.
joonas.fi

IE8 yalnızca standartlar modunda querySelectorAll () 'ı destekler
mbokil

13

Crescent'in söylediklerine eklemek için,

İstediğiniz tek işlevse, NodeList.prototype.map = Array.prototype.map gibi bir şey yapabilirsiniz.

Bunu yapma! Çalışması hiç garanti değil.

Hiçbir JavaScript veya DOM / BOM standardı, NodeListyapıcı-işlevin bir global / windowözellik olarak var olduğunu veya NodeListtarafından döndürülen querySelectorAllişlevin ondan miras alınacağını veya prototipinin yazılabilir olduğunu veya işlevin Array.prototype.mapaslında bir NodeList üzerinde çalışacağını belirtmez .

Bir NodeList'in bir 'ana bilgisayar nesnesi' olmasına izin verilir (ve IE'de ve bazı eski tarayıcılarda birdir). ArrayYöntemler sayısal ve ortaya herhangi JavaScript 'yerli nesne' üzerinde çalışmasına izin verilmesi olarak tanımlanan lengthözelliklere, ancak ana nesneler üzerinde çalışmaya zorunlu değildir (ve IE, sevmezler).

DOM listelerinde tüm dizi yöntemlerini (tümü, yalnızca StaticNodeList değil) almamanız can sıkıcıdır, ancak bunun güvenilir bir yolu yoktur. Geri aldığınız her DOM listesini bir Diziye manuel olarak dönüştürmeniz gerekir:

Array.fromList= function(list) {
    var array= new Array(list.length);
    for (var i= 0, n= list.length; i<n; i++)
        array[i]= list[i];
    return array;
};

Array.fromList(element.childNodes).forEach(function() {
    ...
});

1
Ateş et, bunu düşünmedim. Teşekkürler!
Kev

+1 katılıyorum. Sadece bir yorum, sanırım "var array = new Array (list.length)" yerine "var array = []" yapmanın kodu daha da kısaltacağını düşünüyorum. Ama bunu yaparken bir sorun olabileceğini biliyorsan ilgileniyorum.
Marco Demaio

@MarcoDemaio: Hayır, sorun değil. new Array(n)JS terpine dizinin ne kadar süreceğine dair bir ipucu verir. Bu, bu miktarda alanı önceden tahsis etmesine izin verebilir, bu da potansiyel olarak bir hızlanma ile sonuçlanır, çünkü dizi büyüdükçe bazı bellek yeniden tahsislerinden kaçınılabilir. Modern tarayıcılarda gerçekten yardımcı olup olmadığını bilmiyorum ... Ölçülemeyeceğinden şüphelenirdim.
bobince


2

Bence basitçe aşağıdakileri yapabilirsin

Array.prototype.map.call(document.querySelectorAll(...), function(...){...});

Benim için mükemmel çalışıyor


0

Bu, burada başkaları tarafından önerilen diğer olasılıklar yelpazesine eklemek istediğim bir seçenektir. Yalnızca entelektüel eğlence amaçlıdır ve tavsiye edilmez .


Sırf eğlence olsun diye, querySelectorAlldiz çöküp size boyun eğmeye "zorlamanın" bir yolu burada :

Element.prototype.querySelectorAll = (function(QSA){
    return function(){
        return [...QSA.call(this, arguments[0])]
    }
})(Element.prototype.querySelectorAll);

Şimdi, patronun kim olduğunu göstererek bu işlevin her yerine adım atmak iyi hissettiriyor. Şimdi neyin daha iyi olduğunu bilmiyorum, tamamen yeni bir adlandırılmış işlev sarmalayıcı oluşturup ardından tüm kodunuzun bu garip adı kullanmasını sağlayın (hemen hemen jQuery tarzı) veya yukarıdaki gibi işlevi bir kez geçersiz kılın, böylece kodunuzun geri kalanı yine de mümkün olur orijinal DOM yöntem adını kullanmak için querySelectorAll.

  • Böyle bir yaklaşım, alt yöntemlerin olası kullanımını ortadan kaldıracaktır.

Bunu hiçbir şekilde tavsiye etmem, eğer dürüst olmak gerekirse [ne olduğunu biliyorsun].

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.