डॉक्यूमेंट.क्वेरीसेलेक्टरऑल एक वास्तविक एरियर के बजाए स्टैटिकनोडलिस्ट को क्यों लौटाता है?


103

यह मुझे बताता है कि मैं अभी document.querySelectorAll(...).map(...)भी फ़ायरफ़ॉक्स 3.6 में नहीं कर सकता , और मुझे अभी भी कोई उत्तर नहीं मिल रहा है, इसलिए मैंने सोचा कि मैं इस ब्लॉग से SO पर प्रश्न को पार करूंगा:

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

क्या किसी को तकनीकी कारण का पता है कि आपको एरियर क्यों नहीं मिलता है? या क्यों एक StaticNodeList कि आप इस्तेमाल कर सकते हैं इस तरह से किसी सरणी से विरासत नहीं है map, concat, आदि?

(BTW अगर यह सिर्फ एक फ़ंक्शन है जो आप चाहते हैं, तो आप कुछ ऐसा कर सकते हैं NodeList.prototype.map = Array.prototype.map;... लेकिन फिर, यह कार्यक्षमता (जानबूझकर?) पहली जगह में क्यों अवरुद्ध है?)


3
असल में getElementsByTagName एक Array नहीं लौटाता है, लेकिन एक संग्रह है, और यदि आप इसे एक Array की तरह उपयोग करना चाहते हैं (concat जैसे तरीकों के साथ आदि) तो आपको एक लूप बनाकर इस तरह के संग्रह को Array में बदलना होगा और प्रत्येक तत्व को कॉपी करना होगा। एक सरणी में संग्रह। इस बारे में किसी ने कभी शिकायत नहीं की।
मार्को डेमायो

जवाबों:


81

मैं इसे W3C का दार्शनिक निर्णय मानता हूं। W3C DOM [कल्पना] का डिज़ाइन जावास्क्रिप्ट के डिजाइन के लिए काफी ओर्थोगोनल है, क्योंकि DOM का मतलब प्लेटफ़ॉर्म और भाषा तटस्थ होना है।

