6

origin

origin

result

result

I want to split a string into character, and make each of the character fit the container equally, this is my work for the time being: http://jsfiddle.net/d5fu9/

The first item must attached to the left, and the last item must attached to the right.

  $.fn.textjustify = function() {
        return this.each(function() {
            var text = $(this).text(),
                containerWidth = $(this).width(),
                character = '',
                string = '',
                singleWidth = 0,
                firstItemWidth = 0,
                lastItemWidth = 0,
                alignWidth = 0;

            if ('' !== text) {
                $(this).css('position', 'relative');
                textArray = text.split('');
                singleWidth = Math.floor(containerWidth / textArray.length);

                for (i in textArray) {
                    // Wrapp with div to get character width
                    character = ('' === $.trim(textArray[i])) ? '&nbsp' : textArray[i];
                    string += '<span>' + character + '</span>';
                }

                $(this).html(string);

                firstItemWidth = $(this).find('span:first').width();
                lastItemWidth = $(this).find('span:last').width();
                alignWidth = containerWidth - (firstItemWidth + lastItemWidth);

                $(this).find('span').each(function(i) {
                    if (0 === parseInt(i)) {
                        // The first item
                        $(this).css({position: 'absolute', left: 0, top: 0});
                    } else if ((parseInt(textArray.length) - 1) === parseInt(i)) {
                        // The last item
                        $(this).css({position: 'absolute', right: 0, top: 0});
                    } else {
                        // Other items
                        // stuck in here......
                        var left = (i * singleWidth) - $(this).width() + firstItemWidth;
                        $(this).css({position: 'absolute', left: left, top: 0});
                    }
                });

            }
        });
    }

stuck in the algorithm of middle items's position.

Chan
  • 1,947
  • 6
  • 25
  • 37
  • Posted a fix where you loop all divs with the "spread" class and then fix the chars within them. – KungWaz Dec 12 '13 at 10:23

6 Answers6

1

I think this is the simplest solution. Works great with All browsers (IE included)

  1. without complex (and unreliable) width detection and calculation.
  2. without specifying the words width/height
  3. without relative/absolute positioning
  4. using pure HTML/CSS/JS/JQ tricks.

Working Fiddle

HTML:(very simple)

<div class="Box">
    <div class="Centered">
        <div class="Spread">Lighting</div>
        <div class="Spread">我是中文</div>
    </div>
</div>

CSS:(neat and tricky)

*
{
    margin: 0;
    padding: 0;
}
.Box
{
    width: 150px;
    height: 150px;
    border: 1px solid red;
    margin: 6px;
}
.Box:before
{
    content: '';
    display: inline-block;
    height: 100%;
    vertical-align: middle;
    margin-left: -5px;
}
.Centered
{
    display: inline-block;
    vertical-align: middle;
    width: 100%;
}
.Spread
{
    width: 100%;
    text-align: justify;
    font-size: 0;
}
    .Spread span
    {
        font-size: medium;
    }
.Spread:after
{
    content: '';
    display: inline-block;
    height: 0px;
    width: 100%;
}

JS/JQ:

$.fn.SplitText = function () {
    return this.each(function () {
            return $(this).html('<span>' + $(this).text().split('').join(' ') + '</span>');
    });
}

$(function () {
    $('.Spread').SplitText();
})

Explanations: as mentioned by wared in the comments, IE7 doesn't support the use of pseudo classes. but they are not necessary for the solution. Here's a Fiddle for IE7 (and all other browsers of course).

how the vertical aligning works? when vertical-align:middle; is used on an inline element, it will align the middle of this element with the other inline elements in the same line. that's why I'm creating an inline element with height:100%;, so when we align our inline element to his middle, it will actually be the middle of the container.

how the horizontal distribution works? taking advantage of the text-align:justify;, we create an empty inline element (height:0;) with width:100%;, we can imagine that it takes a full line, and the rest of the text takes the second line. using justify makes the second line spread evenly to take the exact space as the first.

let me know if you need more explanation.

