3

I know I can use [dir='ltr'] or [dir='rtl'] to select elements that have a dir attribute with a specific value.

so I can define for example

 [dir='ltr'] .float-end {float:right}
 [dir='rtl'] .float-end {float:left}

to get a .float-end class that floats right or left when inside an element with ltr or rtl respectively.

The problem starts when I have an ltr sub part of a document that is rtl.

<div dir="rtl">
   <div dir="ltr">
      <div class="float-end"></div>
   </div>
</div>

What happens is that both rules match... and I only want to match the 'ltr' case in this scenario.

The problem gets worse if I want to create classes such as start-20px and end-20px to provide left:20px and right:20px and vice versa depending on context.

Would result in both left:20px and right:20px being applied....

I am looking for suggestions on how to overcome this.

Is there a way to depend on the nearest value of an attribute of any given type of element?

This is all done within the context of LESS mixins if it helps some how...

Thanks

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
epeleg
  • 10,347
  • 17
  • 101
  • 151
  • `[dir='ltr'] > .float-end` – Abhitalks Jun 16 '14 at 15:00
  • hmmm.... will this relate to the inherited value? or will it only work on direct children that have the attribute specified on them? I will have to test this. – epeleg Jun 16 '14 at 15:02
  • so its not good enough :( I need something like [dir inherits 'ltr'] > .float-end .... – epeleg Jun 16 '14 at 15:05
  • Perhaps I'm not understanding well enough, but would `[dir='ltr'] > .float-end:first-child {float:right}` fit the bill? A fiddler with a full example would be pretty helpful in fully understanding what you want. – Justin Jun 16 '14 at 17:13

5 Answers5

3

The problem lies in the fact that the attribute selectors currently available in CSS have no document semantics. For example, you can't use attribute selectors to express some attribute value that is inherited from some other element. This is because as far as an attribute selector is concerned, if an element's attribute doesn't have a certain value specified on the element itself in the document tree, then it will not match the selector, even if the value is derived from elsewhere.

As mentioned, you could limit selection to the closest ancestor using the > combinator, but this requires the immediate ancestor to have the specified attribute, which of course you won't be able to guarantee is the case unless you make sure of it yourself. But if you are able to modify your markup, then that really is the best way to do this, even if it means a little bit of redundancy in your markup. I don't think even LESS by itself would be able to help you, as it still depends on the structure of your markup, which it cannot anticipate.


For what it's worth, Selectors 4 introduces a :dir() pseudo-class which carries semantics of element directionality based on the rules of the document language, something that attribute selectors alone cannot accomplish. For example, both the div and the p in the following example would match :dir(ltr), but only the div is [dir='ltr'] because the p has no dir attribute specified:

<div dir="ltr">
    <p>Left-to-right text</p>
</div>

In your case, you would be able to just use .float-end:dir(ltr) and .float-end:dir(rtl) respectively; you wouldn't even need to have the pseudo-class on an ancestor.

The :dir() pseudo-class compared with the [dir=] attribute selector is similar to :lang() and [lang|=], which I explain here.

Of course, being a new proposal, :dir() isn't implemented anywhere (with the curious exception of Firefox — I'm guessing Mozilla thought of the idea, implemented it, then proposed for it to be standardized). In the meantime, you'll have to work around it by going the route I des

Community
  • 1
  • 1
BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
  • I wander how comes even though HTML and CSS have been around SO MANY YEARS they still don't support the basic concepts of Start and end. i.e. why not add padding-start (i.e. padding-left on ltr and padding-right on trl) and padding-end and so on to any css that has left or right in its name... – epeleg Jun 17 '14 at 05:57
2

There is now a proposed solution for this in the CSS spec: Logical Properties. Basically instead of applying different property values based on some other element's dir attribute, browsers are going to start having property values that are directionally aware.

So in a perfect world, once these have full support you could write:

.float-end {
  float: inline-end;
}

These new values will exist for lots of properties, not just float. Support as of July 2019 is pretty much everybody but Microsoft (IE/Edge), go figure.

