skip to Main Content

I’m trying to show/hide some text (a highlight) in the middle of a paragraph. These criteria are essential:

  1. I need the method to be flexible to work on a single line or multi-line.
  2. And I need it to not mess with the rest of the paragraph’s text line-height.

Below are what I have so far but neither is quite right. Version 1 has the transition I would prefer i.e. width of the <mark> tag reducing to zero and using an overflow: hidden to mask the text as it is reducing is size. The problem with this is it can’t be multi-line. Version 2 is a compromise that reduces font-size: 0px; but can be multi-line.

Is there any solution to getting this to work (looking like version 1 but functioning like version 2)? Or is there another solution to this? (I have also tried transform: scaleX(0); and this does not work).

body
{
    font-size: 15px;
    line-height: 21px;
    font-family: 'Open Sans';
}

.section
{
    position: relative;
    margin: 0px 0px 50px 0px;
    width: 500px;
    height: 210px;
    background: #eeeeee;
    border: 1px solid #666666;
}

.paragraph
{
    position: relative;
    width: 100%;
    height: 100%;
    display: inline;
    vertical-align: middle;
}

.paragraph1
{
    background: #00ffff;
}

.paragraph2
{
    background: #00ffff;
}


/* version 1 */
.mark1
{
    position: relative;
    top: 2px;
    width: 150px;
    height: 21px;
    transition: all 1.0s;
    vertical-align: bottom;
    display: inline-block;
    overflow: hidden;
    white-space: pre;
}

.mark1 span
{
    position: relative;
    top: -2px;
    height: 23px;
    display: inline-block;
    background: #cccccc;
}

.mark1.hidden
{
  width: 0px;
  transition: all 1.0s;
}


/* version 2 */
.mark2
{
    position: relative;
    transition: all 1.0s;
    vertical-align: bottom;
    background: #00ff00;
}

.mark2 span
{
    position: relative;
    padding: 0px 0px 0px 0px;
    background: #cccccc;
}

.mark2.hidden
{
  font-size: 0px;
  transition: all 1.0s;
}
<!DOCTYPE html>
<html lang="">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>example</title>
  <link href='https://fonts.googleapis.com/css?family=Open Sans' rel='stylesheet'>
</head>

<body>


<button onclick="
const collection = document.getElementsByClassName('mark1');

for (let i = 0; i < collection.length; i++) {
collection[i].classList.toggle('hidden');
}
">toggle version 1</button>

<div class="section">
<div class="paragraph paragraph1">
Lorem Ipsum is<mark class="mark1"> <span>simply dummy text of</span></mark> the printing and typesetting industry. <mark class="mark1"><span>Lorem Ipsum</span> </mark>has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
</div>
</div>



<button onclick="
const collection = document.getElementsByClassName('mark2');

for (let i = 0; i < collection.length; i++) {
collection[i].classList.toggle('hidden');
}

">toggle version 2</button>

<div class="section">
<div class="paragraph paragraph2">
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the<mark class="mark2"> <span>industry's standard dummy text ever since the 1500s</span></mark>, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
</div>
</div>

</body>

</html>

2

