Gilson PJ
Gilson PJ

Reputation: 3600

Inserting tags in between html content

I have the sample below content in mydiv

<div id="mydiv">
   sample text sample text sample text...
   ......
   <i>inner text </i> <i>sample text </i>
   ......
   <b>sample text </b> <i>sample text </i>
</div>

Now I want to append highlighting div in between the mydiv content. sample is given below.

<div class="highlight">highlight text</div>

I want to insert this div in every 200 words, but the problem is it should not goes inside any of the children tags. for example in case 200th word is inner it should not append like

<div id="mydiv">
   sample text sample text sample text...
   ......
   <i>inner<div class="highlight">highlight text</div> text </i> <i>sample text </i>
   ......
   <b>sample text </b> <i>sample text </i>
</div>

it should append after the inner tags

<div id="mydiv">
   sample text sample text sample text...
   ......
   <i>inner text </i> <div class="highlight">highlight text</div> <i>sample text </i>
   ......
   <b>sample text </b> <i>sample text </i>
</div>

I tried with substring but it goes inside the child tags. Is there any way to achieve this? We can use any js libraries.

Upvotes: 10

Views: 4414

Answers (9)

Nadir Laskar
Nadir Laskar

Reputation: 4160

UPDATED The div that I will be adding after every 2nd word.

var some_div = '<div style="display:inline-block;color:red;">some_text</div>';

var text = $('#mydiv').text().match(/\w+/g);

Secondly, traverse through all the words and prefixed these words in the html of the div with a unique identifier text.

Here, I am add a string <index>$$ where <index> increments on each traversal.

var i = 1;
var count = 1;
var html = $('#mydiv').html();

text.forEach(function(word, index) {

  var offset = html.indexOf(word);
  while (html[offset - 1] == '$' && html[offset - 2] == '$') {
    offset = html.indexOf(word, offset + 1);
  }

  if ((count % up_index) == 0) {
    html = html.slice(0, offset) + (i++) + '$$' + html.slice(offset)
    $('#mydiv').html(html);
  }

  count++;
});

Finally, loop through all the unique tokens and replace them with your html.

to find tokens use $('#mydiv').find(':contains(' + j + '$$)'); of jquery.

for (var j = 1; j < i; j++) {
  var elm = $('#mydiv').find(':contains(' + j + '$$)');
  if (elm.length == 0) {
    console.log('inroot>>' + ':contains(' + j + '$$)');
    var offset = $(':contains(' + j + '$$)').last().html().indexOf(j + '$$');
    var t_html = $(':contains(' + j + '$$)').last().html().slice(0, (offset + (("" + j + '$$').length))).replace(/[0-9]\$\$/ig, '');
    t_html += some_div;
    t_html += $(':contains(' + j + '$$)').last().html().slice(offset + (("" + j + '$$').length));
    $('#mydiv').html(t_html);

  } else {
    console.log('not inroot>>' + ':contains(' + j + '$$)');
    $(some_div).insertAfter(elm.last());
  }
}

Here is an Example where I have added div after every 2nd word

Firstly, I am fetching all the words inside the interested container as follows,

var some_div = '<div style="display:inline-block;color:red;">some text</div>';

var up_index = 2; // Word index that will be updated every 2nd word.

var text = $('#mydiv').text().match(/\w+/g);

var i = 1;
var count = 1;
var html = $('#mydiv').html();

text.forEach(function(word, index) {

  var offset = html.indexOf(word);
  while (html[offset - 1] == '$' && html[offset - 2] == '$') {
    offset = html.indexOf(word, offset + 1);
  }

  if ((count % up_index) == 0) {
    html = html.slice(0, offset) + (i++) + '$$' + html.slice(offset)
    $('#mydiv').html(html);
  }

  count++;
});

for (var j = 1; j < i; j++) {
  var elm = $('#mydiv').find(':contains(' + j + '$$)');
  if (elm.length == 0) {
    console.log('inroot>>' + ':contains(' + j + '$$)');
    var offset = $(':contains(' + j + '$$)').last().html().indexOf(j + '$$');
    var t_html = $(':contains(' + j + '$$)').last().html().slice(0, (offset + (("" + j + '$$').length))).replace(/[0-9]\$\$/ig, '');
    t_html += some_div;
    t_html += $(':contains(' + j + '$$)').last().html().slice(offset + (("" + j + '$$').length));
    $('#mydiv').html(t_html);

  } else {
    console.log('not inroot>>' + ':contains(' + j + '$$)');
    $(some_div).insertAfter(elm.last());
  }
}

