2010-09-01 25 views
16

मैं इस समारोह मिला एक cssPath प्राप्त करने के लिए से सीएसएस पथ प्राप्त करें:डोम तत्व

var cssPath = function (el) { 
    var path = []; 

    while (
    (el.nodeName.toLowerCase() != 'html') && 
    (el = el.parentNode) && 
    path.unshift(el.nodeName.toLowerCase() + 
     (el.id ? '#' + el.id : '') + 
     (el.className ? '.' + el.className.replace(/\s+/g, ".") : '')) 
); 
    return path.join(" > "); 
} 
console.log(cssPath(document.getElementsByTagName('a')[123])); 

लेकिन मैं कुछ इस तरह है:

html > body > div#div-id > div.site > div.clearfix > ul.choices > li

लेकिन पूरी तरह से सही हो सकता है, यह इस तरह दिखना चाहिए :

html > body > div#div-id > div.site:nth-child(1) > div.clearfix > ul.choices > li:nth-child(5)

क्या किसी को जावास्क्रिप्ट में इसे लागू करने का कोई विचार है?

+2

यह शायद होना चाहिए ': eq (1)' या ': n वें वाले बच्चे (2)' बजाय '[1 ] 'अगर आप एक सीएसएस चयनकर्ता चाहते हैं। –

+0

या सिर्फ तत्व को जावास्क्रिप्ट के साथ एक अद्वितीय आईडी दें? मैं देख सकता हूं कि क्यों सीएसएसपाथ फ़ायरबग प्लगइन या कुछ के रूप में उपयोगी हो सकता है, लेकिन नियमित कोड के लिए, आईडी शुरू करना सबसे प्रभावी है। – BGerrissen

+0

असल में, मुझे विश्वास है कि एक फायरबग प्लगइन है जो फ़ायरफ़िंडर नामक तत्व से सीएसएसपाथ प्राप्त करता है; ओपी – BGerrissen

उत्तर

11

हमेशा सही तत्व प्राप्त करने के लिए, आपको चयनकर्ताओं के लिए :nth-child() या :nth-of-type() का उपयोग करने की आवश्यकता होगी जो विशिष्ट रूप से तत्व की पहचान नहीं करते हैं। तो यह प्रयास करें:

var cssPath = function(el) { 
    if (!(el instanceof Element)) return; 
    var path = []; 
    while (el.nodeType === Node.ELEMENT_NODE) { 
     var selector = el.nodeName.toLowerCase(); 
     if (el.id) { 
      selector += '#' + el.id; 
     } else { 
      var sib = el, nth = 1; 
      while (sib.nodeType === Node.ELEMENT_NODE && (sib = sib.previousSibling) && nth++); 
      selector += ":nth-child("+nth+")"; 
     } 
     path.unshift(selector); 
     el = el.parentNode; 
    } 
    return path.join(" > "); 
} 

आप उनकी संगत संदर्भ में अद्वितीय तत्वों के लिए (जैसे TITLE, BASE, CAPTION, आदि) की जांच के लिए एक नियमित जोड़ सकते हैं।

+0

हाँ यह बहुत अच्छा लग रहा है। क्या यह आईई के साथ भी अनुकूल है? – jney

+0

@ जेनी: यदि आपका मतलब है ': nth-child()' selector, तो नहीं। – Gumbo

17

ऊपर दिए गए उत्तर में वास्तव में एक बग है - जबकि लूप समय-समय पर टूट जाता है जब यह एक गैर-तत्व नोड (उदा। एक टेक्स्ट नोड) का सामना करता है जिसके परिणामस्वरूप गलत सीएसएस चयनकर्ता होता है।

यहाँ एक उन्नत संस्करण है कि है कि समस्या के साथ साथ ठीक करता है:

  • बंद हो जाता है जब यह करने के लिए आवंटित एक आईडी के साथ पहली पूर्वज तत्व
  • का उपयोग करता है nth-of-type() चयनकर्ताओं अधिक पठनीय बनाने के लिए का सामना करना पड़ता
 
    var cssPath = function(el) { 
     if (!(el instanceof Element)) 
      return; 
     var path = []; 
     while (el.nodeType === Node.ELEMENT_NODE) { 
      var selector = el.nodeName.toLowerCase(); 
      if (el.id) { 
       selector += '#' + el.id; 
       path.unshift(selector); 
       break; 
      } else { 
       var sib = el, nth = 1; 
       while (sib = sib.previousElementSibling) { 
        if (sib.nodeName.toLowerCase() == selector) 
         nth++; 
       } 
       if (nth != 1) 
        selector += ":nth-of-type("+nth+")"; 
      } 
      path.unshift(selector); 
      el = el.parentNode; 
     } 
     return path.join(" > "); 
    } 
