0

For instance,

given

<html dir=rtl>    
<body>
    <div>I am right to left</div>
    <div>I am right to left
       <div dir=ltr>I should be left to right
           <div dir=rtl>I should be right to left</div>
       </div>
    </div>    
</body>
</html>

I am asking this question because the nested content is generated and there is no way to know how deep this is going to be.

Ideally a CSS rule should be able to apply a certain rule based on it's deepest match.

The rule:

[dir=rtl] {
    text-align:right;
}

will only match the first. Ideally, you'd want something like

[dir=rtl]:deepest {
    text-align:right;
}

Update:

I realize that the framing of the question might still be a bit confusing, sorry about that!

Here is code that illustrates the problem:

.container {
  padding:20px;
}

[dir=ltr] .container {
  border:5px solid blue;
}

[dir=rtl] .container {
  border:5px solid red;
}
<div id="searchHere">
  <div dir=ltr>
    <div class="container">
      l t r
    </div>
    <div dir=rtl>
      <div class="container">
        r t l
      </div>
      <div>
        <div class="container">
          r t l
        </div>
        <div dir=ltr>
          <div class="container">
            l t r ( should be but is r t l colors instead )
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

To try to clarify what the goal is, it is to say that any match that is found at a deeper level, is what should be applied on all child matches.

This can be achieved using a unique identifier for instance, since that will override prior rules, or by using a more well defined css selector. The more well defined selector could be div > div > div > div ... however, given a dynamically generated content, it would be quite difficult to know the rule to define.

mjs
  • 21,431
  • 31
  • 118
  • 200
  • What's the problem here? Sorry, I could not understand what do you want. – SaidbakR May 04 '15 at 13:30
  • 2
    I *think* the hope is for a selector that would target the innermost `div`, but not the `html` tag (which also has `dir=rtl`). Can't see how to do that without a very specific selector (which won't work, since the dynamic HTML will be different each time), or adding a class to the innermost `div`, and styling based on *that* (my recommendation). – Paul Roub May 04 '15 at 13:31
  • Yeah, there's no `deepest` pseudo-selector. See [this](http://stackoverflow.com/questions/5247888/css-selector-to-get-deepest-element-of-specific-class-in-the-html-tree) and [this](http://snook.ca/archives/html_and_css/css-parent-selectors) – sjagr May 04 '15 at 13:33
  • If you maintain the code that generates the content, you can apply a special class to just the last instance. Or you could use JavaScript to detect the deepest match and apply a special class that way. – gfullam May 04 '15 at 17:48
  • @gfullam Yes, javascript seems to be the only option. – mjs May 05 '15 at 19:25
  • If you're using jQuery, there is a good answer on how to [select deepest child in jQuery](http://stackoverflow.com/questions/3787924/select-deepest-child-in-jquery) – gfullam May 05 '15 at 19:40
  • In fact, I've created a robust [jQuery plugin: .deepest()](http://codepen.io/gfullam/pen/BNoZWG) based on that answer which works just liked you'd expect, returning a collection of the deepest children of each element in the set of matched elements, optionally filtered by a selector. It is fully chainable and respects the `.end()` method. View it on [CodePen](http://codepen.io/gfullam/pen/BNoZWG), [JSFiddle](http://jsfiddle.net/gfullam/f8wuc3f8/), [Gist](https://gist.github.com/geraldfullam/3a151078b55599277da4) – gfullam May 06 '15 at 18:11
  • @gfullam Good job, well done! However, I realize that the framing of the question might still be a bit confusing, sorry about that! Here is a codepen that illustrates the problem: http://codepen.io/anon/pen/JdYaPM ... To try to clarify what the goal is, it is to say that any match that is found at a deeper level, is what should be applied on all child matches. This can be achieved using a unique identifer for instnace since that will override prior rules, or by using a more well defined css selector. The more well defined selector could be div > div > div > div ... – mjs May 06 '15 at 20:13
  • If you're not looking for a true deepest child match, which you can only achieve with JavaScript, it still sounds to me like you just need to rely on standard CSS behavior. I'm struggling to fully understand what you're trying to accomplish. I hope someone else here can answer your question, because I'm really curious to know what it is at this point. :) – gfullam May 06 '15 at 20:34
  • Ah, I see the problem now. Your HTML nesting is suffering from an irregular pattern. If you have control over the generated markup, I suggest making `.container` a direct child of `[dir=rtl]` or `[dir=ltr]`, so you could target it in CSS with `[dir=rtl]>.container` or `[dir=ltr]>.container`; better yet, append a second class `.rtl` or `.ltr` to every `.container` for direct CSS selection with `.container.rtl` or `.container.ltr`. – gfullam May 06 '15 at 21:09
  • Otherwise, your only option is to write a javascript (or jQuery) function for detecting descendants that are not also descendants of a competing parent class. But you're not looking for the deepest match. – gfullam May 06 '15 at 21:14
  • "option is to write a javascript (or jQuery) function for detecting descendants that are not also descendants of a competing parent class" - that pretty much nails it. However, it would also be said that a deeper element matching a rule should override or have preference over another one. – mjs May 07 '15 at 06:07
  • You may be interested in this SO question: [What JQuery selector excludes items with a parent that matches a given selector?](http://stackoverflow.com/questions/965816/what-jquery-selector-excludes-items-with-a-parent-that-matches-a-given-selector) – gfullam May 10 '15 at 01:49
  • 1
    In the specific case of the `dir` HTML attribute, this is why Selectors 4 introduces a `:dir()` pseudo-class that takes attribute inheritance into account in a way that CSS attribute selectors do not. `:dir(rtl)` will match your element similarly to `[dir=rtl]:deepest`, *plus* any of its descendants that don't have a different `dir` specified. – BoltClock May 10 '15 at 04:58
  • @BoltClock Good to know, not very good browser coverage though: https://developer.mozilla.org/en-US/docs/Web/CSS/:dir I have to build the application for the present, and not for the future :( – mjs May 10 '15 at 10:07
  • @momo: Yeah, it's a relatively recent addition. – BoltClock May 10 '15 at 10:08
  • @BoltClock There is the :lang attribute as well, I am unsure if that works the same and could be applied instead of :dir in my case. It has better coverage: https://developer.mozilla.org/en-US/docs/Web/CSS/:lang – mjs May 10 '15 at 10:09
  • @momo: If you're switching between LTR and RTL languages, it could work. – BoltClock May 10 '15 at 10:11
  • @BoltClock Here is a fiddle: http://codepen.io/anon/pen/vOLeKE .. can't get lang to work on nested elements. Am I doing something wrong? The sample is not using dir at all now though. – mjs May 10 '15 at 10:12
  • @momo: The selectors should be `.container:lang(en)` and `.container:lang(ar)`. Your deepest-nested element has lang=ar, if you change it to lang=en it should work as expected. – BoltClock May 10 '15 at 10:15
  • Even this works: http://codepen.io/anon/pen/jPWGMG – mjs May 10 '15 at 10:23
  • @BoltClock You want to submit an answer using this: http://codepen.io/anon/pen/jPWGMG and I will accept it as the correct one. Or do you want me to submit one? – mjs May 10 '15 at 10:36
  • The questions are pretty different between lang and dir. Since you know your original markup better you may want to do the posting :) – BoltClock May 10 '15 at 10:38

