Add or Append HTML inside of a Polymer Element's template from Javascript

StackOverflow https://stackoverflow.com/questions/22946873

  •  30-06-2023
  •  | 
  •  

In an attempt to get carousel functionality working inside of a Polymer element, I am programmatically creating the markup needed for the Slick carousel inside of my element's script. Assume in this code snippet that this.videos has already been loaded as an array of objects that contain youtube video information, specifically an id and name property:

// Create Carousel Container
var carousel = $('<div id="carousel"></div>');

// Add Slides
this.videos.forEach(function(element, index, array){

    // Create a template for each slide
    var slideTemplate = $('<div><iframe width="420" height="315" src="//www.youtube.com/embed/' + element.id + '" frameborder="0" allowfullscreen></iframe></div>');

    // Append Each Slide to the Carousel
    slideTemplate.appendTo(carousel);

    // jQuery Method
    $('#carousel').appendChild("<div></div>");
});

Neither of the "append*" methods above are working, and the resultant DOM contains no div#carousel element. What is the proper way to add HTML to a Polymer element's template?

Here is a previous SO question I asked that explains why I have to attempt this method of injecting markup inside the element from javascript. It has to do with that lingering template tag in the markup > look under UPDATE 2 for the full run-down.

有帮助吗?

解决方案

There's no reason to write code :) The best way to do this is use Polymer's data-binding features rather and setup the DOM template ahead of time. In general, you should never need to touch the DOM like this. Here's the basic idea:

<div id="carousel">
  <template repeat="{{v in videos}}">
    <div><iframe src="//www.youtube.com/embed/{{v.id}}"></iframe></div>
  </template>
</div>

When this.videos is populated, the template engine will automatically stamp the markup for you.

其他提示

Thanks Eric for the response. My first attempt utilized your method of using the template repeat functionality to populate the markup from the this.videos array. I updated the original question with some additional context to help explain why I had to take a different track to make this work.

The template tag in the resulting markup interferes with Slick's ability to work because it needs to have a series of clean divs underneath the div#carousel in which it can manipulate to turn into slides. @ebidel: if you can provide explanation as to ways in which Polymer can or will address this lingering template issue, that would prove helpful in understanding how that new HTML5 template element should work.

Anyway, I figured out how to get this work using my programmatic method, which I'll explain here:

I stripped out the jquery line that creates the div#carousel and instead restored it inside the template:

<template>
    ...
    <polymer-jsonp id="jsonp" url="https://spreadsheets.google.com/feeds/list/1CpXbJHeFrzPpg58lWAsT1N3-QExbX9T5OPVeMfyBqYs/od6/public/values?alt=json-in-script&callback=" response="{{response}}"></polymer-jsonp>

    <div id="carousel"><div>

</template>

Next, here is the code in the Polymer constructor that makes this work:

<script>
    Polymer('video-group', {

        // element is fully prepared
        ready: function(){
            this.$.jsonp.go();            
        },

        // instance of the element is created
        created: function() {
          this.videos = [];
          this.response = {};
        },

        ...

        // Response from JSONP Data Changed Event Handler
        responseChanged: function() {

            // Get the Entry Point for the JSON Feed
            var entries = this.response.feed.entry;

            // Create an empty variable to store the video group
            var videos = [];

            // Push entries from the JSON feed onto the videos array
            for (var i = 0, entry; entry = entries[i]; ++i) {
                videos.push({
                    name: entry.gsx$name.$t,
                    id: entry.gsx$id.$t
                });
            }

            // Set the video group object's array to this newly supplied video array
            this.videos = videos;

            // Create the Carousel
            this.slick();
        },

        // Run the Slick Carousel Command
        slick: function() {

          // Slick it up!
          $('#carousel').slick({
            arrows: true,
              centerPadding: '50',
              dots: true,
              infinite: 'true'
          });

          // Add Slides
          this.videos.forEach(function(element, index, array){              

              // Create a template for each slide
              var slideTemplate = $('<div><iframe width="420" height="315" src="//www.youtube.com/embed/' + array[index].id + '" frameborder="0" allowfullscreen></iframe></div>');

              // Append Each Slide to the Carousel
              $('#carousel').slickAdd(slideTemplate);

          });

        }
    });
  </script>

responseChanged populates the this.videos array with the videos from the jsonp response and then calls the slick() function which first creates the carousel on the div#carousel element and then, using the slick API function slickAdd(), adds each slide inside this.videos to the template.

I don't have the proper time to read the other answers but all in all, i'll tell you: first thing is: jquery doesn't like polyfill, so in order for it to reach inside shadow dom you should use

