5
\$\begingroup\$

My goal is to work out which lines a piece of text is on within a set of paragraphs.

For example, in the image below:

enter image description here

I want to find text starting from "Maecenas non" to the next instance of "nisi" and return that the sentence starts on Line 3 and ends on Line 6.

My attempt is below, and it does the job, but I don't think it would be the most efficient way to do things.

function replacePunctuation(s) {
   return s.replace(/[^\w\s]/g, '');
}

let count = 0;
let rep = replacePunctuation;
let searchEndWords = [];

let passages = [
  {'start':'Lorem ipsum', 'end':'finibus,', 'lnStart':null,'lnEnd':null},
  {'start':'Proin tempus', 'end':'facilisis.', 'lnStart':null,'lnEnd':null},
  {'start':'Suspendisse pretium', 'end':'volutpat', 'lnStart':null,'lnEnd':null},
  {'start':'Nulla facilisi.', 'end':'luctus', 'lnStart':null,'lnEnd':null}
];

$("p").each(function(index) {
  let $el = $(this);
  let words = $el.text().split(' ');
  let previousHeight = 0;

  $el.text('');

  for(let j = 0; j < words.length; j++) {

    $el.text($el.text() + ' ' + words[j]);

    if(previousHeight < $el.height()) {

      count++;
      previousHeight = $el.height();

    } 

    for(let passage of passages) {

      let startWords = passage.start.split(' ');
      
      if(
        searchEndWords.includes(rep(words[j])) && 
        rep(passage.end) === rep(words[j])
      ) {
        passage.lnEnd = count;
      }

      if(
        rep(words[j]) === rep(startWords[0]) && 
        rep(words[j+1]) === rep(startWords[1])
      ) {

        searchEndWords.push(rep(passage.end));
        
        passage.lnStart = count;

      }
    }
  }
});

console.log(passages);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>Get lines</title>
  <script src="https://code.jquery.com/jquery-3.1.0.js"></script>
</head>
<body>
  <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed sed leo ultricies, rutrum ex id, tristique ex. Maecenas non diam vitae nisi egestas lacinia. Donec orci ex, scelerisque et enim finibus, dignissim ornare nisi. Curabitur accumsan magna ut interdum vehicula. Quisque eu feugiat leo. Nam imperdiet bibendum gravida. Proin tempus id urna et vulputate.</p>
  <p>Integer semper elit in dui porta, vitae pulvinar turpis facilisis. Suspendisse pretium tristique erat, eu consequat odio dignissim a. Cras placerat, dui sit amet tristique aliquet, enim velit feugiat arcu, ac ullamcorper diam diam nec ante. Integer facilisis elit vel eros congue accumsan quis vel nisl. Cras aliquet enim neque. Suspendisse urna odio, cursus at eros a, ultrices dapibus diam. Quisque convallis sollicitudin risus ac volutpat. Nulla facilisi. Vivamus elementum libero a quam eleifend interdum vitae tristique nisl. Vivamus gravida massa ex, ac mattis ligula malesuada quis. Vivamus fermentum diam eu ante dignissim eleifend. Nullam posuere in sapien in ornare.</p>
  <p>Ut sit amet dictum nisi. Pellentesque non pulvinar elit. Suspendisse eleifend arcu vitae nisi ultricies, ut sollicitudin eros interdum. Vivamus at quam viverra, posuere elit quis, semper sapien. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vivamus non velit eu nunc ullamcorper tristique eget ac ipsum. Vestibulum molestie faucibus luctus. Praesent egestas ipsum congue ipsum ullamcorper, et imperdiet purus cursus. Sed varius ante vitae sapien malesuada, vitae mollis sapien bibendum. Suspendisse nisl lectus, blandit a lectus vel, ornare cursus ex. Aliquam a fringilla magna, non imperdiet ex. Nullam sit amet blandit orci. Nunc sed leo consequat orci tincidunt porttitor vel ut orci. Vivamus volutpat ante eget tellus euismod finibus. Vestibulum sed maximus ex.</p>
</body>
</html>

Context

I'm working on an exam style application. There are certain questions that refer to lines of text in a passage of information (fiction or nonfiction).

Answers would be laid out like so:

  1. Lines 45-50 ("Lorem ipsum...nori")
  2. Lines 51-53 ("Integer semper... facilisis")
  3. Lines 59-61 ("Ut sit... elit")
  4. Lines 76-79 ("Suspendisse eleifend... interdum")

There is no way to know what lines a piece of text will be on until it has been rendered on the page. This code attempts to find the passage each answer refers to, post render, to work out which lines it spans.

\$\endgroup\$
  • 1
    \$\begingroup\$ It might be beneficial to give us a bit more context as to why you're implementing this. There might be simpler ways to achieve that goal. \$\endgroup\$ – Joseph May 8 '18 at 18:36
  • \$\begingroup\$ What you have here is a UX problem. As you said, until rendered, the lines won't be known. But if I resize the browser or stick the answers in a smaller element, those lines will change. What you need to solve is how to refer to the passages in the answers in a way that is not affected by the environment. A suggestion would be to use superscripts/subscripts on the text. \$\endgroup\$ – Joseph May 9 '18 at 14:08
  • \$\begingroup\$ @RichardHealy: We don't allow edits to code after the question has been answered, as this makes it hard to know exactly which version was reviewed (see this FAQ for details), and so I rolled back your edit. But if you want to post your own answer with the fixed code, that would be fine. \$\endgroup\$ – Gareth Rees May 10 '18 at 8:01
  • \$\begingroup\$ Thanks @GarethRees. I'll do that. Sorry for the inconvenience. \$\endgroup\$ – Richard Healy May 11 '18 at 11:36
