JQuery के .closest () के समान लेकिन अनुगामी वंशज?


123

वहाँ एक समारोह के समान है, jQuery .closest()लेकिन वंशजों को फंसाने और केवल निकटतम लोगों को वापस करने के लिए?

मुझे पता है कि .find()समारोह है, लेकिन यह सभी संभावित मैचों को लौटाता है, निकटतम नहीं।

संपादित करें:

यहाँ निकटतम की परिभाषा है (कम से कम मेरे लिए) :

पहले स्थान पर सभी बच्चों का पता लगाया जाता है, फिर प्रत्येक व्यक्ति के बच्चों का पता लगाया जाता है।

नीचे दिए गए उदाहरण id='2'में निकटतम .closestवंशज हैंid="find-my-closest-descendant"

<div id="find-my-closest-descendant">
    <div>
        <div class="closest" Id='1'></div>
    </div>
    <div class="closest" Id='2'></div>
</div>

कृपया JSfiddle लिंक देखें ।


4
किस बारे में .find("filter here").eq(0)?
शैडो विजार्ड ईआर फॉर यू

@ शडवॉइडर अच्छी तरह से अगर यह एक गहराई-पहला ट्रैवर्सल करता है, तो परिणाम सूची में शून्य तत्व "निकटतम" नहीं हो सकता है, जो "निकटतम" का अर्थ है।
Pointy

@ यह :firstफ़िल्टर के समान नहीं है ?
शैडो विजार्ड इयर फॉर यू

नहीं, मुझे ऐसा नहीं लगता - मुद्दा यह है कि ": पहला" और ".eq (0)" डोम ऑर्डर को संदर्भित करता है, लेकिन अगर "निकटतम" का अर्थ है "सबसे कम डोम दूर" तो एक "पोता" पहले बच्चे को बाद में "बच्चे" के बजाय वापस कर दिया जाएगा जो चयनकर्ता से मेल खाता है। मैं 100% निश्चित नहीं हूं कि ओपी बेशक है।
इंगित '22

1
वहाँ एक jQuery प्लगइन है जो वास्तव में आपके लिए क्या करता है: plugins.jquery.com/closestDescenders
TLindig

जवाबों:


44

आपकी परिभाषा के अनुसार closest, मैंने निम्नलिखित प्लगइन लिखा है:

(function($) {
    $.fn.closest_descendent = function(filter) {
        var $found = $(),
            $currentSet = this; // Current place
        while ($currentSet.length) {
            $found = $currentSet.filter(filter);
            if ($found.length) break;  // At least one match: break loop
            // Get all children of the current set
            $currentSet = $currentSet.children();
        }
        return $found.first(); // Return first match of the collection
    }    
})(jQuery);

3
JQuery .closest()फ़ंक्शन के विपरीत , यह उस तत्व से मेल खाता है जिस पर इसे बुलाया गया था। मेरी jsfiddle देखें । इसे बदलकर यह jsfiddle$currentSet = this.children(); // Current place
allicarn

21
JQuery के निकटतम () को वर्तमान तत्व से भी मिलान किया जा सकता है।
दाविद होर्वाथ

120

अगर "निकटतम" वंशज से आपका मतलब पहले बच्चे से है तो आप कर सकते हैं:

$('#foo').find(':first');

या:

$('#foo').children().first();

या, किसी विशिष्ट तत्व की पहली घटना को देखने के लिए, आप कर सकते हैं:

$('#foo').find('.whatever').first();

या:

$('#foo').find('.whatever:first');

हालांकि वास्तव में, हमें "निकटतम वंशज" का क्या अर्थ है, इसकी ठोस परिभाषा चाहिए।

उदाहरण के लिए

<div id="foo">
    <p>
        <span></span>
    </p>
    <span></span>
</div>

कौन सा <span>होगा $('#foo').closestDescendent('span')वापसी?


1
विस्तृत उत्तर के लिए @ 999 धन्यवाद, मेरी प्रतिज्ञा की उम्मीद सबसे पहले सभी बच्चों का पता लगाया जाता है, फिर प्रत्येक व्यक्ति के बच्चों की। उदाहरण के लिए, आपको दिया गया दूसरा समय वापस मिल जाएगा
मामू

