views:

167

answers:

4

I'm a little confused about which jQuery method and/or selectors to use when trying to select an element, and then remove certain descendant elements from the wrapped set.

For example, given the following HTML:

<div id="article">
  <div id="inset">
    <ul>
      <li>This is bullet point #1.</li>
      <li>This is bullet point #2.</li>
      <li>This is bullet point #3.</li>
    </ul>
  </div>
  <p>This is the first paragraph of the article</p>
  <p>This is the second paragraph of the article</p>
  <p>This is the third paragraph of the article</p>
</div>

I want to select the article:

var $article = $('#article');

but then remove <div id="inset"></div> and its descendants from the wrapped set. I tried the following:

var $article = $('#article').not('#inset');

but that didn't work, and in retrospect, I think I can see why. I also tried using remove() unsuccessfully.

What would be the correct way to do this?

Ultimately, I need to set this up in such a way that I can define a configuration array, such as:

var selectors = [
  {
    select: '#article',
    exclude: ['#inset']
  }
];

where select defines a single element that contains text content, and exclude is an optional array that defines one or more selectors to disregard text content from.

Given the final wrapped set with the excluded elements removed, I would like to be able to call jQuery's text() method to end up with the following text:

This is the first paragraph of the article.
This is the second paragraph of the article.
This is the third paragraph of the article.

The configuration array doesn't need to work exactly like that, but it should provide roughly equivalent configuration potential.

Thanks for any help you can provide!

+2  A: 

if you want to remove anything in #article but #inset use:

$('#article > *:not(#inset)').remove() // selects all direct children of #article but not #inset and removes them

see an example here: http://jsfiddle.net/zwPsD/

if want to apply this rule to more then one DOM element you can chain them:

$('#article, #article2, #article3, #etc').find('> *').not('#inset, #that, #and. #there').remove()

you can find an example of this here: http://jsfiddle.net/ZNjdE/

and with a simple each you can extract the text: http://jsfiddle.net/ZNjdE/2/

meo
Thanks, meo - please see my comment on the original posting for clarification of my goals. It appears that your solution would only apply to direct children of `#article`, though. How would I remove descendants that can be at any level? For example, what if there was another `<div>` between `#article` and `#inset`?
Bungle
Thanks for your help, meo - this answer was very useful to me, although I ended up going with PeterTheNiceGuy's approach since it worked better for the specific requirements of my project. I appreciate the help!
Bungle
A: 

Try something like this.

$('#article').children(':not(#inset)').each(function(){
    alert($(this).text());
});

If you want to do it with an object:

var selectors = {
    select: '#article',
    exclude: ['#inset', 'p']
};

$(selectors.select).children(':not('+selectors.exclude.join(',')+')').each(function(){
    alert($(this).text());
});

EDIT

To get any level of ancestor, you could add extra selectors and use find(). Eg.

$('#article').find('li:first, :not(#inset, #inset *)').each(function(){
    alert($(this).text());
});

With this you'd be excluding #inset and all #inset's ancestors except the first li. It won't quite work with the selectors object from before though because you're excluding a group of elements and then including some of the excluded ones. You could do it with three elements in the object:

var selectors = {select: ... , exclude: ... , includeFromExcluded: ...};
munch
Thanks, munch. How would I exclude descendants of `#article` that may be at any level, though? IIRC, `children()` only traverses the DOM down one level. Please see my comment on meo's answer for elaboration.
Bungle
+1  A: 

I suppose you do not want to modify the original HTML by removing elements from it, but you want to just get the content of article without the inset. Thats why I would use clone() to get a copy of the article and then remove the inset from it.

Like this:

$("#article").clone().find("#inset").remove().end().text()
  • $("#article") selects the article div, clone creates a copy,
  • find gets the children to remove (you could also use children),
  • remove(), removes the selected inset,
  • end() goes back to the original selection.

At the end I just added text() as you mentioned you wanted to do that.

PeterTheNiceGuy
Thanks, PeterTheNiceGuy - this looks like it could work! Working on some tests now...
Bungle
This approach worked best for my project. Thanks again!
Bungle
A: 

Unless I am missing something, why can't you select all of the <p> elements within the article div?

$("#article p")

If that is unacceptable, I think you are looking for the filter function...

$("#article").filter(":not(#inset)")

Note: you can have multiple selectors within the :not() selector. They just have to be comma delimited, so this approach should accomodate your configurational needs.

Josh Stodola
Thanks, Josh. I can't get this to work, though - have I done something wrong? (Firebug/Safari console needed to see the results): http://troy.onespot.com/static/exclusion_selectors.html
Bungle
FWIW, as my understanding of `filter()` goes, it's going to reduce the wrapped set to those that match the selector. In this case, it appears to be only operating on `#article`, which is not filtered since it matches the selector. I think I would need to apply `filter()` to the descendants of `#article`, but I'm not sure how to do that without ending up with duplicated text content when I use text() on the final/reduced wrapped set.
Bungle