Answers


  1. To archive this you need alteast 2 elements

    <mark class="mark1">
        <span class="mark-placeholder">simply dummy text of</span>
        <span class="mark-highlight">simply dummy text of</span>
    </mark>
    

    At this point you can animate width on mark-placeholder
    and transform mark-hightlight as you wish without interupt layout since its absolute

    body
    {
        font-size: 15px;
        line-height: 21px;
        font-family: 'Open Sans';
    }
    
    .section
    {
        position: relative;
        margin: 0px 0px 50px 0px;
        width: 500px;
        height: 210px;
        background: #eeeeee;
        border: 1px solid #666666;
    }
    
    .paragraph
    {
        position: relative;
        width: 100%;
        height: 100%;
        display: inline;
        vertical-align: middle;
    }
    
    .paragraph1
    {
        background: #00ffff;
    }
    
    .paragraph2
    {
        background: #00ffff;
    }
    
    
    /* version 1 */
    .mark1
    {
        position: relative;
        width: 150px;
        height: 21px;
        transition: all 1.0s;
        vertical-align: bottom;
        display: inline-block;
        overflow: hidden;
        white-space: pre;
    }
    
    .mark1.hidden
    {
      width: 0px;
      transition: all 1.0s;
    }
    
    .mark-placeholder
    {
    position: relative;
    opacity: 0;
    display: inline-block;
    }
    
    .mark-hightlight
    {
    box-sizing: border-box;
    display: block;
    color: red;
    background-color: #cccccc;
    z-index: 1;
    position: absolute;
    bottom: 0%;
    top: -0;
    left: 0%;
    right: 0%;
    transform: translateY(-10%);
    }
    <!DOCTYPE html>
    <html lang="">
    
    <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>example</title>
      <link href='https://fonts.googleapis.com/css?family=Open Sans' rel='stylesheet'>
    </head>
    
    <body>
    
    
    <button onclick="
    const collection = document.getElementsByClassName('mark1');
    
    for (let i = 0; i < collection.length; i++) {
    collection[i].classList.toggle('hidden');
    }
    ">toggle version 1</button>
    
    <div class="section">
    <div class="paragraph paragraph1">
    Lorem Ipsum is<mark class="mark1"> <span class="mark-placeholder">simply dummy text of</span><span class="mark-hightlight">simply dummy text of</span></mark> the printing and typesetting industry. has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
    </div>
    </div>
    
    
    
    <button onclick="
    const collection = document.getElementsByClassName('mark2');
    
    for (let i = 0; i < collection.length; i++) {
    collection[i].classList.toggle('hidden');
    }
    
    ">toggle version 2</button>
    
    <div class="section">
    <div class="paragraph paragraph2">
    Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the<mark class="mark2"> <span>industry's standard dummy text ever since the 1500s</span></mark>, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
    </div>
    </div>
    
    </body>
    
    </html>
    Login or Signup to reply.
  2. This code allows you to dynamically input any text, then specify the highlighted parts, and afterwards you can copy it or choose to hide or display it as you wish.

    I tried to write comments for the codes.

    function updateWordList() {
      let text = document.getElementById('inputText').value;
      let words = text.split(/s+/);
      let wordListDiv = document.getElementById('wordList');
      wordListDiv.innerHTML = '';
    
        // Create a checkbox for each word
      words.forEach((word, index) => {
        if (word) {
          let checkbox = document.createElement('input');
          checkbox.type = 'checkbox';
          checkbox.id = `word-${index}`;
          checkbox.value = word;
          checkbox.onchange = updateHighlight;
    
          let label = document.createElement('label');
          label.htmlFor = `word-${index}`;
          label.innerText = word;
    
          wordListDiv.appendChild(checkbox);
          wordListDiv.appendChild(label);
          wordListDiv.appendChild(document.createElement('br'));
        }
      });
    
    
      document.getElementById('resultText').innerText = text;
    }
    
    
    /**
     * Updates the highlighted text based on the selected words from the word list.
     * This function checks each word's checkbox, and if checked, highlights that word in the output.
     * 
     * @function updateHighlight
     */
    function updateHighlight() {
      let text = document.getElementById('inputText').value;
      let words = text.split(/s+/);
    
      let highlightedText = words.map((word, index) => {
        let checkbox = document.getElementById(`word-${index}`);
        if (checkbox && checkbox.checked) {
          return `<span class="highlight" data-index="${index}">${word}</span>`;
        } else {
          return word;
        }
      }).join(' ');
    
      document.getElementById('resultText').innerHTML = highlightedText;
    }
    
    
    /**
     * Copies the currently displayed text (with or without highlights) to the clipboard.
     * It extracts the plain text from the result, creates a temporary textarea,
     * copies the content, and then removes the temporary element.
     * 
     * @function copyHighlightedText
     */
    
    function copyHighlightedText() {
      let resultDiv = document.getElementById('resultText');
      let tempTextarea = document.createElement('textarea');
      tempTextarea.value = resultDiv.innerText;
      document.body.appendChild(tempTextarea);
      tempTextarea.select();
      document.execCommand('copy');
      document.body.removeChild(tempTextarea);
      alert('متن کپی شد!');
    }
    
    
    /**
     * Toggles the visibility of the highlighted words in the result text.
     * When the words are hidden, they are removed from the display but stored in memory,
     * and can be restored upon re-toggling.
     * 
     * @function toggleHighlights
     */
    
    
    let highlightsVisible = true;
    let hiddenWords = [];
    
    function toggleHighlights() {
      let resultDiv = document.getElementById('resultText');
      if (highlightsVisible) {
        let highlightedSpans = resultDiv.querySelectorAll('.highlight');
        hiddenWords = [];
        highlightedSpans.forEach(span => {
          let wordIndex = span.getAttribute('data-index');
          hiddenWords.push({ index: wordIndex, word: span.innerText });
          span.classList.add('hidden-word');
        });
        highlightsVisible = false;
      } else {
        hiddenWords.forEach(hidden => {
          let span = resultDiv.querySelector(`.highlight[data-index="${hidden.index}"]`);
          if (span) {
            span.classList.remove('hidden-word');
          }
        });
        highlightsVisible = true;
      }
    }
    .highlight {
        background-color: rgb(255, 238, 0);
      }
    
      .hidden-word {
        display: none;
      }
    
      #inputText {
        width: 36%;
        border-radius: 6px;
        height: 150px;
      }
    
      #copy {
        width: 26%;
        height: 35px;
        background-color: rgb(111, 111, 253);
        color: white;
        border-radius: 5px;
        outline: none;
        border: none;
      }
    
      #copy:hover {
        background-color: rgb(77, 77, 252);
        cursor: pointer;
      }
    
      #showHide {
        width: 10%;
        height: 35px;
        background-color: rgb(13, 189, 0);
        color: white;
        border-radius: 3px;
        outline: none;
        border: none;
      }
    
      #showHide:hover {
        background-color: rgb(2, 148, 0);
        cursor: pointer;
      }
    <!DOCTYPE html>
    <html lang="fa">
    
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Dynamic Highlight with Word Selection</title>
      <link rel="stylesheet" href="style.css">
    </head>
    
    <body>
    
      <h1>Dynamic word highlight</h1>
    
      <textarea id="inputText" rows="5" cols="50" placeholder="Enter your text ..." oninput="updateWordList()"></textarea>
      <br><br>
    
      <div id="wordList"></div>
      <br><br>
      <!-- buttons -->
      <button id="copy" onclick="copyHighlightedText()">COPY Highlight Words</button>
      <button id="showHide" onclick="toggleHighlights()">Show / hide words</button>
    
      <!-- SHOW RESULT -->
      <div id="resultText" style="margin-top: 20px; white-space: pre-wrap;"></div>
    
      <!-- linked js -->
      <script src="script-MSI.js"></script>
    
    </body>
    
    </html>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search