8
तो सांस-पहले, गहराई नहीं-पहले
jessehouwing

1
बहुत बढ़िया जवाब। मुझे यह जानने में दिलचस्पी होगी कि क्या $('#foo').find('.whatever').first();"चौड़ाई-पहले" या "गहराई-पहले"
कॉलिन

1
इस समाधान के साथ एक समस्या यह है कि jQuery सभी मिलान मानदंडों को खोजेगा और फिर पहले वाले को वापस करेगा। जबकि Sizzle इंजन तेज है, यह अनावश्यक अतिरिक्त खोज का प्रतिनिधित्व करता है। पहले मैच के बाद खोज को शॉर्ट-सर्किट करने और रोकने का कोई तरीका नहीं है।
icfantv

ये सभी उदाहरण गहराई-प्रथम वंशजों का चयन करते हैं, न कि चौड़ाई-प्रथम का, इसलिए इनका उपयोग मूल प्रश्न को हल करने के लिए नहीं किया जा सकता है। मेरे परीक्षण में वे सभी पहले स्थान पर लौटे, दूसरे नहीं।
JrBaconCheez

18

आप चयनकर्ता के findसाथ उपयोग कर सकते हैं :first:

$('#parent').find('p:first');

उपरोक्त पंक्ति <p>के वंशजों में पहला तत्व मिलेगा #parent


2
मुझे लगता है कि यह वही है जो ओपी चाहता था, कोई प्लगइन की आवश्यकता नहीं थी। यह प्रत्येक चयनित तत्व के लिए चयनित तत्व के वंशज में पहले मिलान तत्व का चयन करेगा । इसलिए यदि आपके मूल चयन के साथ 4 मैच हैं, तो आप 4 मैचों का उपयोग करके समाप्त कर सकते हैं find('whatever:first')
रस्टीटम्स

3

इस दृष्टिकोण के बारे में क्या?

$('find-my-closest-descendant').find('> div');

यह "डायरेक्ट चाइल्ड" चयनकर्ता मेरे लिए काम करता है।


ओपी विशेष रूप से कुछ के लिए पूछते हैं जो वंशज का पता लगाते हैं, जो यह जवाब नहीं देता है। यह एक अच्छा उत्तर हो सकता है, लेकिन इस विशेष प्रश्न के लिए नहीं।
१२:०४ पर ४:54

@KjetilNordin शायद इसलिए कि मेरे जवाब के बाद से प्रश्न संपादित किया गया था। इसके अलावा, निकटतम वंशज की परिभाषा अभी भी इतनी स्पष्ट नहीं है, कम से कम मेरे लिए।
klawipo

मैं निश्चित रूप से सहमत हूं। निकटतम वंश का अर्थ बहुत सारी चीजें हो सकती हैं। इस मामले में माता-पिता के साथ आसान, जहां प्रत्येक स्तर पर तत्वों के लिए हमेशा एक ही माता-पिता होते हैं। और आप संभावित संपादन के बारे में भी सही हैं। अब मैं देख रहा हूं कि मेरी टिप्पणी किसी भी तरह से सहायक नहीं थी, खासकर क्योंकि पहले से ही उसी प्रकार का बयान था।
कूदता है

1

शुद्ध जेएस समाधान (ईएस 6 का उपयोग करके)।

export function closestDescendant(root, selector) {
  const elements = [root];
  let e;
  do { e = elements.shift(); } while (!e.matches(selector) && elements.push(...e.children));
  return e.matches(selector) ? e : null;
}

उदाहरण

निम्नलिखित संरचना को ध्यान में रखते हुए:

div == $ 0
$ Div == $ 1
├── ├── div
।। Div.findme == $ 4
├── ├── div
└── └── div
├── div.findme == $ 2
├── ├── div
└── └── div
└── div == $ 3
    ├── div
    ├── div
    └── div
closestDescendant($0, '.findme') === $2;
closestDescendant($1, '.findme') === $4;
closestDescendant($2, '.findme') === $2;
closestDescendant($3, '.findme') === null;


