Question

I am importing via RSS various news articles from various sources (all beyond my coding/formatting control) into my client's database (all legal!) and I am attempting to standardize some of their styling for presentation on client's site via jQuery. For example...

Many many sites use various header tags seemingly for formatting only. When these are pulled into client's presentation they look like crap, so I have working code to find and replace these code blocks with a styled paragraph (the semantics of this code are not important, we mainly link back to original source), and it works fine. Code:

$(document).ready(function() {
    $('#selector h1, #selector h2:not(.article_name), #selector h3, #selector h4, #selector h5').replaceWith(function() {
        return '<p class="h_replace">' + $(this).text() + '</p>';
    });
});

The .h_replace CSS simply enlarges the <p> and adds some other attributes to make it more heading-like. So far, so good.

But here's where I'm hitting a wall: same sites also often use a <p> immediately followed with a child <b> as the only content to substitute/act as a heading.

I want to find and replace these code blocks with a styled paragraph (as above), but I am unsuccessful selecting any <p> that contains ONLY a <b> element. That is to say I want to select this:

<p><b>Some content here.</b></p>

and not this:

<p><b>Some Bold Text:</b> and some NOT bold text.</p>

In the first example, the <p> contains ONLY a child <b> element. In the second example the <p> contains a child <b> element as well as paragraph text not enclosed in the child <b>.

I am looking to programmatically select only the first type of <p>, and not the second.

Any ideas?

Here's what I've got so far, but it selects both instances:

$(document).ready(function() {
    $('#selector p b:only-child').parent('p').replaceWith(function() {
        return '<p class="h_replace">' + $(this).text() + '</p>';
    });
});

The :only-child selector I thought would be a magic bullet, but then, of course, the <b> IS the only child of the <p> in both cases.

I need to select paragraphs that contain only and nothing but <b> content.

I hope that's clear. As always, any help is always greatly appreciated.

Was it helpful?

Solution

Try this:

var els = $('p').filter(function() {
    return /^<(b)>.+<\/\1>$/.test($.trim($(this).html()));
});

It will get you all p that have an only children b without any text nodes. You can change (b) with whatever tag in the code above.

Edit: Don't know why the downvote but this works just fine:

  • <p><b>Something</b></p> // true
  • <p> <b>Something</b> </p> // true
  • <p><b>Something</b> lorem</p> // false
  • <p>lorem <b>Something</b></p> // false
  • <p><span><b>Something</b></span></p> // false

Demo: http://jsfiddle.net/elclanrs/LFt8x/

OTHER TIPS

You can use filter() with contents() in order to verify that the <p> element only has one child node and that node is a <b> element:

$("#selector p").filter(function() {
    var $contents = $(this).contents();
    return $contents.length == 1 && $contents.eq(0).is("b"); 
}).replaceWith(function() {
    return "<p class='h_replace'>" + $(this).text() + "</p>";
});

elclanrs answer did the trick to select the text properly. Again, demo page is here.

Final code:

$(document).ready(function() {

    var pbs = $('#selector p').filter(function() {
        return /^<(b)>.+<\/\1>$/.test($.trim($(this).html()));
    });

    pbs.replaceWith(function() {
        return '<p class="h_replace">' + $(this).text() + '</p>';
    }).addClass('h_replace2').css('width', '100%');

});

StackOverflow is the greatest. Thanks to everyone who piped in! Saved me hours (and by hours I mean days!!) Thanks!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top