$('#mydiv').html($('#mydiv').html().replace(/[0-9]\$\$/ig, ''));
.highlight {
  color: red;
  display: inline-block;
}
b {
  background-color: blue;
}
i {
  background-color: yellow;
}
i,
b {
  border: 1px solid green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="mydiv">
  sample text
  <b><a>sample text</a></b>  <i>sample text </i>
  ......
  <i>inner text </i>  <i>sample text </i>
  ......
  <b>sample text </b>  <i>sample text </i>
</div>

Upvotes: 3

M0nst3R
M0nst3R

Reputation: 5303

The easiest way of doing this is to loop through the content of your div and insert the highlight in the right spot. Here is the code :

$(document).ready(function () {
    // count of characters (modulo the period) and selected period
    var count = 0, period = 200;

    // iterator and highlight
    var words = '', highlight = '<div class="highlight">highlight text</div>';

    // loop through the contents
    $('#mydiv').contents().each(function () {
        // we only care about text contents
        if (this.nodeType === 3) {
            // get the individual words
            words = $(this).text().split(' ');
            // loop through them
            for (var j = 0; j < words.length; j++) {
                // increase count except if the word is empty (mutiple spaces)
                if (words[j] && words[j] !== '\n') { count++; }
                // if the period is exceeded, add the highlight and reset the count
                if (count === period) {
                    words.splice(1 + j++, 0, highlight);
                    count = 0;
                }
            }
            // replace the text
            $(this).replaceWith(words.join(' '));
        }
    });
});

Upvotes: 7

Tomas Langkaas
Tomas Langkaas

Reputation: 4731

Provided that you are ok with manipulating the HTML of #mydiv as a string, one solution is to use string.replace with a regular expression and a replacement function, such as:

function insertInHTMLEveryNWords(n, htmlString, insertString) {
  var wordCounter = 0,
      tagDepth = 0;
  return htmlString.replace(
    /<(\/?)([^>]+)>|[^<\s]+/g,
    function(match, forwardSlash, tagContent) {
      if (tagContent) { //matched a tag
        tagDepth += forwardSlash ? -1 : 1;
      } else { //matched a word
        wordCounter++;
      }
      if (!tagDepth && wordCounter >= n) {
        //if not inside tag and words are at least n,
        wordCounter = 0;
        return match + insertString;
      } else {
        return match;
      }
    });
}

The following snippet contains a working demo of the approach, simplified to insertion after every 2 words (if not in any tag, or directly after the next possible closing tag, as stated by the OP):

var htmlOriginal = document.getElementById('mydiv').innerHTML;

var htmlWithInsertions = insertInHTMLEveryNWords(
  2,
  htmlOriginal,
  '<div>[Inserted DIV after every 2 words or when not in any tag]</div>'
);

//inspect result in console
console.log(htmlWithInsertions);

//replace html in #mydiv
document.getElementById('mydiv').innerHTML = htmlWithInsertions;

function insertInHTMLEveryNWords(n, htmlString, insertString) {
  var wordCounter = 0,
      tagDepth = 0;
  return htmlString.replace(
    /<(\/?)([^>]+)>|[^<\s]+/g,
    function(match, forwardSlash, tagContent) {
      if (tagContent) { //matched a tag
        tagDepth += forwardSlash ? -1 : 1;
      } else { //matched a word
        wordCounter++;
      }
      if (!tagDepth && wordCounter >= n) {
        //if not inside tag and words are at least n,
        wordCounter = 0;
        return match + insertString;
      } else {
        return match;
      }
    });
}
<div id="mydiv">
  Some text, word3,
  <em>emphasized and <strong>strong</strong></em> text.
</div>

A potential limitation is that manipulating the HTML replaces the existing DOM elements in #mydiv and breaks any previous references to them. If that does not pose any problem, this is a straightforward working solution.

Upvotes: 3

Doozerman
Doozerman

Reputation: 235

Here's a code that will give you your desired result. The advantage of this code is that it doesn't check every element whether it's a tag or not by checking if it contains < or </, which in my opinion is good, as our code is not as complicated as it would be, and we don't need to check through all values, whether or not they contain < or </. (Less calculation, should in theory run faster.)

var elements = $("#mydiv").contents(); //Get all contents of #mydiv
$("#mydiv").html(null); //Delete all elements from #mydiv

var count = 0; //This will be counting our elements

for(var i = 0; i < elements.length; i++){ //Go through all elements of #mydiv
    if(elements[i].nodeName == "#text"){ //If the element is a text block, then:

        var textElements = $(elements[i]).text().split(/\s+/g); //Split the text by all the spaces

        for(var j = 0; j < textElements.length; j++){ //Go through all elements inside the text block

            if(textElements[j] == "") continue; //The splitting of the text-block elements is not perfect, so we have to skip some elements manually

            count++; //Add to our count

            $("#mydiv").append(textElements[j] + " "); //Add this element to our cleared #mydiv

            if(count != 0 && count % 200 == 0){ //Every 200 elements, do this:
                $("#mydiv").append(" <span class='highlight'>highlight</span> "); //Add our highlight
            }
        }
    }else{ //If the element is not a text block, then:

        $("#mydiv").append(elements[i]); //Add the non-text element

        count++; //Add to our counter

        if(count != 0 && count % 200 == 0){ //Every 200 elements, do this:
            $("#mydiv").append(" <span class='highlight'>highlight</span> "); //Add our highlight

        }
    }
}

Upvotes: 3

Henz
Henz

Reputation: 121

In any way, I hope this helps.

NOTE: HTML used is given sample content.
Try splitting your div content and work from that. See comments for explanations:

        //1.Get mydiv content
        //2. Split spaces and newlines
        //3. Remove empty array values
        var div =     $("#mydiv").html().toString().split(/[\s+\n]/).filter(String);

        var allowAdd = true, appendNow;
        for(var a=0; a < div.length ; a++){
            if(div[a].match(/^</) && div[a].match(/>$/)){ //search for end tags ie. </i>, </b>
                if(div[a].match(/<\//)){ //if end tag, 
                    allowAdd = true;    //allow append
                }
            }else if (div[a].match(/</)){ //if start stag,
                allowAdd = false;   //disallow append (inside block)

            }

            if((a+1)%200 == 0){
                //every 200 words
                appendNow = true;
            }

            //append if 200th word and end tag is passed
            if(appendNow && allowAdd){
                div[a] += ' <div class="highlight">highlight text </div> ';
                appendNow = false;
            }
        }

        //join array values and assign value to mydiv content
        $("#mydiv").html(div.join(" ")); 

Upvotes: 3

Arun CM
Arun CM

Reputation: 3435

You can use JQuery after or insertAfter to insert element after the target.

append method inserts the specified content as the last child of each element in the jQuery collection

$(function(){
    
  // your logic to find position goes here...
  
  // append  text after an element 
  $("#mydiv i:first-child").after("<div class='highlight'>highlight text</div>");
  
});
.highlight{
   color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="mydiv">
  sample1 text1 sample1 text1 sample1 text1sample1 tex1 sample1 text1 sample text1 sample2 text2 sample2 text sample2 textsample2 text2 sample2 text2 sample2 text2 sample3 text3 sample3 text3 sample3 textsample3 text3 sample3 text3 sample3 text3 sample
  3 text 3sample 3text sample3 textsample3 text4 sample 4text sample4 text

  <i>1inner text 1 </i>  <i>sample text 1</i>
  <i>2inner text 2</i>  <i>sample text2 </i>
  <i>3inner text 3</i>  <i>sample text 3</i>
  <i>4inner text 4</i>  <i>sample text4 </i>
  <i>5inner text 5</i>  <i>sample text5 </i>
  <i>6inner text 6</i>  <i>sample text6 </i>
  <i>7inner text 7</i>  <i>sample text 7</i>

  <b>8sample text 8</b>  <i>sample text 8</i>
  <b>9sample text 9</b>  <i>sample text 9</i>
  <b>10sample text 10</b>  <i>sample text10 </i>
  <b>11sample text 11</b>  <i>sample text 11</i>
  <b>12sample text 12</b>  <i>sample text 12</i>

</div>

Upvotes: 4

RamblinRose
RamblinRose

Reputation: 4963

You haven't given specifics on how you arrive at the text within the DOM (and I assume you're using the DOM). But given the text node containing the word of interest, something like this should do. I am using a minimal amount jQuery for convenience, it is hardly necessary.

// use jQuery to find the text node "inner text" from your example
let textNode = $("#mydiv i")
    .first()
    .contents()
    .filter(function () {
        return this.nodeType == 3; /* text node */
    }).get(0);

// find the parent element of the text node
let el = textNode;
while (el.parentNode) {
    if (el.nodeType == 1) break; /* element */
    el = el.parentNode;
}
// append the html after the parent of the text node.
 $(el).after(`<div class="highlight">highlight text</div>`);

You can see this in action at this plnkr.

Basically the code gets the text node of the Nth word of interest, finds it's parent element, then inserts the desired html as the first right sibling of the parent element.

Upvotes: 3

Kevin Chac&#243;n
Kevin Chac&#243;n

Reputation: 185

I assume you are already finding the word 200 and appending your div after it, something like this maybe?

varUntilSpace200 + '<div "highlight">highlight text</div>' + restOfInnerHTML;

Then all you need to do is check in restOfInnerHTML if after space 200 there is a

"</"

if so, just append everything from that position to the first

">"

var indof1 = restOfInnerHTML.indexOf("</");
var indof2 = restOfInnerHTML.indexOf("<");
var indof3 = restOfInnerHTML.indexOf(">");

if (indof1 < indof2) {
  varUntilSpace200 += restOfInnerHTML.substring(indof1,indof3);
  restOfInnerHTML = restOfInnerHTML.substring(indof3+1,restOfInnerHTML.length);
}

Upvotes: -1

Guillem LLuch Moll
Guillem LLuch Moll

Reputation: 1

The i tag is an inline element and it isn't possible to insert a block element, as div, inside it. And "highlight" is not a valid attribute for div.

To achieve the insertion, use a span instead of a div. And check which attribute are needed for your goals. The result will be (with id as attribute):

<div id="mydiv">
   sample text sample text sample text...
   ......
   <i>inner<span id="highlight">highlight text</span> text </i> <i>sample text </i>
   ......
   <b>sample text </b> <i>sample text </i>
</div>

Upvotes: -1

Related Questions