8

Note: This question is not answered in the above linked question, nor is it a duplicate. This question addresses the problem with the ordering of special characters when using text-direction: rtl, which it turns out can be solved with the unicode-bidi: bidi-override as pointed out by @Serlite below.

I have a div containing some text that I'd like to overflow to the left, not to the right.

For example, if the div contains all letters of the alphabet, the default behaviour is as follows:

Image 1

I, however, want the contents of the div to be displayed like this:

Image 2

Using direction: rtl in the styles for that div works fine, and achieves this effect perfectly for the above example, as well as if the contents contains just numbers (or a mix of alphanumeric characters) as shown below.

Standard behaviour:

Image 3

Behaviour after setting direction: rtl:

Image 4

The problem occurs when special characters are entered into the div, such as * and #.

Setting the string *#0123456789 as the div contents causes the following to be displayed:

Image 5

Note the * moving from the start of the string to the end.

It's difficult to show the problem with just images, so here is a quick jsfiddle to better demonstrate it.

In general, it seems like weird things happen when setting the direction property, and so I'm guessing it's probably not the way to go.

Is there a way I can force the last characters to always be visible while the first characters overflow to the left?

joshfarrant
  • 1,089
  • 1
  • 14
  • 27
  • Is your div `contenteditable` and content of it changable or is constant? – Mohammad May 10 '16 at 15:30
  • 1
    What about with `text-align: right` ? Fiddle : https://jsfiddle.net/8zbzy4sL/3/ – Vincent G May 10 '16 at 15:35
  • please tellme you don't mean something like this? https://jsfiddle.net/8zbzy4sL/4/ –  May 10 '16 at 15:36
  • @Mohammad No, just a regular ol' div with static content. – joshfarrant May 10 '16 at 15:37
  • @PeterDarmis @VincentG that doesn't solve the problem unfortunately. If you fill the inputs in your example so that they overflow, you'll see that `text-align: right;` doesn't solve the issue. See my 2nd and 4th images above for what I'm trying to achieve. – joshfarrant May 10 '16 at 15:39
  • 1
    "In general, it seems like weird things happen when setting the direction property, and so I'm guessing it's probably not the way to go." Different punctuation symbols behave differently in different writing modes. This is perfectly normal behavior. But for your purposes, direction is indeed not the way to go. – BoltClock May 10 '16 at 15:44
  • @dippas I think it does not solve the problem. The OP question is how to avoid (or solve) that the `*` does not move to right. – Francisco Romero May 10 '16 at 15:44
  • 1
    @error404 You're correct, the accepted answer doesn't solve the issue, however a later answer suggesting using a wrapper div to take care of clipping the overflowing text on the left might actually do the trick. I'd considered that previously but it seemed like a bit of a hacky way to do it, but if there's no simpler way then that will be the way I go. – joshfarrant May 10 '16 at 15:46
  • Are you fine using JS? – Jacob G May 10 '16 at 15:47
  • @JacobGray If there's a clean way to do it with JS then that's fine – joshfarrant May 10 '16 at 15:52
  • @joshfarrant https://jsfiddle.net/8zbzy4sL/5/ is that clean enough? – Jacob G May 10 '16 at 15:54
  • There is a funny way in css to let overflow disseapear at left ... https://jsfiddle.net/8zbzy4sL/7/ – G-Cyrillus May 10 '16 at 15:58
  • @GCyrillus Think that's going to be similar to the way I solve it, and as in dippas's linked duplicate above. – joshfarrant May 10 '16 at 16:14
  • i see, did not noticed the answers of Abe out there – G-Cyrillus May 10 '16 at 16:21
  • Well, maybe there is a less tricky way with text-indent : https://jsfiddle.net/8zbzy4sL/8/ (also as a snippet in answer below) + should work in any language ... – G-Cyrillus May 10 '16 at 16:31
  • It is annoying that overflow-X: hidden and text-align: right don't do the trick. Does anyone know why browsers handle it this way? – MSC May 30 '16 at 01:15

4 Answers4

3

