Question Overflow
Question Overflow

Reputation: 11275

How to position a row of text based on a particular letter?

I have a line of text as follow:

->A B C D E F G H I J                   <-

I want to position the line of text using CSS such that letter D is at the centre of the page as shown below:

->             A B C D E F G H I J      <-

Any idea?

Upvotes: 0

Views: 886

Answers (4)

David Thomas
David Thomas

Reputation: 253396

This is posted for two simple reasons:

  1. I commented and said that you'd need to use JavaScript, and
  2. Sometimes, I just can't help myself...

That said, here's a plain JavaScript solution that does not require any library. It's somewhat cumbersome, and I feel that there must be ways in which it can be simplified, however:

// helper function, to save typing later on.
function gEBTN(tag, parent, index) {
    if (!parent && !index) {
        return document.getElementsByTagName(tag);
    }
    else if (parent && !index) {
        return parent.getElementsByTagName(tag);
    }
    else if (index) {
        return parent.getElementsByTagName(tag)[index];
    }
}

// helper function, to save typing later on.
function gCS(elem, parent, prop) {
    if (!prop && parent) {
        return window.getComputedStyle(elem.parentNode, null).getPropertyValue('width');
    }
    else if (prop && parent == true) {
        return window.getComputedStyle(elem.parentNode, null).getPropertyValue(prop);
    }
    else if (!parent && !prop) {
        return window.getComputedStyle(elem, null).getPropertyValue('width');
    }
    else {
        return window.getComputedStyle(elem, null).getPropertyValue(prop);
    }
}

// page: number.
// navElemId: a quoted string, the Id of the element containing
//            the navigation elements, defaults to 'navigation'
// container: a quoted string, the type of element ('div','span','li'...)
//            that forms the navigation 'buttons.' Defaults to 'li.'
function centreNavigationOn(page, navElemId, container) {
    if (!page) { // at minimum you have to supply the page you want to centre on
        return false;
    }
    else {
        var navElemId = navElemId || 'navigation';
        var navElem = document.getElementById(navElemId);
        var containers = gEBTN(container) || gEBTN('li', navElem);
        for (var i = 0, len = containers.length; i < len; i++) {
            if (containers[i].innerHTML == page) {
                var centreOn = containers[i];
            }
            else {
                // just for an easy visual difference between the centred/current
                // page and the others. Adjust/remove according to taste
                containers[i].style.opacity = '0.3';
            }
        }
        if (!centreOn){
            // if no page is found to be centred on, quits
            console.log('No element found to centre on. Quitting now.');
            return false;
        }

        // again, just for simple visual difference between current and others...
        centreOn.style.backgroundColor = '#f90';
        centreOn.style.opacity = '1';

        // finds the width of the centreOn element
        var cOnWidth = parseInt(gCS(centreOn),10);
        var cOnLeftOffset = centreOn.offsetLeft;

        // the centreOn element's parent's parent's width
        // this made sense when I wrote it, now...I'm not so sure... =/
        cPPWidth = parseInt(gCS(centreOn.parentNode.parentNode),10);

        // works out by what distance the centreOn element's parent
        // must be moved in order to centre the centreOn element within the page.
        var moveBy = Math.round((cPPWidth/2) - (cOnLeftOffset + (cOnWidth/2)));
        centreOn.parentNode.style.marginLeft = moveBy + 'px';
    }
};

centreNavigationOn(4, 'navigation', 'li');​

JS Fiddle demo.

