How to detect if an element is accompanied by text with jQuery?

Asked

Viewed 466 times

4

I have a certain div.box that will be repeated several times, and some of them have a link (a.link) with a specific class.

Example:

<div class="box" data-id="1">
Primeiro texto
</div>

<div class="box" data-id="2">
Segundo texto <a class="link">Clique aqui</a>
</div>

<div class="box" data-id="3">
<a class="link">Clique aqui</a>
</div>

<div class="box" data-id="4">
Testando
</div>

Das divs above, I need to detect that, around the a.link, no text exists. That is, from the above example could return only to div.box whose the data-id is equal to 3.

How can I detect it through jQuery?

I mean, I have to make sure that the div.box contains only that element within it a.link (and nothing more).

3 answers

3


There are several conditions that need to be checked. The way I think is most right is to go through all the childNodes and check how many there are that are not empty. At the same time check if at least one of them has the desired element/class:

function temSoClasse(elInicial, seletor) {
    return = $(elInicial).filter(function () {
        var hasLink = false;
        var nodes = $(this.childNodes).filter(function (i, el) {
            if (el.nodeType == 1) {
                if ($(el).parent().find(seletor).length) hasLink = true;
                return true;
            }
            return el.textContent.trim().length;
        });
        return nodes.length == 1 && hasLink;
    });
}

And then you can use it like this:

var els = temSoClasse('.box', 'a.link');
els.css('color', 'blue'); // vai mudar a côr do elemento que se procura

jsFiddle: http://jsfiddle.net/kps5xbec/


A more compressed version could be like this:

function temSoClasse(elInicial, seletor) {
    return $(elInicial).filter(function () {
        return this.querySelector(seletor) && $(this.childNodes).filter(function (i, el) {
            return el.textContent.trim().length;
        }).length == 1;
    });
}

jsFiddle: http://jsfiddle.net/kps5xbec/8/

  • 1

    +1. The idea of the function is wonderful. This makes the code reusable.

  • @Wallacemaxters great! Next time you can leave the question open longer :P

  • Yeah, @bigown had been talking for a long time for me to stop this craze. Sometimes I think there won’t be many who will answer

  • @Wallacemaxters I sometimes see the question at work and only when I get home do I have time to answer... you have nothing to lose :)

  • 3

    @Wallacemaxters nothing prevents you from exchanging acceptance. Accept the one that is best.

  • If you give a improved in code, you a little performance.

  • @Dontvotemedown is true that sometimes I do more complete for the user to understand the steps. I could optimise more but I don’t think it’s safe to say that one is faster than the other: http://jsfiddle.net/kps5xbec/5/

  • Yes, your answers are great. In this case what differed from mine was having created a function and I did not, but it was the same, I think. The two codes get really close(1 and 2, I don’t usually use Date for these tests), but I can’t deny that $(el).parent().find(seletor) can be executed outside the second filter(), for within it is reductive.

  • @Dontvotemedown you’re right that this .find() may be out of the filter(). Good point. I joined in response a new version.

Show 4 more comments

2

I think I found a way:

var list = $(".box").filter(function(i, el) 
{
    return Array.from(el.childNodes).filter(function(cn) 
    {
        return  cn.textContent.trim() != "" && 
                cn.nodeType == 3;
    }).length == 0;
});

Fiddle

Explanation:

  1. On top of the dial $(".box") we rotate a filter(), since the intention is to filter the elements that do not have text between the element a;
  2. For each element, we take the collection childNodes which contains the text elements that we will verify;
  3. To nodeList not quite an array, so to be able to run one more filter() create an array with Array.from(), and then we rotate the filter();
  4. The condition of the latter filter() is he to be of the text type(nodeType == 3) and not be empty(textContent.trim() != ""), because there is textNodes only with line break;
  5. The latter filter() being empty(.length == 0), we are sure - so I hope haha - of him not having textNodes immadiately below it.

UPDATE

Well, if you want to make the function reusable, you can create a method in jQuery, I think it is more correct and within the standard:

$.fn.checarElemento = function(checarFilhos)
{
    return $(this).filter(function(i, el) 
    {
        return $(el).find(checarFilhos).length > 0 && Array.from(el.childNodes).filter(function(cn) 
        {
            return  cn.textContent.trim() != "" && 
                    cn.nodeType == 3;
        }).length == 0;
    });
};

And to ensure there is the desired selector within the element in question, I added the simple condition $(el).find(checarFilhos).length > 0, which, if not satisfied, does not rotate the filter() about the childNodes. Micro optimization, perhaps? Usage:

$(".box").checarElemento("a.link");

Fiddle

  • I had been thinking of a gambiarra. Inside the filter: $(this).html().substr(0, 2) == '<a'

  • @Wallacemaxters man, sometimes can work yes, even if you will always use for the same structure. The way I did intended to work in a generic way, besides that it was a good exercise :). I updated with explanation.

  • Very good exercise. Inspired me to give an answer :). Your answer gave me an idea.

  • @Wallacemaxters really good. The idea is to learn and evolve always.

  • @Wallacemaxters update ^^

  • How cool! You can do with the find too! I usually use the filter with a closure. I didn’t know you could use it with find, that’s pretty interesting!

Show 1 more comment

2

As @Dontvotemedown said, "... this is a good exercise".

$(".box").filter(function(i) 
{
    var $that =  $(this);

    var node = $that.contents().get(0);

     if ($that.contents().length == 1 && $(node).is('a')) {
        return true;
     }

     return false;

});

When we use the function $.contents, jQuery returns the nodes present within that element.

So I check if there’s only one knot with $(this).contents().length == 1.

Then check if the node is element whose tag is a, through the $(node).is('a').

Through function $.is is that we check if the element is a link. How node is not a jQuery selector, it is necessary to use it with jQuery through the $(...).

The $(this).contents().get(0) is in charge of taking the first element.

So we have the verification that the div.box has a node, and if that node is a link.

Updating: Following the example of @Dontvotemedown’s reply, we could also, rather than use $(node).is('a'), do as follows:

 if ($that.contents().length == 1 && node.nodeType == 3) {
     return true;
 }

Browser other questions tagged

You are not signed in. Login or sign up in order to post.