avrahamcool
  • 13,888
  • 5
  • 47
  • 58
  • +1 Nice one! I like the `.split('').join(' ')` trick to activate justification :) –  Dec 13 '13 at 08:34
  • `:after` and `:before` are not supported by IE7 though. There might be a workaround somewhere. I've found this link : http://stackoverflow.com/q/4181884/1636522. –  Dec 13 '13 at 08:53
  • Have you got a link to share which explains this CSS trick to center things vertically and horizontally? I mean, I can't figure out how this `content:''` thing with `height:100%` works ^^' –  Dec 13 '13 at 09:00
  • 1
    @wared I use the `:after` and `:before` only to reduce elements in the DOM, if you need IE7 support, just add them manually and target them via CSS. I'll add some explanation shortly of how the whole thing works.. – avrahamcool Dec 13 '13 at 09:09
  • @wared add explanation. let me know if you need further explanation. – avrahamcool Dec 13 '13 at 09:24
  • This is really nice, thanks :) I was wondering how to center my own text without using `line-height`. Indeed, [my solution](http://stackoverflow.com/a/20541563/1636522) doesn't handle multiple lines since the space between two lines depends on the line height which is set to `200px`... Anyway, I'll leave this as it is. Thanks again for sharing this clever trick, I think I got it ^^' –  Dec 13 '13 at 09:46
0

I added a fixed with for spanelements for the widest char which was W in your fiddle with 15px.

span {
    width: 15px;
}

Then substracted 20px from container width which will be the free space on the sides later.

singleWidth = Math.floor((containerWidth-20) / textArray.length);

Added this extra Space to the firstitemWidth so that the other chars align correctly:

firstItemWidth = $(this).find('span:first').width() + 10;

And set the position of first and last to 10 from left and right here:

if (0 === parseInt(i)) {
               // The first item
               $(this).css({position: 'absolute', left: 10, top: 0});
} else if ((parseInt(textArray.length) - 1) === parseInt(i)) {
               // The last item
               $(this).css({position: 'absolute', right: 10, top: 0});

Here is the updated Fiddle

I hope this will work out for you ;)

Igl3
  • 4,900
  • 5
  • 35
  • 69
  • The first item must be attached to the left, and the last item must to attached to the right, sorry for not clarify the question. – Chan Dec 12 '13 at 10:21
0

I have made a script for you if you do not mind left and right spaces. You can set the right and left margins making some little modifications.

$(document).ready(function(){

    var txt = $(".container").text(); //get the text
    var txt2 = "";
    var len = txt.length;

    $(".container").empty();  //clear the content

    for(i=0; i<len; i++) //surround all characters with span
    {
        txt2 += "<span>" + txt.substr(i,1) + "</span>";    
    }

    $(".container").html(txt2);

    $("span").css({"width":  Math.round($(".container").width())/len + "px", "display":"inline-block", "text-align":"center"});
});

Fiddle is here

zkanoca
  • 9,664
  • 9
  • 50
  • 94
0

Try this:

DEMO: http://jsfiddle.net/jc2mm/4/

JS:

$(".spread").each(function(idx, elem) {
    var titleElem = $(this),
        titleStr = titleElem.html(),
        charWidth = Math.floor(($(".box").width() / titleStr.length) * 100) / 100;

    titleElem.html("");

    for(var i = 0; i < titleStr.length; i++) {
        titleElem.append($("<span>", {"class" : "singleChar"}).css("width", charWidth).html(titleStr[i]));
    }
});

CSS:

.box {
    width: 150px;
    height: 150px;
    border: 1px solid red;
    margin: 6px;
}
.title {
    margin-top: 40px;
    height: 16px;
}
.singleChar {
    float: left;   
    text-align: center;
    display: block;
    min-height: 1px;
}
KungWaz
  • 1,918
  • 3
  • 36
  • 61
0

I've made this stuff : http://jsfiddle.net/wared/HDbA5/.

The idea is to use paddings to create space between chars. Tested with Chrome only. It seems to fail when the DIV has no explicit fixed width, and sometimes there is one pixel remaining, I haven't investigated. Lastly, this script does not support single characters. If you're interested in this answer, this job is for you :)


I must show some code to be able to post this answer, so, here it is :

<div><span>hello</span></div>
jQuery.fn.justify = function () {
    return this.each(function () {
        var el = $(this),
            ct = el.parent(),
            chars = el.text().split(''),
            bits = (chars.length - 1) * 2,
            freeSpace = ct.width() - el.width(),
            bitWidth = Math.floor(freeSpace / bits),
            gap = freeSpace % bits;
        el.html(jQuery.map(chars, function (char, i) {
            var paddings = ['0', null, '0', null];
            if (!i) {
                paddings[1] = (bitWidth + (gap && (--gap, 1))) + 'px';
                paddings[3] = '0';
            } else if (i + 1 === chars.length) {
                paddings[1] = '0';                
                paddings[3] = (bitWidth + (gap && (--gap, 1))) + 'px';
            } else {
                paddings[1] = (bitWidth + (gap && (--gap, 1))) + 'px';                
                paddings[3] = (bitWidth + (gap && (--gap, 1))) + 'px';
            }
            return '<span style="padding:' + paddings.join(' ') + '">' + char + '</span>';
        }).join(''));
    });
};

A "bit" is a portion of the free space. It's half the space between two characters :

"abc"   : 4 bits ("a..b..c").
"ab cd" : 8 bits ("a..b.. ..c..d").

The "gap" is the number of pixels remaining after splitting the free space into bits. Those pixels are assigned to each "bit" until no pixels remain. Let's say gap = 1 :

bitWidth + (gap && (--gap, 1)) // gap = gap - 1 THEN bitWidth + 1
// next bit
bitWidth + (gap && (--gap, 1)) // bitWidth + 0
0

Try this: http://jsfiddle.net/d5fu9/5/

Each character is contained in a box of width singleWidth, left is computed counting the preceding character boxes, characters are centered. Changes:

$(this).css({width: singleWidth,position: 'absolute', left: 0, top: 0});

and

var left = i* singleWidth + (i-1)*(textArray.length-2)/(textArray.length+1);

and in CSS:

.spread {
    text-align: center;
}

Here is another version http://jsfiddle.net/d5fu9/6/ where leftmost and rightmost characters are attached to the border.

Modified width of a single character container:

singleWidth = Math.floor((containerWidth -firstItemWidth - lastItemWidth)/ (textArray.length-2));

and how much each inner character moves from the left:

var left = firstItemWidth+(i-1)* singleWidth ;
user2314737
  • 27,088
  • 20
  • 102
  • 114