The unexpected reordering you're observing is due to an algorithm used for determining the ordering of bi-directional text. I won't go into details on how the ordering is exactly determined, but basically characters are classified by strong, weak, and neutral directionality, which is used to order them in a string.

Your string "*#02468" consists only of a mix of characters with weak or neutral directionality, meaning their ordering may be unexpected without the context of a strongly directional character (like Arabic text, which reads right-to-left). Depending on the characters you use, they may seem to contradict the specified direction.

To avoid this behaviour you're encountering, you can use the unicode-bidi property to override the algorithm, and rely strictly on the specified direction:

#rtl {
  direction: rtl;
  unicode-bidi: bidi-override;
}

Here's an updated JSFiddle.

You can also read up a bit more on the logical ordering of this algorithm, if you're interested. It's a bit too complex to go into detail about.

Hope this helps! Let me know if you have any questions.

Note: If you don't want the characters in your string reversed, this won't work for you. In that case, go with an approach that doesn't use the direction property - because that's exactly what the property was designed for.

Serlite
  • 12,130
  • 5
  • 38
  • 49
  • This is the solution I went with, thanks for your suggestion @Serlite. I in fact don't want the characters in my string reversed, however I'm using react and so it was trivial to reverse the characters of my string on render. It works perfectly! – joshfarrant May 11 '16 at 08:53
1

EDIT Maybe a less tricky way with text-indent and text-align:

input {
  width: 200px;
  font-size: 30px;
  border:none;
  background:none;
  color:white;
  background:#26A0DA;
}
#rtl {
  text-align: right;
  text-indent: -9999px;
}
label {
  display:inline-block;
  line-height:2em;
  padding:0 5em;
  background:#26A0DA;
  }
  
<label for="ltr">ltr
  <input id="ltr" value="*#02468456789" />
</label>

<label for="rtl">rtl
  <input id="rtl" value="*#02468456789" />
</label>

from my comment, i'll also go with a funny answer : any feed back appreciated

input {
  width: 200px;
  font-size: 30px;
}
label {
  display: inline-block;
  width: 200px;
  overflow: hidden;
  margin:0 2em;
  background:gray;
}
#rtl {
  float: right;
  text-align: right;
  width: 900px;/* whatever */
  margin-left: -900px;/* at least as much as width to give a virtual width close to null, so it sticks at right but do not get pushed  */
}
<label for="ltr">ltr
  <input id="ltr" value="*#abcdefghijklmnopqrstuv" />
</label>

<label for="rtl">rtl
  <br/>
  <input id="rtl" value="*#abcdefghijklmnopqrstuv" />
</label>

https://jsfiddle.net/8zbzy4sL/7/

G-Cyrillus
  • 101,410
  • 14
  • 105
  • 129
  • Why you create `input`? @joshfarrant need to `div` that contain static text. – Mohammad May 10 '16 at 16:14
  • @Mohammad input are from the fiddle the OP linked , i used the op HTML. this technique works with any other imbricated tag, it worked even with IE5.5 ... long long ago ;) – G-Cyrillus May 10 '16 at 16:14
0

you can use float:right and text-align:right in input

input {
  width: 200px;
  font-size: 30px;
}
#rtl, label:last-of-type {
  float: right;
  text-align: right;
}
<label for="ltr">ltr
  <input id="ltr" value="*#02468" />
</label>
<hr />
<label for="rtl">rtl
  <input id="rtl" value="*#02468" />
</label>
dippas
  • 58,591
  • 15
  • 114
  • 126
0

You can use CSS position to do this work. For your purpose you need to create two div. Parent div and child div. Child div contain text and has ltr direction.

.box {
    width: 500px;
    height: 50px;
    padding-top: 20px;
    background: #26A0DA;
}

.parent {
    width: 300px;
    height: 30px;
    margin: 0px auto;
    position: relative;   
    overflow: hidden; 
}
  
.child {
   position: absolute;
   right: 0px;
   font-family: arial;
   font-size: 25px;
   color: #eee;
}
<div class="box">
    <div class="parent">
        <div class="child">abcdefghijklmnopqrstuvwxyz</div>
    </div>
</div>
Mohammad
  • 21,175
  • 15
  • 55
  • 84