4

The customary <script type="text/template"> tag for client-side processing isn't sufficient for my purposes because it isn't nestable. So I'm trying to extend it to work around this limitation.

Basically I have some raw html with embedded images that I want to parse the content client-side using javascript. I like the script tag because images inside it are not loaded by the browser. But when the html contains script tags, they break my application. Here's how I'm trying to extend the tag:

    <script>
        var scriptData = document.registerElement('script-data', {
            prototype: HTMLScriptElement.prototype
        });
        captureStart();
    </script>

When executed, chrome throws the following error:

Uncaught NotSupportedError: Failed to execute 'registerElement' on 'Document': Registration failed for type 'script-data'. The prototype is already in-use as an interface prototype object. 

Update:

Disputing my intentions or suggesting alternative methods is fine, however I still want an answer to my initial question: how does one extend the script tag?

How I'm using this is actually more complicated. I'm basically using this to "capture" the whole body of my HTML document, so that I can manipulate things before they are displayed. It's unconventional, I know. But I'm exploring it as a type of academic study.

The benefits to this approach include:

  • The html validates
  • The code is light-weight, and I hope better supported than solutions like mobify.js
  • I am able to modify content before the browser loads image resources

The challenges to this approach include:

  • The body of the document can have 3rd party modules/widgets with script tags or html comments.
  • I can't escape inline script tags. They need to remain in-tact because browsers with javascript disabled will display the document normally.

My proposed solution to these problems:

  • Extending the prototype of the script tag would allow me to use a custom tag that I know will never appear in other components.

Here's a larger sample of the code:

<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>Script Test</title>
        <style>
            html, body {
                margin: 0;
                padding: 0;
            }
        </style>
        <script>document.write("<script type='text/html' id='text-html'>");</script>
    </head>
    <body>
        <img src="http://placehold.it/600x400" alt="" width="600" height="400"/>
    </body>
</html>
<!--</script>
<script>
    var content = document.getElementById("text-html");
    document.write(content.innerHTML);
    content.parentNode.removeChild(content);
</script>-->
10
  • 1
    Depending on the templateng you're using, you shouldn't need to literally nest templates. Just define your different templates and use them together Commented Jul 29, 2014 at 2:58
  • @Ian I don't think I'm nesting templates? There's just one template, with raw html inside. I have limited control over the content, so script tags could appear in some cases. Commented Jul 29, 2014 at 3:03
  • 1
    There’s the <template> element, but you’d probably be better off just escaping </script>. Commented Jul 29, 2014 at 3:04
  • 1
    Not being able to manipulate the raw content is a plain old security flaw, so please find some way to do that. Also, no, they’re not, but your browser probably doesn’t support it. Commented Jul 29, 2014 at 3:11
  • 2
    Oh, sorry. To answer your original question: one doesn’t extend the <script> tag. It’s a very special case. Anyways, what do you mean by “in some cases, it would actually be displayed”? Commented Jul 29, 2014 at 18:32

1 Answer 1

0

Short answer: Use Object.create() for the prototype object. The following code allows you to extend the script element, and represents the way document.registerElement() is meant to be used.

var scriptData = document.registerElement('script-data', {
    prototype: Object.create(HTMLScriptElement.prototype);
});

Long answer

Consider this variation on your original code:

var scriptDataProto = HTMLScriptElement.prototype;
// The next statement throws a DOMException of type "NotSupportedError".
var scriptData = document.registerElement('script-data', {
    prototype: scriptDataProto
});

The source of the exception is illustrated here:

scriptDataProto === HTMLScriptElement.prototype; // true

You're providing a reference to HTMLScriptElement.prototype, meaning that you want to use the exact same prototype for the custom script element you're creating. It's not very well documented yet, but the prototype you provide can't be a reference to any of the built-in HTML[*]Element prototypes; you have to provide a new prototype object.

If you want to inherit from the <script> tag's prototype, then you have to create a new prototype object that inherits from HTMLScriptElement.prototype by using Object.create().

var scriptDataProto = Object.create(HTMLScriptElement.prototype);
scriptDataProto === HTMLScriptElement.prototype; // false

If you'd like, you can then add new members to this prototype without affecting the base HTMLScriptElement.prototype.

scriptDataProto.foo = function() {
    alert('Hello world!');
};
var scriptData = document.registerElement('script-data', {
    prototype: scriptDataProto
});
var element = new scriptData();
element.foo(); // alerts "Hello world!"
document.body.appendChild(element);
element.onclick = element.foo;

For further reading, HTML5 Rocks has a well-written article that really helped me understand how custom elements work.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.