0

I recently asked a question here, and received a great response (which I will shortly be accepting the most active answer of, barring better alternatives arise) but unfortunately it seems the of the two options suggested, neither will be compatible with Ajax (or any dynamically added content that includes such "inline-relative jQuery")

Anyways, my question pertains to good ole' document.write().

While a page is still rendering, it works great; not so much when an appended snippet contains it. Are there any alternatives that won't destroy the existing page content, yet still append a string inline, as in where the call is occurring?

In other words, is there a way/alternative to document.write() that when called post-render, doesn't destroy existing page content? An Ajax friendly version so to speak?


This is where I'm going:

var _inline_relative_index = 0;
function $_inlineRelative(){
    // i hate non-dedicated string concatenation operators
    var inline_relative_id = ('_inline_relative_{index}').replace('{index}', (++_inline_relative_index).toString());
    document.write(('<br id="{id}" />').replace('{id}', inline_relative_id));
    return $(document.getElementById(inline_relative_id)).remove().prev('script');
}

And then:

<div>
    <script type="text/javascript">
        (function($script){
            // the container <div> background is now red.
            $script.parent().css({ 'background-color': '#f00' });
        })($_inlineRelative());
    </script>
</div>
Community
  • 1
  • 1
Dan Lugg
  • 20,192
  • 19
  • 110
  • 174
  • Where in the already-rendered document do you want it to appear? – ceejayoz Sep 22 '11 at 03:52
  • @ceejayoz - Immediately following the ` – Dan Lugg Sep 22 '11 at 03:53
  • Put an empty `
    ` right after the script tag and use jQuery's `appendTo` to add stuff to it.
    – ceejayoz Sep 22 '11 at 03:55
  • @ceejayoz - Unfortunately, that's not feasible. The script needs to be unaware of the existing DOM. This is why `document.write()` was so perfect, but I hadn't realized the Ajax caveat. – Dan Lugg Sep 22 '11 at 04:05
  • What code do you use to load scripts via Ajax? I'm pretty sure it adds the script as the last child node of `document.head`, so `$('head>script:last')` together with [readyState](http://stackoverflow.com/questions/6779515/can-you-select-the-script-element-that-included-the-javascript/6879371#6879371) should be the way to go. – user123444555621 Sep 22 '11 at 04:31

2 Answers2

4

you have access to the innerHTML property of each DOM node. If you set it straight out you might destroy elements, but if you append more HTML to it, it'll preserve the existing HTML.

document.body.innerHTML += '<div id="foo">bar baz</div>';

There are all sorts of nuances to the sledgehammer that is innerHTML, so I highly recommend using a library such as jQuery to normalize everything for you.

zzzzBov
  • 174,988
  • 54
  • 320
  • 367
  • Thanks @zzzzBov - I'm using jQuery to make an inline script (*an object contained therein*) aware of its position in the DOM. The issue is creating a "stub" inline to search by. I don't think that `innerHTML` will solve it, because that'll simply append to the complete document, rather than at the position of the ` – Dan Lugg Sep 22 '11 at 04:08
  • @Bracketworks, I read your previous question, and I'd asked the exact same thing recently. The currently executing script *must* be the last script in the dom unless it was added via a different script or deferred. – zzzzBov Sep 22 '11 at 04:11
  • Thats the caveat; "*unless it was added via a different script or deferred*", er go failing Ajax. By the way, great incentive too on your end; I briefed your question and answer. The inline "stub" would fix all the issues associated with appending dynamic content throughout the DOM, such as those that you discussed. For at least Ajax loaded HTML snippets (*containing inline scripts*) perhaps "sandboxing" the snippet, and somehow performing the appropriate stub insertion, prior to appending to the DOM; but that may release any object references in jQuery. – Dan Lugg Sep 22 '11 at 04:19
  • `document.write` doesn't add content to the document after the currently executing `script`, it appends content to the document. If a script were to be inserted into the `` from the context of the `body` and executed before the DOM was ready (while write was still open), it would still write to the `body` **not** immediately after itself (the `script` element) in the `head`. Unfortunately, your stub solution presents identical issues to the ones present by selecting the last script, with one extra catch: if you mistakenly call `document.write` after the doc is closed, you brick the page. – zzzzBov Sep 22 '11 at 04:25
  • There's always a catch, eh @zzzzBov? :P Had an interesting thought; `documentFragment` objects - do they observe the open/close methods that the complete `document` object does? Perhaps there's some trickery that can work there, or perhaps by `.cloneNode()` on the `document`. – Dan Lugg Sep 22 '11 at 05:22
  • @Bracketworks, on this particular issue I've tried to map out as many solutions as meticulously as possible and then see how I can make them fail. I will grant that the `document.write` call is a particularly clever solution that I hadn't thought of. The main failing point has been asynchronous insertion of `script` elements. There's no way to control where the script is inserted, and every "relative" insertion that I've been able to come up with appends to the end of the document, rather than immediately after the `script` that called the insertion. – zzzzBov Sep 22 '11 at 18:53
1

You can assign id to the script tag and replace it with the new node.

<p>Foo</p>
<script type="text/javascript" id="placeholder">
    var newElement = document.createElement('div');
    newElement.id='bar';
    var oldElement = document.getElementById('placeholder');
    oldElement.parentNode.replaceChild(newElement, oldElement);
</script>
<p>Baz</p>

And if you need to insert html from string, than you can do it like so:

var div = document.createElement('div');
div.innerHTML = '<div id="bar"></div>';
var placeholder = document.getElementById('placeholder'), 
    container = placeholder.parentNode,
    elems = div.childNodes, 
    el;
while (el = elems[0]) {
    div.removeChild(el);
    container.insertBefore(el, placeholder);
}
container.removeChild(placeholder);
Andrey M.
  • 3,688
  • 3
  • 33
  • 36