0

I know that this is super simple with jQuery, although I am after a CSS only solution (if possible).

I have a list of divs, with the last item being an error message. I have a simple filtering system, and if none of the divs match the selected filter, I would like to display the error div.

HTML Structure:

<div id="listHolder">
    <div class="listItem" data-filter-class="["filter1"]"></div>
    <div class="listItem" data-filter-class="["filter2"]"></div>
    <div class="listItem" data-filter-class="["filter1"]"></div>
    <div class="listItem" data-filter-class="["filter4"]"></div>
    <div class="errorItem">Nothing to display here</div>
</div>

What I am trying to achieve:

If a div does not match any of the filters, my filter plugin gives them the class of inactive. Hence, I need to check if all divs with the class of listItem also have the class of inactive to give the errorItem class the style of display:block.

FYI I am using the Wookmark plugin for my list and filtering system. I am also using LESS.

Fizzix
  • 23,679
  • 38
  • 110
  • 176

2 Answers2

4

Sure it's possible: http://jsfiddle.net/rudiedirkx/7b1kyfz3/3/

You want to hide the last item if a previous item is not hidden:

.listItem:not(.inactive) ~ .errorItem {
    display: none;
}

The demo uses JS just to toggle the inactive class, not for display logic of the errorItem.

I still agree with all the smart people here though: JS can probably do this better. You're using it already anyway.

Rudie
  • 52,220
  • 42
  • 131
  • 173
3

The problem you have is in your requirement:

I need to check if all divs with the class of listItem also have the class of inactive to give the errorItem class the style of display:block

While we can set a style for the final <div> element based on its preceding siblings, we can't (without knowing how many there might be) 'check if all divs' have the inactive class. We can, however, use the sibling combinator (+) and a cumbersome selector:

.errorItem {
    display: none;
}
.listItem.inactive + .listItem.inactive + .listItem.inactive + .listItem.inactive + errorItem {
    display: block;
}

This is, however, ridiculous (especially if there's a dynamic number of elements preceding the .errorItem element.

If there's a class-name applied for an element which does match the supplied filters, active for example, this is much simpler, and achieved by:

.errorItem {
    display: block;
}

.listItem.active ~ .errorItem {
    display: none;
}

Also, as pointed out in the comments, the negation operator is also available (though, obviously, it depends on implementation by the browser in use), which would lend itself to the selector:

.errorItem {
    display: block;
}

.listItem:not(.inactive) ~ .errorItem {
    display: none;
}

On the whole, I'd strongly suggest using JavaScript to support this functionality, especially since the use of Wookmark implies JavaScript (if not necessarily jQuery) use in the same site already.

Native JavaScript:

function hasPrecedingSibling (elem, state) {
    if (!state) {
        return false;
    }
    var found = false,
        cur = elem;
    while (cur.previousElementSibling && found === false) {
        if (cur.classList.contains(state)) {
            found = true;
        }
        else {
            cur = cur.previousElementSibling;
        }
    }
    return found;
}

[].forEach.call(document.querySelectorAll('.errorItem'), function (err) {
    err.style.display = hasPrecedingSibling (err, 'active') ? 'none' : 'block';
});
David Thomas
  • 249,100
  • 51
  • 377
  • 410
  • 1
    If you can do it with `.active`, you can do it with `:not(.inactive)`, right? http://jsfiddle.net/rudiedirkx/7b1kyfz3/2/ – Rudie Nov 09 '14 at 00:08
  • Actually, that's very true; thanks! (I'll be editing that in, momentarily) :) Ah. I'm sorry, I hadn't seen that you'd added your own answer (I tend not to refresh the page while I'm updating my own answers); if you'd prefer I can excise the `:not(.inactive)` part, if you'd prefer I not duplicate your own approach? – David Thomas Nov 09 '14 at 00:15
  • No worries. You did half the work, I did half the work. – Rudie Nov 09 '14 at 00:23
  • @Rudie: I appreciate the generosity :) – David Thomas Nov 09 '14 at 00:26
  • Works perfectly, thanks! Used the `:not(.inactive)` method, works very well since the elements are not given an `active` class unfortunately. Thanks for your answer, and your explanations. – Fizzix Nov 09 '14 at 00:29