यह समाधान काम करता है, लेकिन मैंने एक वैकल्पिक ES6 समाधान यहां पोस्ट किया है ( stackoverflow.com/a/55144581/2441655 ) क्योंकि मुझे यह पसंद नहीं है: 1) whileसाइड-इफेक्ट वाले अभिव्यक्ति। 2) .matchesकेवल एक के बजाय दो लाइनों पर चेक का उपयोग ।
वेनरिक्स

1

मामले में किसी को शुद्ध जेएस समाधान (j6 के बजाय ES6 का उपयोग करके) की तलाश है, यहां मैं एक का उपयोग कर रहा हूं:

Element.prototype.QuerySelector_BreadthFirst = function(selector) {
    let currentLayerElements = [...this.childNodes];
    while (currentLayerElements.length) {
        let firstMatchInLayer = currentLayerElements.find(a=>a.matches && a.matches(selector));
        if (firstMatchInLayer) return firstMatchInLayer;
        currentLayerElements = currentLayerElements.reduce((acc, item)=>acc.concat([...item.childNodes]), []);
    }
    return null;
};

हे, मेरे ध्यान में अपना जवाब लाने के लिए धन्यवाद! आप सही हैं, अपने आप को पीछे देखते हुए, ऐसा महसूस होता है कि यह बहुत चालाक होने की कोशिश कर रहा है और वास्तव में अजीब है ( matchesदो बार दौड़ना भी मुझे काफी परेशान करता है)। आपके लिए +1 :)
ccjmne

0

मुझे लगता है कि पहले आपको परिभाषित करना होगा कि "निकटतम" का क्या मतलब है। यदि आपका मतलब है कि आपके मापदंड से मेल खाने वाले वंशज नोड जो कि माता-पिता की कड़ी के मामले में सबसे कम दूरी है, तो ": पहले" या ".eq (0)" का उपयोग करना जरूरी नहीं होगा:

<div id='start'>
  <div>
    <div>
      <span class='target'></span>
    </div>
  </div>
  <div>
    <span class='target'></span>
  </div>
</div>

उस उदाहरण में, दूसरा ".getget" <span>तत्व "प्रारंभ" के लिए "करीब" है <div>, क्योंकि यह केवल एक अभिभावक हॉप है। यदि आपके "निकटतम" से इसका मतलब है, तो आपको फ़िल्टर फ़ंक्शन में न्यूनतम दूरी ढूंढनी होगी। एक jQuery चयनकर्ता से परिणाम सूची हमेशा DOM ऑर्डर में होती है।

शायद:

$.fn.closestDescendant = function(sel) {
  var rv = $();
  this.each(function() {
    var base = this, $base = $(base), $found = $base.find(sel);
    var dist = null, closest = null;
    $found.each(function() {
      var $parents = $(this).parents();
      for (var i = 0; i < $parents.length; ++i)
        if ($parents.get(i) === base) break;
      if (dist === null || i < dist) {
        dist = i;
        closest = this;
      }
    });
    rv.add(closest);
  });
  return rv;
};

जिस तरह से यह परिणाम वस्तु बनाता है की वजह से एक हैक प्लगइन की तरह है, लेकिन विचार यह है कि आप सभी मिलान तत्वों में से सबसे छोटा पैतृक पथ खोजने के लिए मिल गया है। यह कोड <चेक के कारण DOM ट्री में बचे तत्वों की ओर पूर्वाग्रह करता है ; <=पूर्वाग्रह सही होगा।


0

मैंने इसे पकाया, स्थिति चयनकर्ताओं के लिए कोई कार्यान्वयन नहीं (उन्हें matchesSelectorअभी और अधिक की आवश्यकता है ):

डेमो: http://jsfiddle.net/TL4Bq/3/

(function ($) {
    var matchesSelector = jQuery.find.matchesSelector;
    $.fn.closestDescendant = function (selector) {
        var queue, open, cur, ret = [];
        this.each(function () {
            queue = [this];
            open = [];
            while (queue.length) {
                cur = queue.shift();
                if (!cur || cur.nodeType !== 1) {
                    continue;
                }
                if (matchesSelector(cur, selector)) {
                    ret.push(cur);
                    return;
                }
                open.unshift.apply(open, $(cur).children().toArray());
                if (!queue.length) {
                    queue.unshift.apply(queue, open);
                    open = [];
                }
            }
        });
        ret = ret.length > 1 ? jQuery.unique(ret) : ret;
        return this.pushStack(ret, "closestDescendant", selector);
    };
})(jQuery);

