6

Consider the following HTML where IDs #p1, #p2 and #p3 are siblings (see fiddle):

<div id="container">
    <span id="p1">Paragraph 1</span>
    <span id="p2">Paragraph 2</span>
    This is just loose text.
    <p id="p3">Paragraph 3</p>
</div>

These are my definitions of strict and loose siblings:

  • I consider ID #p1 and #p2 strict siblings (because there is no text which is not encapsulated in any kind of html element between them.

  • And ID #p2 and #p3 to be loose siblings.

Given any element with a next sibling is it possible to know if the next sibling is strict or loose?

Edit: The siblings may be of different type and I updated the fiddle to reflect that.

Fabricio
  • 839
  • 9
  • 17
  • You want to do something with css2 selectors `~` and `+` http://www.quirksmode.org/css/selectors/#t11 – Adam Merrifield Feb 13 '14 at 17:49
  • I'd say a better terminology would be "adjacent elements" or "contiguous elements" as they are all strictly node siblings, one is just a text node rather than an element node. – Klors Feb 13 '14 at 18:25

5 Answers5

4

Is this what you meant: http://cssdeck.com/labs/0blyuslnzv

var isStrict = function(elem1, elem2) {
    "use strict";

    var e1 = document.querySelectorAll(elem1)[0],
        elemNext = document.querySelectorAll(elem1 +" + "+ elem2)[0];

    if (e1.nextSibling.textContent.trim().length) {
        return e1.nextSibling === elemNext;
    } else {
        return e1.nextElementSibling === elemNext;
    }
};

Usage eg.

isStrict("head", "body") => true

isStrict("#p1", "#p2") => true

isStrict("#p2", "#p3") => false
Samuli Hakoniemi
  • 18,740
  • 1
  • 61
  • 74
2

Try this.

var $selector = $("#p1");
$selector.siblings().each(function(){
    $(this).addClass('checkSelector');
    if(document.querySelectorAll($selector.selector+' + .checkSelector').length){
        //Do Strict Action
    }else if(document.querySelectorAll($selector.selector+' ~ .checkSelector').length){
        //Do non strict but still sibling action
    }
    $(this).removeClass('checkSelector');
});

http://jsfiddle.net/chsck/5/

Adam Merrifield
  • 10,307
  • 4
  • 41
  • 58
2

Answering your question of

Given any element with a next sibling is it possible to know if the next sibling is strict or loose?

rather than following your lead from the fiddle, yes it's possible.

function isMyNextSiblingStrict(element)
{
    return ("nodeType" in element && element.nodeType === 1 && element.nextSibling !== null && element.nextSibling.nodeType === 1);
}

This will return true when an element's next sibling is another element. However, be careful with your example as it is incorrect by your own definition, the two spans have a text node between them made up of white space, so #1 and #2 are not "strict siblings".

<div id="container">
    <span id="p1">Paragraph 1</span><!-- WHITESPACE
 --><span id="p2">Paragraph 2</span>
    This is just loose text.
    <p id="p3">Paragraph 3</p>
</div>

These would be "strict siblings".

<div id="container">
    <span id="p1">Paragraph 1</span><span id="p2">Paragraph 2</span>
    This is just loose text.
    <p id="p3">Paragraph 3</p>
</div>

edit - just for fun I've created a jQuery extension that should do what you want - you can see it in your updated jsFiddle, I've not tested it much but it seems to work as you describe

