3

Is it possible to transclude one HTML element into multiple locations of a document, as it is in MediaWiki? I want to include elements inside other elements, without copying and pasting their content. I know that it's possible to embed web pages inside other web pages using iframes, but is there any reliable way to embed HTML elements inside other HTML elements, on the same page?

<p id = "p1">This is paragraph 1. </p>
<p id = "p2">
    This is paragraph 2.
    </p>
<p id = "p3">This is paragraph 3. It should contain paragraphs 1 and 2.
    <!-- {{p1}} {{p2}} -->
</p>
Anderson Green
  • 30,230
  • 67
  • 195
  • 328

3 Answers3

2

This is a bit of a hack, but it works in Firefox, Chrome, and Opera. Not tested in IE, don't have it on this laptop... let me know if it works in IE if you get a chance to test.

Put this in the document's head, in a script tag:

function transclude(elementId) {
    var clone = document.getElementById(elementId).cloneNode(true),
        placeholder;
    clone.id = null;
    document.write('<br id="__placeholder__">');
    placeholder = document.getElementById('__placeholder__');
    placeholder.parentNode.insertBefore(clone, placeholder);
    placeholder.parentNode.removeChild(placeholder);
    return transclude;
}

To transclude elements, use this:

<p id="p1">This is paragraph 1. </p>
<p id="p2">
    This is paragraph 2.
</p>
<p id="p3">This is paragraph 3. It should contain paragraphs 1 and 2.
    <script>transclude("p1")("p2")</script>
</p>

Notes:

  • ID attribute is removed from cloned elements.

  • Elements are transcluded as soon as the script containing the call to transclude runs, no waiting for the document to load. Because of the use of document.write, this will not work after the document has been loaded.

  • We use a dummy placeholder element, a <br>, to prevent a side effect of document.write, where writing for example a <p> after a <p> that has been opened but not terminated causes the first tag to terminate prematurely.

    In other words, the tag name of the placeholder element should be different from the names of any unterminated outer tags, thus the self-terminating <br> tag.

  • Transclusion function returns itself for chaining.

http://jsfiddle.net/bcWjE/1

Dagg Nabbit
  • 75,346
  • 19
  • 113
  • 141
  • Note that this was a reasonable answer in 2013, but in 2020 (and on) you should never use `document.write`. In this case, you'd use some JS that you load with ` – Mike 'Pomax' Kamermans Aug 09 '20 at 17:48
  • @Mike'Pomax'Kamermans The reason for using `async defer` is to make the page load faster. The reason for avoiding `document.write` is because it _can_ cause reparsing to occur if partial tags are written (and it also holds up the page, waiting on JavaScript to run). This code has neither of those issues. In fact, your solution causes the layout to jump around, and the content to change, as a result of asynchronous network requests. Avoid `document.write` if you like, but `async defer` is _definitely_ incorrect here. (I'd even argue holding up the page is correct behaviour.) – wizzwizz4 Jan 28 '22 at 19:42
  • it's 2022: gracefully load them in, or generate your content server-side so you don't even need JS to transclude content. Holding up the page hasn't been the right thing for years. – Mike 'Pomax' Kamermans Jan 28 '22 at 21:13
1

Using jQuery:

$(document).ready(function(){
  $('#p3').html($('#p1').html()+$('#p2').html())
});

JsFiddle

mutil
  • 3,205
  • 1
  • 27
  • 34
  • 1
    This creates two elements having the same `id` attribute. Also I don't believe the OP mentioned jQuery anywhere. – Dagg Nabbit Mar 23 '13 at 04:03
0

It may be more appropriate to use jQuery's .clone() method, which performs a deep copy of a DOM node, perserving things like bound methods.

Trivial example (from the docs) applying $('.hello').clone().appendTo('.goodbye'); to

<div class="container">
  <div class="hello">Hello</div>
  <div class="goodbye">Goodbye</div>
</div>

results in

<div class="container">
  <div class="hello">Hello</div>
  <div class="goodbye">
    Goodbye
    <div class="hello">Hello</div>
  </div>
</div>
msanford
  • 11,803
  • 11
  • 66
  • 93
  • I can live with downvotes, but *add a comment*: downvoting without commenting serves no useful function to the answerer or the OP. – msanford Nov 04 '13 at 16:59