0

I'm writing a MUD client with HTML web-sockets where I'm receiving HTML from the MUD server. Essentially think of it as a chat-client.

When I receive data from the MUD server to be presented in a two column horizontal layout, it will only work if I receive the entire data at once, whereas if I receive it a line at a time, it will turn the nice two column format into a single column layout.

Sample code to reproduce the issue is here. I'm hoping there's some kind of solution to this which would not require assembling the entire string before adding it to innerHTML as shown in the third solution.

The incorrect formatting is the same in both FF and Chrome which makes me hope there's a solution to the challenge :-)

<html>
<body>
    <br/><hr/>
    <!-- *** Conversation Output *** -->
    <div id="static" role="log" aria-live="polite" width="100%">
        what it should look like: <br/>
        <table class='twocolhor'>
            <tr><td>&lt;wielded&gt;</td><td><div class='wielded'>a hand mace</div></td></tr>
            <tr><td>&lt;worn about body&gt;</td><td><div class='wielded'>white robes of a healer</div></td></tr>
            <tr><td>&lt;worn on feet&gt;</td><td><div class='wielded'>a pair of sandals</div></td></tr> 
        </table>
    </div>
    <br/><hr/>
    <div id="incomingMsgOutput2" role="log" aria-live="polite" width="100%">
        Sample, this will NOT work<br/>
    </div>
    <br/><hr/>
    <div id="incomingMsgOutput3" role="log" aria-live="polite" width="100%">
        Sample, this will work<br/>
    </div>


</body>
<script>

var obj = document.getElementById("incomingMsgOutput2");


obj.innerHTML += "<table class='twocolhor'>"
obj.innerHTML += "<tr><td>&lt;wielded&gt;</td><td><div class='wielded'>a hand mace</div></td></tr>"
obj.innerHTML += "<tr><td>&lt;worn about body&gt;</td><td><div class='wielded'>white robes of a healer</div></td></tr>"
obj.innerHTML += "<tr><td>&lt;worn on feet&gt;</td><td><div class='wielded'>a pair of sandals</div></td></tr>"
obj.innerHTML += "</table>"


var obj = document.getElementById("incomingMsgOutput3");

obj.innerHTML += "<table class='twocolhor'>" + "<tr><td>&lt;wielded&gt;</td><td><div class='wielded'>a hand mace</div></td></tr>" +
                 "<tr><td>&lt;worn about body&gt;</td><td><div class='wielded'>white robes of a healer</div></td></tr>" +
                 "<tr><td>&lt;worn on feet&gt;</td><td><div class='wielded'>a pair of sandals</div></td></tr>" +
                 "</table>"

</script>
</html>

2 Answers2

1

@Michael I have tried to run the code and seems like closes the table tag there itself and HTML looks like as below

<div id="incomingMsgOutput2" role="log" aria-live="polite" width="100%">
Sample, this will NOT work<br>
<table class="twocolhor"></table>&lt;wielded&gt;
<div class="wielded">a hand mace</div>
&lt;worn about body&gt;
<div class="wielded">white robes of a healer</div>
&lt;worn on feet&gt;
<div class="wielded">a pair of sandals</div></div>

This is happening because 'innerHtml' sets the HTML and hence expects a valid or properly formatted HTML. Since the tag is not closed (in the first line, when we are setting obj.innerHtml) so, it closes the tag to make it look like a valid Html and leads to incorrect behaviour in our case.

A better solution can be to create a properly formatted string and then assign it to 'innerHtml'.

You can find a better explanation in below link Javascript - .innerHTML changes auto close tags

I hope this helps and answers the question.

Hemant Kumar
  • 186
  • 6
  • Thank you for explaining what happens. Is there a way you know that would allow me to test if the string I'm about to add is well-formed? Then I could wait for enough data to arrive and only add the string once I have a fully well formed string. – Michael Seifert Apr 05 '20 at 15:00
  • We can make use of simple regex to test if a string is valid Html and keep on appending until valid Html is generated. https://stackoverflow.com/questions/15458876/check-if-a-string-is-html-or-not/15458987, can help here. – Hemant Kumar Apr 05 '20 at 15:09
1

You should insert all HTML tags at once when you use innerHTML. Anyway it renders all code after appending new line and clear of all bad uses of tags or opened ones.

You should use other methods, for example DOM modifier createElement than appending line by line.

Example:

function addElement(parentId, elementTag, elementId, html) {
    // Adds an element to the document
    var p = document.getElementById(parentId);
    var newElement = document.createElement(elementTag);
    newElement.setAttribute('id', elementId);
    newElement.innerHTML = html;
    p.appendChild(newElement);
}
Jsowa
  • 9,104
  • 5
  • 56
  • 60