" getElementsByFoo()एक आदेश दिया गया रिटर्न NodeList" या " querySelectorAll()रिटर्न " जैसे निर्णय StaticNodeListबहुत अधिक जानबूझकर होते हैं, ताकि कार्यान्वयन को भाषा-निर्भर कार्यान्वयन के आधार पर अपने लौटे डेटा संरचना को संरेखित करने के बारे में चिंता न करें (जैसे .mapकि जावास्क्रिप्ट और रूबी में एरे पर उपलब्ध है, लेकिन C # में सूचियों पर नहीं )।

W3C उद्देश्य कम: वे कहूँगा एक NodeListएक को शामिल करना चाहिए केवल पढ़ने के लिए .lengthप्रकार की संपत्ति लंबे अहस्ताक्षरित क्योंकि वे हर कार्यान्वयन कर सकते हैं कम से कम समर्थन का मानना है कि है, लेकिन वे कहते हैं कि नहीं होगा स्पष्ट है कि []सूचकांक ऑपरेटर स्थितीय तत्वों प्राप्त करने का समर्थन करने के लिए अतिभारित किया जाना चाहिए, क्योंकि वे कुछ गरीबों की छोटी-छोटी भाषा को नहीं बोलना चाहते हैं जो उनके साथ आती हैं जो लागू करना चाहती हैं getElementsByFoo()लेकिन ऑपरेटर ओवरलोडिंग का समर्थन नहीं कर सकती हैं। यह एक प्रचलित दर्शन है जो अधिकांश कल्पनाओं में मौजूद है।

जॉन रेजिग ने आपके जैसे ही विकल्प को आवाज दी है , जिसमें वह कहते हैं :

मेरा तर्क इतना अधिक नहीं है कि NodeIteratorयह बहुत डोम-जैसा नहीं है कि यह बहुत जावास्क्रिप्ट-जैसा नहीं है। यह जावास्क्रिप्ट भाषा में मौजूद सुविधाओं का लाभ नहीं उठाता है और इसकी क्षमता का सबसे अच्छा उपयोग करता है ...

मैं कुछ हद तक सहानुभूति रखता हूं। यदि DOM विशेष रूप से जावास्क्रिप्ट सुविधाओं को ध्यान में रखकर लिखा गया है, तो यह बहुत कम अजीब और उपयोग करने के लिए अधिक सहज होगा। उसी समय मैं डब्ल्यू 3 सी के डिजाइन निर्णयों को समझता हूं।


धन्यवाद, जो मुझे स्थिति की समझ बनाने में मदद करता है।
केव

@ केव: मैंने आपकी टिप्पणी को उस ब्लॉग लेख पृष्ठ पर देखा, जिसमें कहा गया है कि आप StaticNodeListकिसी ऐरे में कैसे परिवर्तित होंगे । मैं @ m8989 के उत्तर को मूल ऐरे में NodeList/ a में परिवर्तित करने के लिए जाने के तरीके के रूप में समर्थन करूंगा StaticNodeList, लेकिन यह IE (8 obv) में JScript त्रुटि के साथ विफल हो जाएगा, क्योंकि वे ऑब्जेक्ट्स होस्ट किए गए / "विशेष" हैं।
वर्धमान ताजा

सच, इसीलिए मैंने उसे उकसाया। किसी और ने हालांकि मेरा +1 रद्द कर दिया है। आपको होस्ट / विशेष से क्या मतलब है?
केव

1
@ केव: होस्ट किए गए चर "होस्ट" वातावरण (जैसे एक वेब ब्राउज़र) द्वारा प्रदान किए गए कोई भी चर हैं। उदाहरण के लिए document, windowआदि IE अक्सर इन "विशेष रूप से" (उदाहरण के लिए COM ऑब्जेक्ट्स) को लागू करता है जो कभी-कभी सामान्य उपयोग के अनुरूप नहीं होते हैं, छोटे और सूक्ष्म तरीकों से, जैसे कि Array.prototype.slice.callबम दिए जाने पर बमबारी StaticNodeList;)
क्रिसेंट फ्रेश

200

आप ES2015 (ES6) प्रसार ऑपरेटर का उपयोग कर सकते हैं :

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

StaticNodeList को आइटमों के एरे में परिवर्तित करेगा।

इसका उपयोग कैसे करें, इस पर एक उदाहरण दिया गया है।

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


24
दूसरा तरीका Array.from () का उपयोग करना है :Array.from(document.querySelectorAll('div')).map(x => console.log(x.innerHTML))
माइकल बर्डिसेव

42

मुझे नहीं पता कि यह एक सरणी के बजाय नोड सूची क्यों देता है, हो सकता है क्योंकि getElementsByTagName की तरह जब आप DOM को अपडेट करते हैं तो यह परिणाम को अपडेट करेगा। वैसे भी उस सरल सरणी में परिणत करने के लिए एक बहुत ही सरल विधि है:

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

और फिर आप कर सकते हैं:

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

3
वास्तव में जब आप DOM को अपडेट करते हैं तो यह परिणाम अपडेट नहीं करता है - इसलिए 'स्थिर'। आपको परिणाम को अपडेट करने के लिए मैन्युअल रूप से qSA को फिर से कॉल करना होगा। sliceहालांकि लाइन के लिए +1 ।
केव

1
हाँ, जैसे केव ने कहा: qSA परिणाम स्थिर है, getElementsByTagName () परिणामसेट गतिशील है।
joonas.fi

IE8 केवल मानक मोड में --SblectorAll () मानकों का समर्थन करता है
mbokil

13

बस क्रिसेंट ने जो कहा, उसे जोड़ने के लिए

यदि यह केवल एक फ़ंक्शन है जो आप चाहते हैं, तो आप NodeList.prototype.map = Array.prototype.map जैसे कुछ कर सकते हैं

यह मत करो! यह काम करने की गारंटी नहीं है।