2
\$\begingroup\$

First thing first, there's some bug. The example get the second finibus (tellus euismod finibus.), but should get the first (et enim finibus,), same for the volutpat. You also have 2 jquery reference.

Second, as Joseph stated in the comments, you'll be impacted by changes in the environment. If you stick to this you'll have to prevent some issues (here's resize doc for one).

Then to reduce code, i'll change the whole logic. Here's an example of another way to do it, but the JS way to deal with replace really annoyed me XP

It's not perfect in the slightest, I made it for fun and some may even call it dirty so everybody is free to improve on it ^^

  //credits : https://www.designcise.com/web/tutorial/how-to-replace-all-occurrences-of-a-word-in-a-javascript-string, added startSearch to skip the word re-added in replaceStr
  String.prototype.replaceAll = function(searchStr, replaceStr, startSearch) {
    let posMatch=this.indexOf(searchStr,startSearch);
    if(posMatch === -1) {
      return this; //no match exists in string? return string
    }
    // replace and remove first match, and do another recursirve search/replace starting from the end of last replacement
    startSearch=posMatch+replaceStr.length;
    return (this.replace(searchStr, replaceStr)).replaceAll(searchStr, replaceStr,startSearch);
  }
  
  function getLine(word, status){
    $('p').html(function() {return this.innerHTML.replaceAll(word,'<span class="'+status+'">'+word+'</span>',0)});
    let myFirst=$('.start').first();
    if(status=='end'){
      let followingSiblings=myFirst.nextAll('.end');
      if(followingSiblings.length>0)myFirst=followingSiblings.first();
      else myFirst=myFirst.closest('p').nextAll('p').find('.end').first();
    }
    let line=Math.ceil(myFirst.position().top/18);//our line-height
    line-=myFirst.closest('p').index('p');//to take into account auto margin from p, adapt as needed
    return line;
  }
  
  let passages = [
    {'start':'Lorem ipsum', 'end':'finibus,', 'lnStart':null,'lnEnd':null},
    {'start':'Proin tempus', 'end':'facilisis.', 'lnStart':null,'lnEnd':null},
    {'start':'Suspendisse pretium', 'end':'volutpat', 'lnStart':null,'lnEnd':null},
    {'start':'Nulla facilisi.', 'end':'luctus', 'lnStart':null,'lnEnd':null}
  ];
  
  $(passages).each(function(index) {
    passages[index]['lnStart']=getLine(this.start, 'start');
    passages[index]['lnEnd']=getLine(this.end, 'end');
    $('p').html(function() {return this.textContent.replaceAll('<span class="start">','',0).replaceAll('<span class="end">','',0).replaceAll('</span>','',0)});
  });
  console.log(passages);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>Get lines</title>
  <style>
    p{
      line-height:18px;
    }
  </style>
</head>
<body>
  <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed sed leo ultricies, rutrum ex id, tristique ex. Maecenas non diam vitae nisi egestas lacinia. Donec orci ex, scelerisque et enim finibus, dignissim ornare nisi. Curabitur accumsan magna ut interdum vehicula. Quisque eu feugiat leo. Nam imperdiet bibendum gravida. Proin tempus id urna et vulputate.</p>
  <p>Integer semper elit in dui porta, vitae pulvinar turpis facilisis. Suspendisse pretium tristique erat, eu consequat odio dignissim a. Cras placerat, dui sit amet tristique aliquet, enim velit feugiat arcu, ac ullamcorper diam diam nec ante. Integer facilisis elit vel eros congue accumsan quis vel nisl. Cras aliquet enim neque. Suspendisse urna odio, cursus at eros a, ultrices dapibus diam. Quisque convallis sollicitudin risus ac volutpat. Nulla facilisi. Vivamus elementum libero a quam eleifend interdum vitae tristique nisl. Vivamus gravida massa ex, ac mattis ligula malesuada quis. Vivamus fermentum diam eu ante dignissim eleifend. Nullam posuere in sapien in ornare.</p>
  <p>Ut sit amet dictum nisi. Pellentesque non pulvinar elit. Suspendisse eleifend arcu vitae nisi ultricies, ut sollicitudin eros interdum. Vivamus at quam viverra, posuere elit quis, semper sapien. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vivamus non velit eu nunc ullamcorper tristique eget ac ipsum. Vestibulum molestie faucibus luctus. Praesent egestas ipsum congue ipsum ullamcorper, et imperdiet purus cursus. Sed varius ante vitae sapien malesuada, vitae mollis sapien bibendum. Suspendisse nisl lectus, blandit a lectus vel, ornare cursus ex. Aliquam a fringilla magna, non imperdiet ex. Nullam sit amet blandit orci. Nunc sed leo consequat orci tincidunt porttitor vel ut orci. Vivamus volutpat ante eget tellus euismod finibus. Vestibulum sed maximus ex.</p>
</body>
</html>

*I'm new to this so I'm sorry if I messed up somewhere due to some bad habits and I'm waiting for your guidance ^^

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.