Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Traversing: $.fn.contents() supports HTMLTemplateElement #3462

Merged
merged 8 commits into from Jan 29, 2017
Merged

Traversing: $.fn.contents() supports HTMLTemplateElement #3462

merged 8 commits into from Jan 29, 2017

Conversation

@TechQuery
Copy link
Contributor

@TechQuery TechQuery commented Dec 20, 2016

Summary

Add support to $.fn.contents() for HTMLTemplateElement.

Related with #3436

Checklist

@jsf-clabot
Copy link

@jsf-clabot jsf-clabot commented Dec 20, 2016

CLA assistant check
All committers have signed the CLA.

@mention-bot
Copy link

@mention-bot mention-bot commented Dec 20, 2016

@TechQuery, thanks for your PR! By analyzing the history of the files in this pull request, we identified @markelog, @timmywil and @jeresig to be potential reviewers.

@timmywil
Copy link
Member

@timmywil timmywil commented Dec 20, 2016

@TechQuery Thanks! This looks great. However, the form element has unique behavior in that form elements are attached as properties of the form. I could see content being an input name, so if elem were a form, elem.content.childNodes would not return what you expect. Make sense? In other words, I think a jQuery.nodeName check will guard us here.

@@ -143,7 +143,11 @@ jQuery.each( {
return siblings( elem.firstChild );
},
contents: function( elem ) {
return elem.contentDocument || jQuery.merge( [], elem.childNodes );
switch ( elem.nodeName.toLowerCase() ) {

This comment has been minimized.

@markelog

markelog Dec 23, 2016
Member

Just if..else, no need in swtich

@@ -291,6 +291,16 @@
<map name="imgmap" id="imgmap">
<area shape="rect" coords="0,0,200,50">
</map>

<template id="template">

This comment has been minimized.

@markelog

markelog Dec 23, 2016
Member

Please create that html in unit test and then append it

@@ -710,8 +710,8 @@ QUnit.test( "prevUntil([String])", function( assert ) {
} );

QUnit.test( "contents()", function( assert ) {
assert.expect( 12 );

This comment has been minimized.

@markelog

markelog Dec 23, 2016
Member

Please create separate test for this

QUnit.test( "contents() for <template />", function( assert ) {
assert.expect( 6 );

jQuery( "body" ).append(

This comment has been minimized.

@markelog

markelog Dec 27, 2016
Member

Not on body but on #qunit-fixture otherwise it will not be cleared

This comment has been minimized.

@TechQuery

TechQuery Dec 27, 2016
Author Contributor

If I append the HTML to #qunit-fixture, should I remove them after my testing?

This comment has been minimized.

@mgol

mgol Dec 27, 2016
Member

@TechQuery no, the point of #qunit-fixture is that it gets cleaned up automatically between test runs.

@markelog
Copy link
Member

@markelog markelog commented Dec 27, 2016

In which browsers did you test this?

@TechQuery
Copy link
Contributor Author

@TechQuery TechQuery commented Dec 27, 2016

@markelog

Chrome

  • 48.0.2564.23
  • 54.0.2840.59 m (64-bit)

Firefox

  • 50.1.0

Internet Explorer

  • 11.0.9600.18449
  • Document Mode 10 (IE 11 Debugger)
  • Document Mode 9 (IE 11 Debugger)
}

if ( jQuery.nodeName( elem, "template" ) ) {
elem = elem.content || elem;

This comment has been minimized.

@markelog

markelog Dec 27, 2016
Member

That's because of the IE right?

"</form>"
);

var c = jQuery( "#template" ).contents();

This comment has been minimized.

@markelog

markelog Dec 27, 2016
Member

Probably better to use more verbose variable name

iform.children().eq( 0 ).remove();
assert.equal( iform.contents().length, 3, "Check no conflict with Form shortcuts" );

iform.add( iform.prev() ).remove();

This comment has been minimized.

@markelog

markelog Dec 27, 2016
Member

For what?

jQuery( jQuery.map( c, function( node ) {
return document.importNode( node, true );
} ) )
).appendTo( document.body );

This comment has been minimized.

This comment has been minimized.

@TechQuery

TechQuery Dec 27, 2016
Author Contributor

$.fn.clone() doesn't support document.importNode() which child nodes of template needs (as MDN document said)...

This comment has been minimized.

@markelog

markelog Dec 27, 2016
Member

The same MDN page says importNode is not supported in IE 9 and we do support IE 9, soooo

This comment has been minimized.

@TechQuery

TechQuery Dec 27, 2016
Author Contributor

https://developer.mozilla.org/en-US/docs/Web/API/Document/importNode

This page says that document.importNode() is an "Initial definition" in DOM Level 2 (which IE 9 had implemented), only the second optional argument is in DOM Level 4.

This comment has been minimized.

@markelog

markelog Dec 27, 2016
Member

Ooh, I get it, ok

This comment has been minimized.

@markelog

markelog Dec 27, 2016
Member

You probably responding to https://github.com/jquery/jquery/pull/3462/files/#r93926975 comment though?

QUnit.test( "contents() for <template />", function( assert ) {
assert.expect( 6 );

jQuery( "#qunit-fixture" ).append(

This comment has been minimized.

@markelog

markelog Dec 27, 2016
Member

Sounds easier to create node, assign, append and use through the tests?

Like so –

var template = jQuery("<template id='template'>...")

Note the single quotes inside the string


assert.equal( c.find( "span" ).text(), "Hello, Web Component!", "Find span in Template and check its text" );

jQuery( "<div id=\"templateTest\" />" ).append(

This comment has been minimized.

@markelog

markelog Dec 27, 2016
Member

What we doing here?

This comment has been minimized.

@TechQuery

TechQuery Dec 27, 2016
Author Contributor

Test handling of nodes imported from the template.

var c = jQuery( "#template" ).contents();
assert.equal( c.length, 6, "Check template element contents" );

assert.equal( c.find( "span" ).text(), "Hello, Web Component!", "Find span in Template and check its text" );

This comment has been minimized.

@markelog

markelog Dec 27, 2016
Member

Sometimes you use uppercase and sometimes lowercase, I suggest to Template -> template, same for form

}

if ( jQuery.nodeName( elem, "template" ) ) {
elem = elem.content || elem;

This comment has been minimized.

@mgol

mgol Dec 27, 2016
Member

@TechQuery All places where a specific logic has to be applied because of deficiencies of a browser a support comment needs to be added like this one.

In this case, I'd expect something like that:

// Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only
// Treat the template element as a regular one in browsers that
// don't support it.

It's only necessary to mention browsers we still support; see https://jquery.com/browser-support/.

This comment has been minimized.

@markelog

markelog Dec 27, 2016
Member

@mgol I was planning to get to support comment realization, but there is also couple considerations with this code, so right now, I think we need to discuss this a bit further

assert.equal( contents.length, 6, "Check cloned nodes of template element contents" );

assert.equal( contents.filter( "div" ).length, 3, "Count cloned elements from template" );
jQuery( "#templateTest" ).remove();

This comment has been minimized.

@markelog

markelog Dec 28, 2016
Member

I suppose we no longer need that line?

"</template>"
);

var contents = jQuery( "#template" ).contents();

This comment has been minimized.

@markelog

markelog Dec 28, 2016
Member

That's a bit different what I had in mind, but I guess we can work with that

}

// Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only
// Treat the template element as a regular one in browsers that

This comment has been minimized.

@markelog

markelog Dec 28, 2016
Member

Total nitpick, but anyhow - only Treat -> only treat and no dot at the end

This comment has been minimized.

@TechQuery

TechQuery Dec 28, 2016
Author Contributor

Uh... My opinion of #3462 (comment) is that the first line is a title, and the rest lines are description...

(This is my first PR to jQuery such an international OS project, I'm still in the process of learning OS regulation & specification. I'm really sorry to bother you checking these details...)

This comment has been minimized.

@mgol

mgol Dec 28, 2016
Member

Uh... My opinion of #3462 (comment) is that the first line is a title, and the rest lines are description...

More a concrete information about affected browsers in a consistent format than a title. But yeah, the rest is just a description of what's going on there.

(This is my first PR to jQuery such an international OS project, I'm still in the process of learning OS regulation & specification. I'm really sorry to bother you checking these details...)

It's absolutely not a problem, ask away! :)

// Treat the template element as a regular one in browsers that
// don't support it.
if ( jQuery.nodeName( elem, "template" ) ) {
elem = elem.content || elem;

This comment has been minimized.

@markelog

markelog Dec 28, 2016
Member

I was wondering that if someone define content property on the node, then we would we be screwed, but I we probably don't need to support such case

This comment has been minimized.

@TechQuery

TechQuery Dec 28, 2016
Author Contributor

Before my PR, the code of $.fn.contents() didn't check the constructor of iframe.contentDocument property too...

This comment has been minimized.

@mgol

mgol Dec 29, 2016
Member

Yeah, I don't think it's necessary to worry about it at this point. If someone defines the content property on a template element in a non-standard way, they're asking for a problem.

@markelog
Copy link
Member

@markelog markelog commented Dec 28, 2016

Small fixes still needed, after that I will recheck browser tests and if no one else has any comments we can land this

@@ -743,6 +743,37 @@ QUnit.test( "contents()", function( assert ) {
assert.equal( c[ 0 ].nodeValue, "hi", "Check node,textnode,comment contents is just the one from span" );
} );

QUnit.test( "contents() for <template />", function( assert ) {

This comment has been minimized.

@mgol

mgol Dec 29, 2016
Member

Pasting from #3436 (comment):

Note that we have to be careful to not make the contents stop being inert once running .contents() on a template element wrapped in jQuery so we'll need a test that ensures this does not happen. For example, you could create a test with a <template> tag containing a script and <img> with an onload handler and make sure none of them fires.

This comment has been minimized.

@mgol

mgol Dec 29, 2016
Member

I'd prefer a separate test for inertness, btw; something like:

QUnit.test( "contents() for <template /> remains inert", function( assert ) {

This comment has been minimized.

@TechQuery

TechQuery Dec 29, 2016
Author Contributor

<div id="outerBox">
    <template>
        <img src="path/to/image.jpg" onload="alert('OK')" />
    </template>
</div>

In my opinion, event handler executes or not, it's depended on browser support of <template /> during parsing HTML code.

IE treats <template /> as an HTMLUnknownElement, so <img /> will be in the same document (fragment) of this template, then onload will be executed.

Otherwise, <img /> will be in a DocumentFragment, and onload won't be executed until $('#outerBox template').contents().appendTo('#outerBox').

This comment has been minimized.

@mgol

mgol Dec 29, 2016
Member

Yes, I know in older browsers the code will get executed; you should skip the inertness test in those browsers; see this test for an example how to do it.

By treating the contents of the template element not carefully you can lose the inertness; for example if you append template.content to the DOM. jQuery applies some manipulation steps in various APIs so we need tests that ensure that a simple $('template').contents() doesn't break the inertness by itself.

This comment has been minimized.

@mgol

mgol Dec 29, 2016
Member

In the tests in the onload for <img> and in the script tag just assign something to globals and test that the assignments didn't happen. You need to register the globals you set via Globals.register to get them cleaned up automatically at the end of the test, here is an example test that uses this pattern.

This comment has been minimized.

@mgol

mgol Dec 29, 2016
Member

As for the last test, it uses assert.ok( true, "Something is not supported in this browser" ) pattern that we used before QUnit.skip was available. Now I'd rather expect something like:

QUnit[ "content" in document.createElement( "template" ) ? "test" : "skip" ](
	"contents() for <template /> remains inert",
	function( assert ) {
		Globals.register( "testScript" );
		Globals.register( "testImgOnload" );
		/* the rest of the test */
	}
)();

Let me know if something is not clear.

This comment has been minimized.

@TechQuery

TechQuery Dec 29, 2016
Author Contributor

I confused about it is necessary to assert.ok() when I use QUnit.skip()? Or it's just a placeholder like this example?

This comment has been minimized.

@mgol

mgol Dec 29, 2016
Member

assert.ok( true, "String" ) was just a poor man's QUnit.skip before it existed; it's not necessary here. That's what I was trying to communicate in my last comment. :)


QUnit[ "content" in document.createElement( "template" ) ? "test" : "skip" ](
"contents() for <template /> remains inert",
2,

This comment has been minimized.

@mgol

mgol Dec 29, 2016
Member

Please use the assert.expect syntax here and in the previous test. The one you used here is deprecated and removed in QUnit 2: https://qunitjs.com/upgrade-guide-2.x/#replace-expected-argument-in-qunit-test

@timmywil timmywil added this to the 3.2.0 milestone Jan 9, 2017
@markelog markelog merged commit 3e3b09d into jquery:master Jan 29, 2017
2 checks passed
2 checks passed
continuous-integration/travis-ci/pr The Travis CI build passed
Details
licence/cla Contributor License Agreement is signed.
Details
@markelog
Copy link
Member

@markelog markelog commented Jan 29, 2017

@TechQuery awesome work! Your patience and hard work are much appreciated.

@mgol it seems all your concerns were addressed, but just in case, could you give another look?

@mgol
Copy link
Member

@mgol mgol commented Feb 1, 2017

@markelog Everything looks good here 👍

timmywil added a commit to timmywil/jquery that referenced this pull request Feb 6, 2017
@lock lock bot locked as resolved and limited conversation to collaborators Jan 18, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

6 participants