कोई भी जावास्क्रिप्ट या DOM / BOM मानक यह नहीं बताता है कि NodeListकंस्ट्रक्टर-फ़ंक्शन वैश्विक / windowप्रॉपर्टी के रूप में भी मौजूद है, या जो इसके NodeListद्वारा लौटाया गया querySelectorAllहै, वह इससे प्राप्त करेगा, या यह कि इसका प्रोटोटाइप राइट है, या यह कि फ़ंक्शन Array.prototype.mapवास्तव में NodeList पर काम करेगा।

एक नोडलिस्ट को 'होस्ट ऑब्जेक्ट' (और आईई और कुछ पुराने ब्राउज़रों में एक है) की अनुमति है। Arrayतरीकों किसी भी जावास्क्रिप्ट 'देशी वस्तु' है कि वह संख्यात्मक को उजागर करता है पर संचालित करने के लिए अनुमति दी जा रही के रूप में परिभाषित कर रहे हैं lengthगुण, पर वे मेजबान वस्तुओं पर काम करने के लिए आवश्यक नहीं कर रहे हैं (और IE में, वे नहीं है)।

यह कष्टप्रद है कि आपको DOM सूचियों पर सभी सरणी विधियाँ नहीं मिलती हैं (वे सभी, न केवल StaticNodeList), लेकिन इसका कोई विश्वसनीय तरीका नहीं है। आपको अपने द्वारा प्राप्त की गई प्रत्येक DOM सूची को मैन्युअल रूप से Array में बदलना होगा:

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
गोली मारो, मैंने ऐसा नहीं सोचा था। धन्यवाद!
केव

मैं +1 से सहमत हूं। सिर्फ एक टिप्पणी, मुझे लगता है कि "var array = []" करने के बजाय "var array = new Array (list.length)" की जगह कोड को और भी छोटा बना देते हैं। लेकिन मुझे दिलचस्पी है अगर आप जानते हैं कि ऐसा करने में कोई समस्या हो सकती है।
मार्को डेमायो

@MarcoDemaio: नहीं, कोई बात नहीं। new Array(n)बस JS terp को एक संकेत देता है कि सरणी का अंत कब तक हो रहा है। इससे इसे पहले से अंतरिक्ष की उस मात्रा को आवंटित करने की अनुमति मिल सकती है, जिसके परिणामस्वरूप संभावित रूप से स्पीडअप हो जाएगा क्योंकि सरणी बढ़ने पर कुछ मेमोरी रिलोकेशन से बचा जा सकता है। मुझे नहीं पता कि क्या यह वास्तव में आधुनिक ब्राउज़रों में मदद करता है, हालांकि ... मुझे संदेह नहीं है कि यह औसत रूप से होगा।
बॉब


2

मुझे लगता है कि आप बस निम्नलिखित कर सकते हैं

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

यह मेरे लिए एकदम सही काम करता है


0

यह एक ऐसा विकल्प है जो मैं यहां दूसरों द्वारा सुझाई गई अन्य संभावनाओं की श्रेणी में जोड़ना चाहता था। यह केवल बौद्धिक मनोरंजन के लिए है और इसकी सलाह नहीं दी जाती है


बस इसके मज़े के लिए , यहाँ querySelectorAllघुटने टेकने और झुकने के लिए "बल" देने का एक तरीका है :

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

अब उस फंक्शन में कदम रखना अच्छा लगता है, जो यह दिखाता है कि बॉस कौन है। अब मुझे नहीं पता कि क्या बेहतर है, एक नया नामांकित फ़ंक्शन रैपर बनाना और फिर आपके सभी कोड का उपयोग उस अजीब नाम (बहुत ज्यादा jQuery- शैली) या एक बार ऊपर की तरह फ़ंक्शन को ओवरराइड करना है ताकि आपका बाकी कोड अभी भी सक्षम हो सके मूल DOM विधि नाम का उपयोग करने के लिए querySelectorAll

मैं इसे किसी भी तरह से सुझा नहीं सकता, जब तक कि आप ईमानदारी से एक [आप क्या जानते हैं] न दें।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.