paulcpederson
  • 1,367
  • 1
  • 9
  • 13
  • this should have been in css since day one.... too bad it still says "This is an experimental API that should not be used in production code." on all those properties in the link you provided. Normally I would leave the originally accepted answer and just bump up this one to keep the original credit for the original solution. but since it was my own solution - why not... I will accept this solution in hope that it actually becomes a viable production solution sometime soon. – epeleg Jul 24 '19 at 20:42
1

Well, I can not give a solution to your first question (about floats), but I can give you one about the second (left / right properties).

As stated by the w3c

If neither 'left' nor 'right' is 'auto', the position is over-constrained, and one of them has to be ignored. If the 'direction' property of the containing block is 'ltr', the value of 'left' wins and 'right' becomes -'left'. If 'direction' of the containing block is 'rtl', 'right' wins and 'left' is ignored.

vals
  • 61,425
  • 11
  • 89
  • 138
  • Interesting. but it does not help me (not your fault of course) as there might be times when the dir is ltr but you still want to assign a value to `right:` and not `left:`. – epeleg Jun 17 '14 at 05:53
1

O.k. So having learned that what I expected just does not work (at least not as of now [June 2014]), I ended up with the following LESS based solution:

// provide a mixin to use two different rule sets depending on the current direction.

.ltr(@ltrRules) {
  body[dir="ltr"] & , body[dir="rtl"] [dir="ltr"] &, body[dir="rtl"] [dir="ltr"]&  { @ltrRules(); }
}

.rtl (@rtlRules) {
  body[dir="rtl"] & , body[dir="ltr"] [dir="rtl"] &, body[dir="ltr"] [dir="rtl"]& { @rtlRules(); }
}

.bidi(@ltrRules,@rtlRules) {
  .ltr(@ltrRules);
  .rtl(@rtlRules);
}


// padding
// ------------------------------------------
.padding-start(@p) { 
  .bidi(
   { padding-left: @p } ,
   { padding-right: @p }
  )
}

so using .padding-start(10;) on some SELECTOR

.my .selector {
  .padding-start(10px);
}

will eventually generate the following CSS:

body[dir="ltr"] .my .selector,
body[dir="rtl"] [dir="ltr"] .my .selector,
body[dir="rtl"] [dir="ltr"].my .selector {
  padding-left: 10px;
}

body[dir="rtl"] .my .selector,
body[dir="ltr"] [dir="trl"] .my .selector,
body[dir="ltr"] [dir="trl"].my .selector {
  padding-right: 10px;
}

The compromise is that I can not change the direction multiple times going into the depth of the document and that the initial seeing of the direction must be on the body tag.

That siad, if for some absurd reason I WILL get to some point in the future when I will have to change the dir more then twice I can just use the same method and add handling for body[dir="ltr"] [dir="rtl"] [dir="ltr"] & and so on...

With some luck in a few years someone will understand that it is important to add the semantics of start and end that get interpreted as left and right respectively in LTR contexts and vice versa in TRL contexts making all my macros redundant... [as has already been done in text-align for example].

epeleg
  • 10,347
  • 17
  • 101
  • 151
0

I know this is a very late reply but it could help someone if I got the question right.

Does this make sense?

[dir="ltr"] *:not([dir="rtl"]) .float-end,
[dir="ltr"] > .float-end{
    float: right;
}
[dir="rtl"] *:not([dir="ltr"]) .float-end,
[dir="rtl"] > .float-end{
    float: left;
}

JSFiddle here

ronnykaram
  • 81
  • 6
  • hmmm... At first moment I thought this is great, but on second thought I think it is just wrong. I beleive the first selector for example is not for an elemnt with a "float-end" class that is within an element that has a dir=ltr attribute and NO other element in between with dir=rtl but rather an elemnt with a "float-end" class that is within an element that has a dir=ltr attribute and DOES have at least one element in between that does not have an attribute of dir=rtl. e.g. It would wrongly match p[dir=ltr] p[dir=rtl] div div[class=float-end] and float the last div right and not left. – epeleg Sep 17 '16 at 20:07
  • It would be great if you can provide a real example with some HTML markup. – ronnykaram Sep 22 '16 at 20:49