+0

': nth-of-type()' 'nth-child() 'से अलग-अलग काम करता है - कभी-कभी यह एक दूसरे के साथ बदलने का एक साधारण मामला नहीं है। – BoltClock

+1

'if (nth! = 1) 'अच्छा नहीं है, अल्ट्रा-विशिष्ट पथ रखने के लिए आपको हमेशा बच्चे का उपयोग करना चाहिए, भले ही यह 1 है। – Sych

+0

@Sych, क्यों? ठीक काम करने लगता है और 'एचटीएमएल' में 'nth-of-type'' जोड़ना उदाहरण के लिए काम नहीं करेगा। – jtblin

5

दो अन्य प्रदान किए गए उत्तरों में ब्राउजर संगतता के साथ कुछ धारणाएं थीं जिनमें मैं भाग गया था। कोड के नीचे एनएच-चाइल्ड का उपयोग नहीं किया जाएगा और पिछले एलीमेंट सिब्लिंग चेक भी होगा।

function previousElementSibling (element) { 
    if (element.previousElementSibling !== 'undefined') { 
    return element.previousElementSibling; 
    } else { 
    // Loop through ignoring anything not an element 
    while (element = element.previousSibling) { 
     if (element.nodeType === 1) { 
     return element; 
     } 
    } 
    } 
} 
function getPath (element) { 
    // False on non-elements 
    if (!(element instanceof HTMLElement)) { return false; } 
    var path = []; 
    while (element.nodeType === Node.ELEMENT_NODE) { 
    var selector = element.nodeName; 
    if (element.id) { selector += ('#' + element.id); } 
    else { 
     // Walk backwards until there is no previous sibling 
     var sibling = element; 
     // Will hold nodeName to join for adjacent selection 
     var siblingSelectors = []; 
     while (sibling !== null && sibling.nodeType === Node.ELEMENT_NODE) { 
     siblingSelectors.unshift(sibling.nodeName); 
     sibling = previousElementSibling(sibling); 
     } 
     // :first-child does not apply to HTML 
     if (siblingSelectors[0] !== 'HTML') { 
     siblingSelectors[0] = siblingSelectors[0] + ':first-child'; 
     } 
     selector = siblingSelectors.join(' + '); 
    } 
    path.unshift(selector); 
    element = element.parentNode; 
    } 
    return path.join(' > '); 
} 
3

रिवर्स सीएसएस चयनकर्ता लुकअप करना एक स्वाभाविक रूप से मुश्किल चीज है। मैं आम तौर पर समाधान के दो प्रकार का सामना करना पड़ा:

  1. तत्व के नाम, कक्षाओं का एक संयोजन से बाहर चयनकर्ता स्ट्रिंग इकट्ठा करने के लिए डोम पेड़ पर जाएँ, और id या name विशेषता। इस विधि के साथ समस्या यह है कि इसका चयनकर्ताओं में हो सकता है जो कई तत्वों को वापस कर देते हैं, जो इसे काट नहीं देंगे अगर हमें केवल एक अद्वितीय तत्व चुनने की आवश्यकता होती है।

  2. nth-child() या nth-of-type() का उपयोग कर चयनकर्ता स्ट्रिंग को इकट्ठा करें, जिसके परिणामस्वरूप बहुत लंबे चयनकर्ता हो सकते हैं। ज्यादातर मामलों में अब एक चयनकर्ता उच्च विशिष्टता है, और डीओएम संरचना में परिवर्तन होने पर विशिष्टता उतनी अधिक होगी जितनी अधिक होगी।

नीचे दिए गए समाधान इन दोनों मुद्दों से निपटने का प्रयास है। यह एक संकर दृष्टिकोण है जो एक अद्वितीय सीएसएस चयनकर्ता आउटपुट करता है (यानी, document.querySelectorAll(getUniqueSelector(el)) हमेशा एक आइटम आइटम सरणी वापस करना चाहिए)। जबकि लौटा चयनकर्ता स्ट्रिंग जरूरी नहीं है, यह सीएसएस चयनकर्ता दक्षता की ओर एक आंख से ली गई है जबकि nth-of-type() और nth-child() को प्राथमिकता देकर विशिष्टता को संतुलित किया गया है।

आप aAttr सरणी को अद्यतन करके चयनकर्ता में शामिल करने के लिए कौन से विशेषताओं को शामिल कर सकते हैं निर्दिष्ट कर सकते हैं। न्यूनतम ब्राउज़र आवश्यकता आईई 9 है।