वहाँ शायद कुछ कीड़े है, हालांकि, यह बहुत परीक्षण नहीं किया।


0

रोब डब्ल्यू का जवाब मेरे लिए काफी काम का नहीं था। मैंने इसे इसके लिए अनुकूलित किया जिसने काम किया।

//closest_descendent plugin
$.fn.closest_descendent = function(filter) {
    var found = [];

    //go through every matched element that is a child of the target element
    $(this).find(filter).each(function(){
        //when a match is found, add it to the list
        found.push($(this));
    });

    return found[0]; // Return first match in the list
}

मेरे लिए यह दिखता है कि वास्तव में चाहते .find(filter).first(), केवल धीमी;)
मथायस Samsel

हाँ, तुम सही हो। मैंने यह तब लिखा था जब मैं कोडिंग के लिए बहुत नया था। मुझे लगता है कि मैं इस जवाब को हटा दूंगा
डैनियल टोनन

0

यदि चयनकर्ता से मेल खाता है तो मैं लक्ष्य को शामिल करने के लिए निम्नलिखित का उपयोग करूंगा:

    var jTarget = $("#doo");
    var sel = '.pou';
    var jDom = jTarget.find(sel).addBack(sel).first();

मार्कअप:

<div id="doo" class="pou">
    poo
    <div class="foo">foo</div>
    <div class="pou">pooo</div>
</div>

0

भले ही यह एक पुराना विषय है लेकिन मैं अपने करीबी को लागू करने का विरोध नहीं कर सका। कम से कम यात्रा (पहले सांस) के साथ पहले पाया वंशज। एक पुनरावर्ती (व्यक्तिगत पसंदीदा) है, अन्य एक टूडू सूची का उपयोग करके ताकि पुनरावृत्ति बिना jQquery एक्सटेंशन के रूप में हो।

आशा है कि किसी को लाभ होगा।

नोट: पुनरावर्ती स्टैक ओवरफ़्लो हो जाता है, और मैंने दूसरे में सुधार किया, अब दिए गए पिछले उत्तर के लिए simular।

jQuery.fn.extend( {

    closestChild_err : function( selector ) { // recursive, stack overflow when not found
        var found = this.children( selector ).first();
        if ( found.length == 0 ) {
            found = this.children().closestChild( selector ).first(); // check all children
        }
        return found;
    },

    closestChild : function( selector ) {
        var todo = this.children(); // start whith children, excluding this
        while ( todo.length > 0 ) {
            var found = todo.filter( selector );
            if ( found.length > 0 ) { // found closest: happy
                return found.first();
            } else {
                todo = todo.children();
            }
        }
        return $();
    },

});  

0

निम्न प्लगइन nth निकटतम वंशज देता है।

$.fn.getNthClosestDescendants = function(n, type) {
  var closestMatches = [];
  var children = this.children();

  recursiveMatch(children);

  function recursiveMatch(children) {
    var matches = children.filter(type);

    if (
      matches.length &&
      closestMatches.length < n
    ) {
      var neededMatches = n - closestMatches.length;
      var matchesToAdd = matches.slice(0, neededMatches);
      matchesToAdd.each(function() {
        closestMatches.push(this);
      });
    }

    if (closestMatches.length < n) {
      var newChildren = children.children();
      recursiveMatch(newChildren);
    }
  }

  return closestMatches;
};

0

मैं इसी तरह के समाधान की तलाश कर रहा था (मैं सभी करीबी वंशज चाहता था, अर्थात पहले प्रथम + सभी मैच चाहे वह किस स्तर तक मौजूद हो), यहाँ मैं क्या कर रहा हूँ:

var item = $('#find-my-closest-descendant');
item.find(".matching-descendant").filter(function () {
    var $this = $(this);
    return $this.parent().closest("#find-my-closest-descendant").is(item);
}).each(function () {
    // Do what you want here
});

आशा है कि ये आपकी मदद करेगा।




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