Notes: this function will fail in those browsers, such as IE, that have no implementation of window.getComputedStyle(). This probably guarantees that, as interesting as it was to put together, this script is unlikely to be used. Except, perhaps, in a modified form (I do, of course, freely give this script to anyone who wants to use it, releasing it even beyond the CC-By-SA license of Stack Overflow, as I think I'm able...). Though if anyone does modify it, I'd genuinely appreciate their linking to the modified version, either in comments or by editing this answer to add links...

References:

Upvotes: 1

Zeta
Zeta

Reputation: 105905

If you use a monospaced font then you can use position:relative; and move your row of text, since every letter and space has the same size (demo). You can achieve a similar result on fonts without fixed-width, but the offset (left:;) is a little bit harder to calculate, as almost every letter occupies another amount of horizontal space.

<nav class="letter monospace">
    <div>A B C D E F G H I J</div> <!-- monospaced font demo -->
</nav>
<nav class="letter">
    <div>A B C D E F G H I J</div> <!-- standard browser font demo -->
</nav>
<div id="leftmiddle"> </div>
<div id="rightmiddle"> </div>
nav.letter{
    font-size:2em;
    text-align:center;
}
nav.letter > div{ /* standard browser font */
    position:relative;
    left:1.05em; /* you have to calculate the horizontal shift yourself based on the font you're using */
}
nav.letter.monospace{
    font-family:monospace;
}
nav.letter.monospace > div{ /* monospaced font */
    position:relative;
    left:1.75em; /* to center the row around 'E' change to 0.75em, for C to 2.75em... */
}

nav ~ div{
    width:49%;
    min-height:200px;
    height: 80%;    
}
#leftmiddle{ float:left;    border-right:1px solid;}
#rightmiddle{    float:right;    border-left:1px solid;}

HTML4 demo, working in IE7-9, FF10, Opera, Chrome.

UPDATE: See this alternative demo (working in IE7-9, FF10, Opera, Chrome):

<div class="letter">
    <div class="wrapper">
        <div class="before">
            A B C
        </div>
        D       
        <div class="after">
            E F G H I J
        </div>
    </div>
</div>
.letter{
    text-align:center;
    overflow:hidden;
}
.letter > .wrapper{
    width:1em;
    margin:auto;
    position:relative;
}
.letter > .wrapper > .before{
    position:absolute;
    right:125%;
    text-align:right;
    width:10em;
}
.letter > .wrapper > .after{
    position:absolute;
    text-align:left;
    top:0;
    left:125%;
    width:20em;
}

Upvotes: 3

maxedison
maxedison

Reputation: 17573

As @DavidThomas pointed out, JavaScript is required. I just wanted to expand on this a bit.

CSS cannot achieve this for a couple main reasons:

  1. There's no way to know how to alter the line of text in question so that D is centered until the document flow has been established. But the application of CSS is central to document flow, so at the time that CSS is being applied it can't possibly know what the final, resulting document will be. In other words, we'd have a catch 22 on our hands if CSS could be used for this. In order to "know" how to position that line of text, the page's CSS would already need to know what the final document would look like. But in order to get the final document, the CSS first needs to be applied.
  2. Even if 1 & 2 weren't the case, CSS still has no way of analyzing a page, grabbing the position of elements, etc. This simply isn't its purpose.

In contrast, JavaScript can be used to solve this issue because:

  1. Unlike CSS, JavaScript can do its work after the document flow has been established.
  2. JavaScript has the necessary tools for analyzing the page so that it can make the necessary changes.

Hard to say how complicated it would be to achieve with JavaScript without knowing more about your specific case. If you know JavaScript, however, I think it would be fairly simple.

Upvotes: 1

Sparklellama
Sparklellama

Reputation: 720

Maybe use something like

<div id="container" style="position: absolute; width:1000px; height:10px">
    <div id="innerContainer" style="position: absolute">
        <span>A</span><span>B</span><span>C</span><span>D</span><span>E</span><span>F</span>
    </div>
</div>

and then something like the following in JQuery in $(document).ready() {}

var left_pad = 20; // set this to container width / 2
                   // calculate it or hard code it
$("#innerContainer").css("left"),$((left_pad - span:contains('D').position().left) + 'px')

just off the top of my head, no time to test, but hope it points you in the right direction. With a bit of patience this should be able to be gotten working.

Upvotes: 1

Related Questions