function getUniqueSelector(elSrc) { 
    if (!(elSrc instanceof Element)) return; 
    var sSel, 
    aAttr = ['name', 'value', 'title', 'placeholder', 'data-*'], // Common attributes 
    aSel = [], 
    // Derive selector from element 
    getSelector = function(el) { 
     // 1. Check ID first 
     // NOTE: ID must be unique amongst all IDs in an HTML5 document. 
     // https://www.w3.org/TR/html5/dom.html#the-id-attribute 
     if (el.id) { 
     aSel.unshift('#' + el.id); 
     return true; 
     } 
     aSel.unshift(sSel = el.nodeName.toLowerCase()); 
     // 2. Try to select by classes 
     if (el.className) { 
     aSel[0] = sSel += '.' + el.className.trim().replace(/ +/g, '.'); 
     if (uniqueQuery()) return true; 
     } 
     // 3. Try to select by classes + attributes 
     for (var i=0; i<aAttr.length; ++i) { 
     if (aAttr[i]==='data-*') { 
      // Build array of data attributes 
      var aDataAttr = [].filter.call(el.attributes, function(attr) { 
      return attr.name.indexOf('data-')===0; 
      }); 
      for (var j=0; j<aDataAttr.length; ++j) { 
      aSel[0] = sSel += '[' + aDataAttr[j].name + '="' + aDataAttr[j].value + '"]'; 
      if (uniqueQuery()) return true; 
      } 
     } else if (el[aAttr[i]]) { 
      aSel[0] = sSel += '[' + aAttr[i] + '="' + el[aAttr[i]] + '"]'; 
      if (uniqueQuery()) return true; 
     } 
     } 
     // 4. Try to select by nth-of-type() as a fallback for generic elements 
     var elChild = el, 
     sChild, 
     n = 1; 
     while (elChild = elChild.previousElementSibling) { 
     if (elChild.nodeName===el.nodeName) ++n; 
     } 
     aSel[0] = sSel += ':nth-of-type(' + n + ')'; 
     if (uniqueQuery()) return true; 
     // 5. Try to select by nth-child() as a last resort 
     elChild = el; 
     n = 1; 
     while (elChild = elChild.previousElementSibling) ++n; 
     aSel[0] = sSel = sSel.replace(/:nth-of-type\(\d+\)/, n>1 ? ':nth-child(' + n + ')' : ':first-child'); 
     if (uniqueQuery()) return true; 
     return false; 
    }, 
    // Test query to see if it returns one element 
    uniqueQuery = function() { 
     return document.querySelectorAll(aSel.join('>')||null).length===1; 
    }; 
    // Walk up the DOM tree to compile a unique selector 
    while (elSrc.parentNode) { 
    if (getSelector(elSrc)) return aSel.join(' > '); 
    elSrc = elSrc.parentNode; 
    } 
} 
0

मुझे किसी भी तरह अनावश्यक उत्परिवर्तन के कारण सभी कार्यान्वयन अपठनीय पाते हैं।

(defn element? [x] 
    (and (not (nil? x)) 
     (identical? (.-nodeType x) js/Node.ELEMENT_NODE))) 

(defn nth-child [el] 
    (loop [sib el nth 1] 
    (if sib 
     (recur (.-previousSibling sib) (inc nth)) 
     (dec nth)))) 

(defn element-path 
    ([el] (element-path el [])) 
    ([el path] 
    (if (element? el) 
    (let [tag (.. el -nodeName (toLowerCase)) 
      id (and (not (string/blank? (.-id el))) (.-id el))] 
     (if id 
     (element-path nil (conj path (str "#" id))) 
     (element-path 
      (.-parentNode el) 
      (conj path (str tag ":nth-child(" (nth-child el) ")"))))) 
    (string/join " > " (reverse path))))) 

जावास्क्रिप्ट: यहाँ मैं मेरा ClojureScript और जे एस में प्रदान

const isElement = (x) => x && x.nodeType === Node.ELEMENT_NODE; 

const nthChild = (el, nth = 1) => { 
    if (el) { 
    return nthChild(el.previousSibling, nth + 1); 
    } else { 
    return nth - 1; 
    } 
}; 

const elementPath = (el, path = []) => { 
    if (isElement(el)) { 
    const tag = el.nodeName.toLowerCase(), 
      id = (el.id.length != 0 && el.id); 
    if (id) { 
     return elementPath(
     null, path.concat([`#${id}`])); 
    } else { 
     return elementPath(
     el.parentNode, 
     path.concat([`${tag}:nth-child(${nthChild(el)})`])); 
    } 
    } else { 
    return path.reverse().join(" > "); 
    } 
}; 
संबंधित मुद्दे