$.fn.extend({
    siblingsStrict: function( until, selector ) {
        var ret = jQuery.map( this, function( elem ) {
            var n = ( elem.parentNode || {} ).firstChild,
                r = [],
                contig = false;

        for ( ; n; n = n.nextSibling ) {
                if ( n === elem ) {
                    contig = true;
                }
                else if ( n.nodeType === 1 && n !== elem ) {
                    r.push( n );
                }
                else if ( n.nodeType === 3 && n.textContent.replace(/\s/g, "") === "" ) {
                    continue;
                }
                else if ( n.nodeType === 3 && contig === true ) {
                    break;
                }
                else if ( n.nodeType === 3 && contig === false) {
                    r = [];
                }
        }

        return r;
    }, until );

        if ( name.slice( -5 ) !== "Until" ) {
            selector = until;
        }

        if ( selector && typeof selector === "string" ) {
            ret = jQuery.filter( selector, ret );
        }

        if ( this.length > 1 ) {
            // Remove duplicates
            if ( !guaranteedUnique[ name ] ) {
                ret = jQuery.unique( ret );
            }

            // Reverse order for parents* and prev-derivatives
            if ( rparentsprev.test( name ) ) {
                ret = ret.reverse();
            }
        }

        return this.pushStack( ret );
    }
});
Klors
  • 2,665
  • 2
  • 25
  • 42
  • _However, be careful with your example as it is incorrect by your own definition_ - While commuting to work this morning I was thinking "I scr*wed up in my question..." but gladly someone was awake enough to spot it... :-) I will review and test all answers and pick one later today. – Fabricio Feb 14 '14 at 08:12
  • I was testing your first solution and got a console error: Firefox says `TypeError: invalid 'in' operand element`, Opera says `Uncaught exception: TypeError: Operator 'in' applied to non-object` and Chrome also gags: `Uncaught TypeError: Cannot use 'in' operator to search for 'nodeType' in #p1`. See [fiddle](). Any idea of what's going on? – Fabricio Feb 14 '14 at 10:04
  • I'm not sure and unfortunately I can't see your fiddle, here's a fiddle http://jsfiddle.net/chsck/12/ – Klors Feb 14 '14 at 10:15
  • Although I am struggling to follow what is going on in the extension code it does answer the question... +1 for that. Indeed I forgot to put my fiddle. In it I was trying to invoke the function with `isMyNextSiblingStrict("#p1")` which was a typo from what I wanted to do. But after seeing your last fiddle, which BTW works perfectly, I also fixed my error and I tried to invoke the function with `isMyNextSiblingStrict($("#p1"))` but it always returns `FALSE`. I thought `$("#p1")` would be the same as `document.getElementById("p1")` but it doesn't seem to be the case... [chat]? – Fabricio Feb 14 '14 at 10:29
  • $("#p1") returns a jQuery object, whereas document.getElementById("p1") returns a DOM node. You can get the same thing from jQuery by doing $("#p1")[0] or $("#p1").eq(0). I've not seen the chat before, fancy. Don't worry too much about the extension code, it's mostly just taken from jQuery's own method defintion for `.siblings`. The pertinent bit is the middle function in the `.map`. – Klors Feb 14 '14 at 10:37
0

Ignoring what others might have written;

function checkAdjacency(element1,element2){
    var result = isLooseOrStrict(element1,element2);
    if(result != 'not'){
        return result;
    }
    else{
        return isLooseOrStrict(element2,element1);    
    }
}

function isLooseOrStrict(elemName1, elemName2){
    var element = document.getElementById(elemName1);
    var element2 =  document.getElementById(elemName2);

    if(element.nextSibling.nodeType != 3){
        if(element.nextSibling == elem2){
            return 'strict';
        }
    }
    else if(element.nextSibling.nextSibling == element2){
        if(element.nextSibling.nodeValue.trim() != '')
            return 'loose';
        else
            return 'strict';
    }
    return 'not';
}
Mudasir Ali
  • 128
  • 1
  • 11
-1

you can do this using only vanilla like bellow

(jsFiddle -> http://jsfiddle.net/Castrolol/chsck/4/ )

function getSiblings(elem){    
    var siblings = [];
    var actualElem = elem;
    while(actualElem = actualElem.nextSibling ){
        if( !actualElem.textContent.trim() ) continue;
        siblings.push({
            elem: actualElem,
            isStrict: !(actualElem instanceof Text)
        });
    }
    return siblings;
}

//using the function
var element = $("#p1").get(0);
var siblings = getSiblings(element);
siblings.forEach(function(sibling){
    if( sibling.isStrict ) {
        //here is a tag
        sibling.elem.style.color = "red";
    }else{
        //here not is a tag        
    }
});
Luan Castro
  • 1,184
  • 7
  • 14