Alex
Alex

Reputation: 5636

Selecting the last line of a <p> element

I am aware of the :first-line selector in CSS, but I cannot find anything that will allow me to select the last line.

The reason I am trying to select the last line is because I want to be able to define the text-overflow and white-space attributes of the last line.

I am trying to avoid using JavaScript to form a solution.

Upvotes: 17

Views: 24738

Answers (5)

mattpr
mattpr

Reputation: 3230

You can use getComputedStyle to attempt to "clone" the original paragraph styles. Then set height:auto on the clone and add words in 1 by 1 detecting wrapping based on the offsetHeight change of the clone.

This probably doesn't work under a variety of conditions:

  • when absolute positioning with top/right/bottom/left set
  • when using auto-hyphenation to break words across lines
  • when paragraph includes inline elements that change font size, weight, character spacing, etc.
  • when p { line-height:0; }
  • probably a lot more

But for simple cases, it does work if you really need it. But it is definitely a hack. You can use similar technique to figure out text size to avoid wrapping when working with dynamic text length (contrived example...usually not user friendly to dynamically size text).

Pen if the below example doesn't work or if you prefer pug/scss.

// https://stackoverflow.com/a/19824266/
function copyNodeStyle(sourceElement, targetElement) {
    var computedStyle = window.getComputedStyle(sourceElement);
    Array.from(computedStyle).forEach(function (key) {
        return targetElement.style.setProperty(key, computedStyle.getPropertyValue(key), computedStyle.getPropertyPriority(key));
    });
}

function getElementHeight(el) {
    return el.offsetHeight;
}

function createTestElement(sourceElement) {
    var body = document.getElementsByTagName('body')[0],
        elTest = document.createElement(sourceElement.tagName);

    body.appendChild(elTest);
    copyNodeStyle(sourceElement, elTest);
    elTest.style.height = 'auto';  // so height adjusts based on content (width should be fixed to match original)
    elTest.style.transform = 'translateX(-2000px);'; // make sure test element is hidden out of viewport.  
    // could break things if original sizing was dependent on a transform

    elTest.textContent = 'A';  // default content, single character (no wrapping possible)

    return elTest;
}

function getLines(sourceElement) {
    var elTest = createTestElement(sourceElement),
        oneLineParagraphHeight = getElementHeight(elTest),
        tokens = sourceElement.textContent.split(/ +/g),
        lines = [],
        line = '', token, currHeight;

    for (token of tokens) {
        // add the next token and check if we wrapped
        elTest.textContent = line + ' ' + token;
        currHeight = getElementHeight(elTest);

        if (currHeight > oneLineParagraphHeight) { // adding this token caused wrap
            lines.push(line);
            line = token; // this token starts a new line
        }
        else {
            line += ' '+token;  // token part of current line
        }
    }
    lines.push(line);
    elTest.remove();  // remove our test element
    return lines;
}


const sourceElement = document.querySelector('#demo'),
      lines = getLines(sourceElement);

console.log('lines', lines);

// modify last line (so there was some point to getting lines)
lines[lines.length-1] = '<span class="last-line">'+lines[lines.length-1]+'</span>';
sourceElement.innerHTML = lines.join(' ');
p#demo {
    width: 300px; /* constrain width so text wraps */
    border: 1px solid red; /* so we can see bounding box */
}

p#demo .last-line {
    color: purple;
    font-weight: bold;
}
<p id="demo">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec pellentesque lectus in pellentesque dictum. Maecenas ultrices et neque in iaculis. Maecenas accumsan leo vel dolor sollicitudin gravida.</p>

Upvotes: 1

Immanuel
Immanuel

Reputation: 5

You can use only one line and give css : nth-child(3)

Here (3) denotes the number of a last line

Upvotes: -3

MMM
MMM

Reputation: 7310

Unfortunately there is no CSS selector that would apply.

You can however select the first line using the pseudo element :first-line. Unfortunately at the moment there is no equivalent for the last line.

Upvotes: 9

David Allen
David Allen

Reputation: 1163

I don't think this can be done with CSS alone. jQuery or a PHP function might be able to get want you what

Upvotes: 3

rickyduck
rickyduck

Reputation: 4084

IIRC, the only css control over the last line is text-align:

text-align-last: right;

I'm afraid javascript, or actually wrapping the last line in a span and styling that, is the only way. Below is the JS for reference.

var thetext = document.getElementById('some_element').innerHTML.split(/\r?\n/);
alert(thetext[thetext.length - 1]);

Upvotes: 4

Related Questions