javascript - Visa 'Läs mer' på samma rad som avklippt text efter 'x' rader


Jag försöker lägga till åtgärden Läs mer på samma rad som den avklippta texten efter 'x' rader. Just nu visas den under den avklippta texten.

<p style={{ overflow: 'hidden', display: '-webkit-box', 
-webkit-line-clamp: 4, line-clamp: 4, -webkit-box-orient: 'vertical' 
}}>
   Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed 
   do eiusmod tempor incididunt ut labore et dolore magna aliqua. 
   Ut enim ad minim veniam, quis nostrud exercitation ullamco 
   laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure
   dolor in reprehenderit in voluptate velit esse cillum dolore eu 
   fugiat nulla pariatur. Excepteur sint occaecat cupidatat non 
   proident, sunt in culpa qui officia deserunt mollit anim id est 
   laborum.
</p>
<Button>Read more</Button>

Jag kan hantera funktionen där texten växlas genom att ändra overflow-egenskapen till visible/hidden. Jag försöker bara styla det på ett sätt där Läs mer alltid är på sista raden i stället för ellipsis.

Detta klipper för närvarande av vid 4 rader vilket är korrekt, men vill ersätta ellipsen med knappen Läs mer.

All hjälp uppskattas

Svar #1

Här är ett exempel:

import { useState, useRef, useEffect } from 'react';

const ClampTextWithReadMore = ({ text, maxLines }) => {
  const [isClamped, setIsClamped] = useState(false);
  const textRef = useRef(null);

  useEffect(() => {
    if (textRef.current) {
      const computedStyle = window.getComputedStyle(textRef.current);
      const lineHeight = parseInt(computedStyle.lineHeight);
      const maxHeight = maxLines * lineHeight;

      if (textRef.current.scrollHeight > maxHeight) {
        setIsClamped(true);
      }
    }
  }, [text, maxLines]);

  const toggleClamp = () => {
    setIsClamped(!isClamped);
  };

  return (
    <div style={{ position: 'relative' }}>
      <p
        ref={textRef}
        style={{
          overflow: isClamped ? 'hidden' : 'visible',
          display: '-webkit-box',
          WebkitLineClamp: maxLines,
          WebkitBoxOrient: 'vertical',
          maxHeight: isClamped ? `${maxLines * 1.2}em` : 'none', // Adjust as needed
        }}
      >
        {text}
      </p>
      {isClamped && (
        <button style={{ position: 'absolute', bottom: 0, right: 0 }} onClick={toggleClamp}>
          Read more
        </button>
      )}
    </div>
  );
};

// Usage
<ClampTextWithReadMore text={/* Your text here */} maxLines={4} />;

Denna kodsnutt använder useRef för att komma åt DOM-elementet som innehåller texten och beräknar om texten är begränsad baserat på maximal höjd. Om texten är begränsad renderas villkorligt knappen "Läs mer" som är placerad längst ner till höger i det begränsade området. Att klicka på knappen växlar det begränsade tillståndet.

Svar #2

Placera knappen inuti paragraf-taggen så att den blir en del av textflödet. Sedan behöver du använda lite CSS för att korrekt positionera knappen under trunkering.

<div style={{ position: 'relative' }}>
    <p style={{ overflow: 'hidden', display: '-webkit-box', 
                -webkit-line-clamp: 4, line-clamp: 4, 
                -webkit-box-orient: 'vertical', marginBottom: '0' 
              }}>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed 
        do eiusmod tempor incididunt ut labore et dolore magna aliqua. 
        Ut enim ad minim veniam, quis nostrud exercitation ullamco 
        laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure
        dolor in reprehenderit in voluptate velit esse cillum dolore eu 
        fugiat nulla pariatur. Excepteur sint occaecat cupidatat non 
        proident, sunt in culpa qui officia deserunt mollit anim id est 
        laborum.
        <span style={{ position: 'absolute', right: 0, bottom: 0 }}>
            <Button>Read more</Button>
        </span>
    </p>
</div>

Skapa en anpassad funktion:

import React, { useState, useRef, useEffect } from 'react';
const EllipsisText = ({ children, maxLine, lineHeight }) => {
  const textRef = useRef();
  const [readMore, setReadMore] = useState(false);
  const [text, setText] = useState(children);
  const maxTextHeight = maxLine * lineHeight;
  useEffect(() => {
    if (textRef.current) {
      if (textRef.current.scrollHeight > textRef.current.offsetHeight) {
        setReadMore(true);
        let trimLength = children.length * (maxTextHeight / textRef.current.scrollHeight);
        setText(children.substring(0, trimLength - 3) + '...'); 
      }
    }
  }, [children, maxTextHeight]);

  return (
    <div style={{ position: 'relative', lineHeight: `${lineHeight}px` }}>
      <p ref={textRef} style={{ maxHeight: maxTextHeight, overflow: 'hidden' }}>
        {text}
      </p>
      {readMore && (
        <button style={{ position: 'absolute', right: 0, bottom: 0 }} onClick={() => setReadMore(false)}>
          Read more
        </button>
      )}
    </div>
  );
};

const App = () => {
  const text = `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`;

  return (
    <div>
      <EllipsisText maxLine={4} lineHeight={16}>
        {text}
      </EllipsisText>
    </div>
  );
};

export default App;