0

Consider the following:

I have a contenteditable div:

<div id="contentEditableDiv" contenteditable="true">
    <p id="content0e">Lorem ipsum...</p>
    <p id="content1e">Lorem ipsum...</p>
</div>

I add a MutationObserver to observe DOM changes in this div in JavaScript:

var mutationObserver = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
        manipulateAddedContent(mutation);
    });
});

mutationObserver.observe(jQuery("#contentEditableDiv")[0], 
    { attributes: true, childList: true, characterData: true });

After solving this issue I want to manipulateAddedContent. Yet the function is declared like this:

function manipulateAddedContent(mutation){
    jqMutation = jQuery(mutation);
    if(jqMutation.prop("addedNodes").length > 0){
        for (i = 0; i < jqMutation.prop("addedNodes").length; i++) {
            console.log(mutation.addedNodes[i]);
        }
    }
}

In the contenteditable div it is possible to add a new p DOM Element by pressing enter inside an existing p element, so the HTML looks like:

Inspector:
    <div id="contentEditableDiv" contenteditable="true" style="...">
        <p id="content0e">Lorem ipsum...</p>
        <p id="content1e">Lorem ipsum...</p>
        <p></p>
    </div>

But the MutationObserver records the already existing p element with id="content1e".

Well, with the anticipation that the MutationObserver won´t do it right, I am also happy if you could give me an alternative solution.

Below is a Stack Snippet. If you click with the cursor inside one of the existing p elements and press enter a new p element gets created. The MutationObserver records the already existing p element, but not the newly created one.

Stack Snippet:

var mutationObserver = new MutationObserver(function(mutations) {
 mutations.forEach(function(mutation) {
  manipulateAddedContent(mutation);
 });
});

mutationObserver.observe(jQuery("#contentEditableDiv")[0], 
  { attributes: true, childList: true, characterData: true });

function manipulateAddedContent(mutation){
 jqMutation = jQuery(mutation);
 if(jqMutation.prop("addedNodes").length > 0){
  for (i = 0; i < jqMutation.prop("addedNodes").length; i++) {
      console.log(mutation.addedNodes[i]);
  }
 }
}
 #contentEditableDiv {
  border: 1px solid black;
  min-height: 200px;
  width: 200px;
 }

 #contentEditableDiv p{
  border: 1px solid red;
  margin-left: 5px;
  margin-right: 5px;
 }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>

<div id="contentEditableDiv" contenteditable="true" >
  <p id="content0">Lorem ipsum...</p>
  <p id="content1">Lorem ipsum...</p>
</div>

So why does the MutationObserver records the wrong Element?

How can I fix it? => How get the newly added p element (without id)?

goulashsoup
  • 2,639
  • 2
  • 34
  • 60
  • 1
    The manipulateAddedContent it is only logging the 1st node observed (mutation.addedNodes[0]). Could it be that it's just not logging all the affected nodes? – raphael75 Oct 07 '16 at 19:37
  • No I add a change in the question. The `for` loop only gives me the `p` with `id="content1e"`. The new `p` element gets recorded after I add a second one. – goulashsoup Oct 07 '16 at 19:39
  • 2
    Could you pull the code together into a single [mcve]? Maybe use a [Stack Snippet](http://meta.stackoverflow.com/q/269753/215552) or jsfiddle.net or something? That would make it a lot easier to follow along. – Heretic Monkey Oct 07 '16 at 21:04
  • Thanks for that tip. I compressed the question and deleted the unrelevant content loading part. I really don´t know why I decided it would be important at first. – goulashsoup Oct 08 '16 at 17:25

2 Answers2

0

One solution is quite simple. I just let me get the next DOM element of the returned one:

var mutationObserver = new MutationObserver(function(mutations) {
 mutations.forEach(function(mutation) {
  manipulateAddedContent(mutation);
 });
});

mutationObserver.observe(jQuery("#contentEditableDiv")[0], 
  { attributes: true, childList: true, characterData: true });

function manipulateAddedContent(mutation){
 jqMutation = jQuery(mutation);
 if(jqMutation.prop("addedNodes").length > 0){
  for (i = 0; i < jqMutation.prop("addedNodes").length; i++) {
      if(typeof jQuery(mutation.addedNodes[i]).next()[0] !== "undefined"){
       console.log(jQuery(mutation.addedNodes[i]).next()[0]);
      }
  }
 }
}
 #contentEditableDiv {
  border: 1px solid black;
  min-height: 200px;
  width: 200px;
 }

 #contentEditableDiv p{
  border: 1px solid red;
  margin-left: 5px;
  margin-right: 5px;
 }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>

<div id="contentEditableDiv" contenteditable="true" >
  <p id="content0">Lorem ipsum...</p>
  <p id="content1">Lorem ipsum...</p>
</div>

But I will not accept my answer cause I still do not know WHY the mutationObserver returns the wrong element.

goulashsoup
  • 2,639
  • 2
  • 34
  • 60
0

This has nothing to do with MutationObserver, it's not returning the wrong element. When you press enter inside any element within a contenteditable container it gets duplicated, which is why the element reported by MutationObserver has the same id. If you try to locate the element through DevTools, it will show you the most recently added one - because it has the same id.

#uniqueOrIsIt {
 border: 1px solid gold;
}
.paragraph {
 border: 1px solid blue;
}
<div contenteditable="true">
  <p id="uniqueOrIsIt">text</p>
  <p class="paragraph">text</p>
  <p style="border: 1px solid green;color:green">text</p>
</div>

All three of the p elements will get duplicated with the exact same attributes.

The solution depends on what you're trying to accomplish - do you want each inserted p element to have a new unique id? In that case you can, for each mutation, check if any of the addedNodes are p elements and if they are, assign them a unique id. Keep a simple let counter = 1 and set the ids as addedNode.setAttribute('id', ('content' + counter++))

szydlovski
  • 800
  • 3
  • 10