skip to Main Content

I am formatting a Shakespearean script in HTML. Because Shakespeare wrote in meter, it is customary to indent lines that do not begin on the first beat of the meter, like so:

p.indent { padding-left: 100pt; }

<h4>HERMIA</h4>
<p>O me!</p>
<h5>[To Helena]</h5>
<p class="indent">You juggler, you cankerblossom,</p>
<p>You thief of love! What, have you come by night</p>
<p>And stol’n my love’s heart from him?</p>
<h4>HELENA</h4>
<p class="indent">Fine, i’ faith.</p>
<p>Have you no modesty, no maiden shame,</p>
<p>No touch of bashfulness? What, will you tear</p>
<p>Impatient answers from my gentle tongue?</p>
<p>Fie, fie, you counterfeit, you puppet, you!</p>

HERMIA

O me!

[To Helena]

        You juggler, you cankerblossom,
You thief of love! What, have you come by night
And stol’n my love’s heart from him?

HELENA

        Fine, i’ faith.
Have you no modesty, no maiden shame,
No touch of bashfulness? What, will you tear
Impatient answers from my gentle tongue?
Fie, fie, you counterfeit, you puppet, you!

However, rather than indent by a fixed amount, it would be ideal to indent to the width of the prior line. That would produce an appearance closer to this:

HERMIA

O me!

[To Helena]

   You juggler, you cankerblossom,
You thief of love! What, have you come by night
And stol’n my love’s heart from him?

HELENA

                 Fine, i’ faith.
Have you no modesty, no maiden shame,
No touch of bashfulness? What, will you tear
Impatient answers from my gentle tongue?
Fie, fie, you counterfeit, you puppet, you!

In the desired solution, the amount of left-padding of each p.indent element should bes equal to the width of the most recent prior p element (which in general is not the most recent prior element, as h4 or h5 elements may intervene).

I assume I can’t do this in pure CSS, but I’ve been surprised about that before. If not, then simple JS?

2

Answers


  1. At first, I thought I could just read the length of the words in the heading / but that’s not really the same if it’s uppercase or lowercase, and it isn’t a winning approach.

    Are you sure you want to hand put those ‘.indent’ classes? What about just anything after a heading? The first paragraph after any heading? Either way the tools you’d use would be the same.

    For each .indent paragraph, identify the nearest preceding sibling that is a heading ( to ) You can use .previousSibling https://developer.mozilla.org/en-US/docs/Web/API/Element/previousElementSibling — then to get the width of the actual font/text in the heading – you kinda need a span or some sub element that isn’t a block-level element (not full-width) / so, you can either put that in there when rendered – or add it with JS. Then you can check those with getBoundingClientRect() https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect — and then you can check that width and use that to set the indent of it’s sibling paragraph. I think that’s about as simple as you’re going to get. Alternatively, you could grab all the elements, and go down and use nextSibling and get the first paragraph after headings.

    Login or Signup to reply.
  2. I doubt this is achievable with css alone.

    Javascript to determine indents

    It’s fairly straight forward to apply an indent. IE setting a padding-left value.
    (though this does not play nicely with line-breaks inside of <p> tags, if line-breaks are expected, you would prepend the line with the required amount of &nbsp;s to apply the offset)
    To find the amount to pad, we need to know what the pairs of <p> tags are. Once we have all pairs collected we can figure out the position of last character and use that to indent the next line. (in case of line-breaks inside <p> you might need the translate that position to an increment of &nbsp;s)

    function getLastCharacterCoordinates(element) {
      const textNode = element.firstChild;
      const textLength = textNode.length;
      if (textLength === 0) return null;
    
      const range = document.createRange();
      // Set the range to select only the last character
      range.setStart(textNode, textLength - 1);
      range.setEnd(textNode, textLength);
      const rect = range.getBoundingClientRect();
      return {
        x: rect.left,
        y: rect.top
      };
    }
    
    function setIndent(element, indentSize) {
      // you might need to prepend &nbsp; characters instead of setting paddingLeft. depends on whether there are line-breaks inside your p tags. 
      element.style.paddingLeft = `${indentSize}px`
    }
    
    function findPairs() {
      // returns an array of pairing <p> tags, 
      // where the next line should start at the end of the previous line.
      const boundary = document.getElementById("boundary");
      const children = Array.from(boundary.children);
      // Reduce to find pairs of <p> tags separated by any other tag
      const result = children.reduce((acc, child, index, arr) => {
        // If the current element is <p> and there is a next element
        if (child.tagName === 'P' && index < arr.length - 2) {
          const nextTag = arr[index + 1].tagName; // Check the tag in between
          const nextP = arr[index + 2]; // The tag two steps ahead
          // If the element two steps ahead is also a <p> and there is another tag in between
          if (nextP.tagName === 'P' && nextTag !== 'P') {
            // we found a pair
            acc.push([child, nextP]);
          }
        }
        return acc;
      }, []);
      return result;
    }
    
    // call the functions and apply the indents:
    const pairs = findPairs()
    pairs.forEach(pair => {
      const cord = getLastCharacterCoordinates(pair[0])
      setIndent(pair[1], cord.x)
    })
    p,
    h1,
    h2,
    h3,
    h4,
    h5 {
      /* remove default browser margins */
      margin: 0;
    }
    <div id="boundary">
      <h4>HERMIA</h4>
      <p>O me!</p>
      <h5>[To Helena]</h5>
      <p>You juggler, you cankerblossom,</p>
      <p>You thief of love! What, have you come by night</p>
      <p>And stol’n my love’s heart from him?</p>
      <h4>HELENA</h4>
      <p>Fine, i’ faith.</p>
      <p>Have you no modesty, no maiden shame,</p>
      <p>No touch of bashfulness? What, will you tear</p>
      <p>Impatient answers from my gentle tongue?</p>
      <p>Fie, fie, you counterfeit, you puppet, you!</p>
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search