One of the most misunderstood thing about innerHTML stems from the way the API is designed. It overloads the + and = operators to perform DOM insertion. This tricks programmers into thinking that it is merely doing string operations when in fact innerHTML behaves more like a function rather than a variable. It would be less confusing to people if innerHTML was designed like this:
element.innerHTML('some html here');
unfortunately it's too late to change the API so we must instead understand that it is really an API instead of merely an attribute/variable.
When you modify innerHTML it triggers a call to the browser's HTML compiler. It's the same compiler that compiles your html file/document. There's nothing special about the HTML compiler that innerHTML calls. Therefore, whatever you can do to a html file you can pass to innerHTML (the one exception being that embedded javascript don't get executed - probably for security reasons).
This makes sense from the point of view of a browser developer. Why include two separate HTML compilers in the browser? Especially considering the fact that HTML compilers are huge, complex beasts.
The down side to this is that incomplete HTML will be handled the same way it is handled for html documents. In the case of elements not inside a table most browsers will simply strip it away (as you've observed for yourself). That is essentially what you're trying to do - create invalid/incomplete HTML.
The solution is to provide innerHTML with complete HTML:
var htmlString = "<table border=1> <tr> <td> User Id </td> <td> Question </td> <td> Link Question </td> </tr>";
for (var i = 0; i < 25; i++) {
htmlString += "<tr>"
htmlString += "<td>" + jsonObj[i]["user_id"] + "</td>";
htmlString += "<td>" + jsonObj[i]["text"] + "</td>";
htmlString += "</tr>"
}
htmlString += "</table>"
myResponse.innerHTML += htmlString;