3 Answers3

1

Update:

Make desired elements directly targetable

In your initial example, the desired elements are directly targetable, hence my original answer below.

But in your comments, you alluded to something much more complex that you clarified with the Code Pen you shared.

What you are actually trying to accomplish is targeting the descendants of certain elements that aren't also descendants of a competing element, in this case, [dir=rtl] .container vs. [dir=ltr] .container.

There is no current CSS selector that can do this without the possibility of interference (as you demonstrated in your pen), so the best way to style these elements is to make sure that they are directly targetable by applying an identifier class to each based on its relationship to the closest parent with a dir attribute set.

If you have control over the generated markup, you should handle the logic in your template before it is generated so that the markup is just ready.

But if you don't have control over the generated markup, you can apply the classes with jQuery using .filter with a passed in function.

Either way, you want your markup to end up like this:

<div id="searchHere">
  <div dir=ltr>
    <div class="container ltr">
      l t r
    </div>
    <div dir=rtl>
      <div class="container rtl">
        r t l
      </div>
      <div>
        <div class="container rtl">
          r t l
        </div>
        <div dir=ltr>
          <div class="container ltr">
            l t r
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

A jQuery approach

$(function () {
  
  $('.container')
    .filter(function () {
      // Return elements whose closest parent w/ attribute 'dir' is 'rtl'
      return $(this).closest('[dir]').attr('dir') === 'rtl';
    })
    .addClass('rtl')  // Apply identifier class
    .end()
    .filter(function () {
      // Return elements whose closest parent w/ attribute 'dir' is 'ltr'
      return $(this).closest('[dir]').attr('dir') === 'ltr';
    })
    .addClass('ltr'); // Apply identifier class
  
});
.container {
  padding: 20px;
}
.container.ltr {
  border: 5px solid blue;
}
.container.rtl {
  border: 5px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="searchHere">
  <div dir=ltr>
    <div class="container">
      l t r
    </div>
    <div dir=rtl>
      <div class="container">
        r t l
      </div>
      <div>
        <div class="container">
          r t l
        </div>
        <div dir=ltr>
          <div class="container">
            l t r
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

Original answer:

CSS already provides this by default

CSS native behavior is to apply the styles to every element that matches the given selector.

In your example, the rule [dir=rtl] { text-align:right; } will apply to every element that matches the selector [dir=rtl], not just the first.

Also, your example doesn't even require CSS to work because you're utilizing the HTML dir attribute, so the browser will automatically apply the right-alignment for text within those elements.

<body dir="rtl">
    <div>I am right to left</div>
    <div>I am right to left
       <div dir="ltr">I should be left to right
           <div dir="rtl">I should be right to left</div>
       </div>
    </div>    
</body>

But even in a pure CSS example, you only need to use a standard selector to target even the nested elements:

.rtl {
    direction: rtl;
}

.ltr {
    direction: ltr;
}
<body class="rtl">
    <div>I am right to left</div>
    <div>I am right to left
       <div class="ltr">I should be left to right
           <div dir="rtl">I should be right to left</div>
       </div>
    </div>    
</body>
gfullam
  • 11,531
  • 5
  • 50
  • 64
  • 1
    I could be wrong but I think the OP only wants the deepest selector to be applied. That is, all ancestor selectors will be ignored. – hungerstar May 04 '15 at 14:57
  • Ah, I see. That would be nice. – gfullam May 04 '15 at 17:47
  • @gfullam yes, there are other more complex applications that your solution won't cover. For instance, sometimes you want to flip margins, so margin-left:5px; becomes margin-right:5px. if you have a class [dir=rtl] margin-left-5 { margin-right:5px; } .. a deeper ltr should restore this and a deeper rtl apply right, and so on. – mjs May 05 '15 at 19:23
  • I'm not sure I follow your example. In `[dir=rtl] margin-left-5 { margin-right:5px; }` should `margin-left-5` be a class selector `.margin-left-5`? If so, it is confusing that your class called `margin-left-5` would have a rule to apply `margin-right`. But even if that is how you intend to name the class, you still only need to set two classes in your CSS, and every subsequent child element will receive the necessary margin rule if the specificity of the selectors is equivalent. I'll update the examples to demonstrate. – gfullam May 05 '15 at 19:29
  • @gfullam I applaude you for your tenacity, preservance and determination, and the good results you have achieved, however a fixed layout is not possible in my case. It appears thoughs as if this can be accomplished using :dir and :lang. See http://stackoverflow.com/a/30150602/961018 – mjs May 10 '15 at 11:02
  • @gfullam You should consider answering this: http://stackoverflow.com/questions/5247888/css-selector-to-get-deepest-element-of-specific-class-in-the-html-tree as the solution for my problem doesn't fix that. Your's might. – mjs May 10 '15 at 12:18
  • I'm am in no way suggesting a fixed layout. I'm suggesting that you simply put classes on items you want to target with CSS that would be difficult to target otherwise. This is a common approach that is used in dynamically generated content all the time. You can do it at the time of generation or you can do it post-generation with jQuery as I demonstrate. Either way will achieve the results you want. Notice that in the code snippet I posted, you could change the HTML to any depth of nesting with any variety of nesting and it would still work. – gfullam May 10 '15 at 13:48
1

It appears that this is possible, using the :dir or :lang attribute.

Using the :lang is preferable in 2015 as it is supported by most browsers.

Example:

.container {
  padding:20px;
}

:lang(ar) {
  direction:rtl;
}

:lang(en) {
  direction:ltr;
}

.container:lang(en) {
  background-color:blue;
}

.container:lang(ar)  {
  background-color:red;
}

.container .a:lang(en)   {
  background-color:orange;
}

.container .a:lang(ar) {
  background-color:yellow;
}
<div id="searchHere">
  
    <div lang=en>      
      <div class="container">
          l t r
        <div class=a>
              a
         </div>
      </div>
      
      <div lang=ar>
          <div class="container">
             r t l
            <div class=a>
              a
              </div>
          </div>        
        
          <div>
            <div class="container">
                r t l 
              <div class=a>
              a
              </div>
            </div>  
            
            <div lang=ar>
                <div class="container">
                   r t l
                  <div class=a>
                    a
                    </div>
                </div>  
            </div>
            
            <div lang=en>      
              <div class="container">
                  l t r
                <div class=a>
              a                  
              </div>
                
                <div lang=ar>
                  <div class="container">
                     r t l
                    <div class=a>
                      a                      
                      <div lang=en>      
                        <div class="container">
                          l t r
                          <div class=a>
                            a                  
                          </div>
                          <div>
                            <div>
                            
                              <div lang=en>      
                                <div class="container">
                                  l t r
                                  <div class=a>
                                    a                  
                                  </div>
                                </div>                      
                              </div>
                              
                              <div lang=ar>      
                                <div class="container">
                                  r t l
                                  <div class=a>
                                    a                  
                                  </div>
                                </div>                      
                              </div>
                              
                            </div>
                            
                          </div>
                        </div>                      
                      </div>
                  </div> 
                </div>
              </div>            
            </div>
            
          </div>
        
      </div>
      
    </div>
  
</div>

Although the example demonstrates this with ltr and rtl, the :lang could in theory be made to behave as a deepest match, using for instance :lang(deepest-overrides-all) although that is probably not where lang=deepest-overrides-all should be defined on an element.

mjs
  • 21,431
  • 31
  • 118
  • 200
-1

Nope.

If you can generate HTML to an unspecified depth then your can do it for CSS, but there us no "innermost match" selector.

The best your can do is to generate CSS using a script that understands that the deeper the level the less important the oiot level is. This is opposite of HTML and CSS's assumptions.

STW
  • 44,917
  • 17
  • 105
  • 161