var foo = document.querySelector('bodyOrhtmlOrWhatever /deep/ id-inside-the-shadow');

or

var foo = document.querySelector('moreSpecificSelectorinTheLightDOM ::shadow id-inside-this-shadow-dom');

or the third way which i found somewhere inside a polymer element or docs (can't remember, sorry) that is

var foo = `document.querySelector('moreSpecificSelectorinTheLightDOM').shadowRoot
        .querySelector('first-id-or-tag-inside-this-specific-shadow-dom').shadowRoot
        .querySelector('tag-inside-the-shadow-dom'); (...)

then consume it with jquery (which methods are way easier) like so

$(foo).each(function( index ) {
  console.log( 'do your magic here, "this" means the element being iterated' );
});

Well, to summarize "why": the Polyfill on 0.5.1 looks for javascript vanilla selectors and change them accordingly to work with non shadow-dom enabled browsers. Really hope it helps. It sure helped me. any further questions hit me up.

@ebidel solution will work (he's goddamn awesome) BUT, i'm not sure your library will like to play with it.

I'm on writing a selector component but i'm not sure if it will ever work, if it does i'll make a pull request of something of sorts.

I had a very similar use case. What I did was create a template for the slide, a template for the slider, and then initialized slick in my base page. In the Slider template's constructor, I use a factoryImpl function to add the individual slides in

Slide template

<dom-module id="slide">
    <template>
        <div>
            <!-- slide content goes here -->
        </div>
    </template>
    <script>
        Slide = Polymer({
            is: 'slide',
            factoryImpl: function(slide) {
                //Assume slide is a JS object that I use to set any {{properties}} I'll need
                this.property = slide.analagousProperty;
                this.otherProperty = someFunction(slide.analagousProperty);
            },
            properties: {
                //Properties defined here
            }
        });
    </script>
</dom-module>

Slider template

<dom-module id="slider">
    <template>
        <div id="slider">
        </div>
        <div class="controlHolder">
        </div>
    </template>
    <script>
        Slider = Polymer({
            is: 'slider',
            factoryImpl: function(slides) {
                var destination = Polymer.dom(this.$.slider);
                for (i = 0; i < slides.length; i++) {
                    var slide = new Slide(slides[i]).querySelector('div');
                    destination.appendChild(slide);
                }
                Polymer.dom.flush();
            },
            ready: function() {
                $('#slider').on('afterChange', 
                    function(event, slick, currentSlide) {
                        $('.slick-center .play-button').click(function() {
                        });
                });
            }
        });
    </script>
</dom-module>

Actual page

<head>
    <script src="../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
    <script src="../bower_components/jquery/dist/jquery.min.js"></script>
    <script src="../bower_components/slick-carousel/slick/slick.min.js"></script>
    <link rel="import" href="../bower_components/polymer/polymer.html">
    <link rel="import" href="../base_slide.html">
    <link rel="import" href="../base_slider.html">
    <link rel="stylesheet" type="text/css" href="../bower_components/slick-carousel/slick/slick.css" />
    <link rel="stylesheet" type="text/css" href="../bower_components/slick-carousel/slick/slick-theme.css" />
    <link rel="stylesheet" type="text/css" href="../myCustomStyleSheet.css">
</head>
<body>
    <div class="mainContent"> 
        <h1>Some heading</h1>
    </div>
    <div id="slideContent">
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            var slides = [
                {
                    sourceImage: '1.jpg',
                    caption: 'A kindly person'
                },
                {
                    sourceImage: '2.jpg',
                    caption: 'A kindly person'
                },
                {
                    sourceImage: '3.jpg',
                    caption: 'A kindly person'
                },
                {
                    sourceImage: '4.jpg',
                    caption: 'A kindly person'
                },
                {
                    sourceImage: '5.jpg',
                    caption: 'A kindly person'
                },
                {
                    sourceImage: '6.jpg',
                    caption: 'A kindly person'
                },
                {
                    sourceImage: '7.jpg',
                    caption: 'A kindly person'
                }
            ];
            var slider = new Slider(slides);
            document.getElementById('slideContent').appendChild(slider);
            $('#slider').slick({
                arrows: true,
                initialSlide: 4,
                dots: true,
                infinite: true,
                cssEase: 'ease',
                accessibility: true,
                swipeToSlide: true,
                focusOnSelect: true,
                centerMode: true, //Requires odd number of slides to show
                slidesToShow: 5,
                centerPadding: '60px',
                adaptiveHeight: true
            });
        });
    </script>
</body>

This is by no means my final implementation, but it shows another way to get the job done: use the querySelector() method to rip out the div you want from your custom item, then use appendChild() in the slider's constructor to drop in the stuff you want.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top