Marc
Marc

Reputation: 6190

Vertically align <span> in <li> with unknown height

I am working on some kind of context menu. The arrow that indicates whether a row will provide a submenu should always be centered. This works well with single line <li> elements, but not with multiline. Here the <span> that contains the arrow symbol is aligned with the last line of the text.

It gets worse if the menu contains a scrollbar, then the arrow is beneath the line.

I am thankful for any help, the solution can contain JS/jQuery if that's required to be flexible.

You can find my current source here: http://jsfiddle.net/ass9sxo6/3/

Btw: Would be even better if the arrow symbol was in the padding area of the <li> so that it doesn't steal any space that could be used for text.

Upvotes: 2

Views: 1244

Answers (5)

public override
public override

Reputation: 982

Set .indicator element to position: absolute; (and give its' parent <li> non-static positioning). Give it custom right offset (say: right: 5px;), set .top and .bottom to 0, and set .line-height to parent <li>s' height. It centers an indicator verticaly automatically as <li>s' height changes. Something like this should work fine:

.indicator {
  margin      : 0;
  padding     : 0;
  bottom      : 0;
  display     : block;
  line-height : 50px; /* same as parents' height, assuming its' 50px... */
  position    : absolute;
  right       : 5px;
  top         : 0;
}


I sometimes use this less snippet to easily tweak menu properties. Adjust the variables in file header, export it as css, run: lessc --clean-css ul-stacked.less > ul-stacked.min.css, and slap a ul-stacked class to any <ul> with this structure: FIDDLE

<ul class="ul-stacked">
  <li><span>ipsum</span></li>
  <li class="parent"> <span> nulla <span class="indicator">&gt;</span> </span> </li>
  <li><span>lorem</span></li>
  <!-- etc. -->
</ul>


// ul-stacked.less

// #vars

@class-name          : ul-stacked;
@class-name-feedback : indicator;
@class-name-parent   : parent;

@body-bg               : #f8f8f8;
@body-frame-bg         : #ccc;
@body-frame-width      : 2px;
@body-height-base      : 50px;
@body-pad-content-base : .2em;
@border-radius-base    : 2px;
@feedback-offs-right   : 1em;
@spacing-bottom-base   : 2px;


@body-bg-hover         : lighten(@body-bg, 2%);
@body-height           : @body-height-base * 1;
@spacing-bottom        : @spacing-bottom-base + @body-frame-width / 2;


// #mixins

.reset-base() {
  border  : none;
  margin  : 0;
  outline : none;
  padding : 0;
}

.wrapper-fluid() {
  display : block;
  height  : auto;
  width   : 100%;
}

.list-unstyled() {
  list-style          : none;
  list-style-position : outside;
  margin              : 0;
  padding             : 0;
}

.borders(@r: @border-radius-base) {
  border-radius: @r;
}

ul.@{class-name} {

  .list-unstyled;
  .reset-base;
  .wrapper-fluid;

    > li {

      .borders;
      .reset-base;

      background : @body-frame-bg;
      display    : block;
      float      : none;
      height     : @body-height;
      position   : relative;
      width      : 100%;

      > * {

        &:hover {
          background : @body-bg-hover;
        }

        background : @body-bg;
        bottom     : @body-frame-width;
        display    : block;
        left       : @body-frame-width;
        padding    : @body-pad-content-base;
        position   : absolute;
        right      : @body-frame-width;
        top        : @body-frame-width;
      }
    }

    > li {
      margin-bottom: @spacing-bottom;
    }

    > li:last-child {
      margin-bottom: 0;
    }

    > li.@{class-name-parent} {
      > * {
        > .@{class-name-feedback} {
          .reset-base;
          bottom      : 0;
          display     : block;
          line-height : @body-height;
          position    : absolute;
          right       : @feedback-offs-right;
          top         : 0;
        }
      }
    }
}

// eof

Upvotes: 1

Le-roy Staines
Le-roy Staines

Reputation: 2057

Instead of adding <span class="indicator"></span> to the end of every <li> element you can use CSS3 :after selector with the content attribute to append an arrow at the end of every <li> element.

Then you set CSS attribute position:relative on each <li> element so that it's child elements will inherit its position. And on each li:after content you can set display:block and position:absolute along with top:50% and margin-top:-0.5em to get the vertical alignment you want.

Here's a modification to your JSFiddle: http://jsfiddle.net/ass9sxo6/6/

And the CSS attributes I added to demonstrate the effect:

li {position:relative;}
li:after {
    content: '›';
    font-weight: bold;
    position: absolute;
    right: 5px;
    top: 50%;
    margin-top: -0.5em;
}

P.S. You should try use Ascii symbols where possible rather than base64 images to keep the file size small and the code easy to understand.

Upvotes: 2

dashtinejad
dashtinejad

Reputation: 6253

You can use absolute:

li {
    position: relative;
}

.indicator {
    position: absolute;
    top: 50%;
    right: 0;
    margin-top: -8px;
}

JSFiddle Demo.

Upvotes: 1

Karthik N
Karthik N

Reputation: 535

Just remove the span tag and make the background image to li tag... Its very simple.

Check this fiddle demo.

.box li {
  background-image: url(https://cdn4.iconfinder.com/data/icons/ionicons/512/icon-arrow-right-b-16.png);
  background-position:right center;
  background-repeat: no-repeat;
}
<div class="box">
    <ul>
        <li>
            This will be some multiline content
        </li>
        <li>
            and single line
        </li>
        <li>
            This will be some multiline content
        </li>
        <li>
            and single line
        </li>
        <li>
            This will be some multiline content
        </li>
        <li>
            and single line
        </li>
    </ul>
<div>

Upvotes: 0

emmanuel
emmanuel

Reputation: 9615

You could center your span with this addition in your css:

.box li { 
  position: relative;
}

.box ul li span { 
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0;
  margin: auto;
 }

Fiddle: http://jsfiddle.net/ass9sxo6/4/

Positioned the span element absolute with top / bottom / right values 0 and set margin to auto. In order to work needs relative position to container element li.

Upvotes: 3

Related Questions