从脚本标签中排除HTML

5

我正在尝试学习Handlebars.js并考虑如何在我制作的网站中使用它。这是一个单页面网站,将有两个容器,每个容器中都有三个div,每个div都包含嵌入式Soundcloud播放器通过其API。

当在Handlebars处理的脚本标签中包含包含API请求的div时,该网站行为非常不可靠,并且只显示其中的一些六个播放器。它不是真正一致的,但可以始终显示不同的播放器。问题似乎在于Soundcloud JavaScript SDK,但我对其中的内容不太熟悉,因此不想深入了解。

因此,我想到了一种方法来排除播放器div(请参见代码),使它们能够立即加载而不被处理为JavaScript,但仍会显示在占位符div中的艺术家-标题下面(该div设置为包含Handlebar脚本的结果)。

问题在于,我无法找到一个好的方法来实现这一点,是否有任何简单的函数(使用Handlebars助手可能)可以帮助我实现我的目标?

<div id="placeholder"></div>
<script id="player-template" type="text/x-handlebars-template">
            <div id="container1">
                    Artist1 - {{title1}}
                    <div id="player1"></div>
                    Artist2 - {{title2}}
                    <div id="player2"></div>
                    Artist3 - {{title3}}
                    <div id="player3"></div>
           </div>
           <div id="container2">
                    Artist4 - {{title4}}
                    <div id="player4"></div>
                    Artist5 - {{title5}}
                    <div id="player5"></div>
                    Artist6 - {{title6}}
                    <div id="player6"></div>
            </div>
    </script>
    <script src="js/handlebars_title_script.js"></script>

当然,一种解决方法是为每个艺术家-标题div制作一个Handlebar模板,并将每个模板的占位符设置为仅包含Artist1 - {{title1}}的div,但这实际上破坏了使用Handlebars来最小化我的HTML编码的目的。

是否有任何提示可以帮助我解决这个问题?

编辑1:

我找到了另一种解决方案,即更改我的JavaScript代码(我最初没有发布,所以显然您无法帮助我解决问题)。

$(document).ready(function() {
    var hey = "heya";
    SC.get("/users/artist/tracks", {limit: 1}, function(tracks){
    var title_data1 = tracks[0].title;
    hey = tracks[0].title;
    alert(title_data1);
    alert(hey)
    });


//Data that will replace the handlebars expressions in our template
var playerData = {
    title1 : hey,
};

document.getElementById( 'player-placeholder' ).innerHTML = playerTemplate( playerData );

});

抱歉有些用意不良。这段代码的唯一问题是 title1(在变量 playerData 中,即 Handlebars 上下文中)获取了变量 hey(“heya”)的第一个值。当它被提示时,会弹出真正的标题,如何使 title1 使用该值而不需要嵌套更多的 JavaScript(因为那就会导致上述错误,球员显示出奇怪的情况)?
1个回答

2

注意:本答案在评论中发生了巨大变化。如果您想看到答案的演变,请查看早期版本。

在获取了您提供的JsFiddle示例后,我成功地使它按照您的要求工作。

演示

HTML:

<body>
    <div id="wrapper">
        <div id="player-placeholder"><!-- rendered template goes here --></div>

        <!-- handlebars template: -->
        <script id="player-template" type="text/x-handlebars-template">
            {{#each tracks}}
                <div class="track">
                    <header class="header">
                        <span class="artist">{{user.username}}</span> - <span class="title">{{title}}</span>
                    </header>
                    <section class="player" data-uri="{{permalink_url}}">
                    </section>
                </div>
            {{/each}}
        </script>
    </div>
</body>

JavaScript:

$(document).ready(function() {

  /*
    get your template string See:

    http://api.jquery.com/id-selector/
    http://api.jquery.com/html/
  */
  var source = $('#player-template').html();

  // compile the template into a handlebars function
  var template = Handlebars.compile(source);

  // initialize sound cloud api
  SC.initialize({
    client_id: '90fb9e15c1e26f39b63f57015ab8da0d'
  });

  /*
    This function will be called once the HTTP transaction
    started by SC.get(...) completes. Note, there's nothing
    wrong with doing this as an anonymous function, I'm
    simply assigning it to a variable to show that this
    is a distinct function that's called later
  */
  var callback = function(tracksResponse){
    /*
       once a response has been received, we'll use the response
       to generate a new context to pass to the template function.
       Note, you can use the template function in here because its
       within a closure. See:
       https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures
    */
    var context = { tracks: tracksResponse };
    var html = template(context);

    /*
      assign the rendered html to your placeholder on the page
      see: http://api.jquery.com/html/
    */
    $('#player-placeholder').html(html);

    /*
      Now that the html is rendered and on the page, its time to
      setup the sound cloud players. Note the css classes I assigned
      to the track/player. This line selects all of the player's and
      runs the function over each. See:

      http://api.jquery.com/class-selector/
      http://api.jquery.com/each/
    */
    $('.track .player').each(function(index, e){
      var $this = $(this); // jQuery reference to the current object in 'each loop'

      /*
        I assigned the permalink_url of each track to an attribute called 'data-uri'
        This line gets the value of that attribute. See:

        http://api.jquery.com/data/#data2
      */
      var permalink = $this.data('uri'); 
      var urlParameters = '/&maxheight=100&maxwidth=300&format=json&sharing=false';

      /*
        finally we call the sound cloud oEmbed function feeding it the url
        stored in the element, as well as the actual element.

        (see the second argument of the each function: http://api.jquery.com/each/)
      */
      SC.oEmbed(permalink + urlParameters, e);
    });
  };

  // get tracks for your artist
  // Note the "limit" in the object controls the number of items returned
  // by sound cloud
  SC.get("/users/theshins/tracks", {limit: 5}, callback);
});

出了什么问题?

JavaScript是单线程、异步、事件驱动的语言。这意味着JavaScript没有线程的概念(我有意忽略了WebWorkers)。为了解决这个限制,几乎所有JavaScript中的IO都是非阻塞的(异步的)。

每当异步IO事务开始时,它会立即返回给调用者,代码执行会继续。几乎所有IO操作都需要“回调”或者在IO操作完成时调用相应的事件。这就意味着所有IO操作的基本模式如下:

  • 创建一个回调函数
  • 调用IO操作,将其所需的参数和回调一起传递
  • 立即返回执行
  • 在未来的某个时候,将会调用回调函数

在原始例子中,$(document).ready(function(){...})会在document.onReady事件被触发时排队匿名函数进行。你的原始例子中,有两个回调函数被分配。这并不是一个问题,.ready(...)实际上是设计用于接受和排队许多回调函数。然而,你的错误在于有两个不同的代码块调用了SC.get(...)

从技术上讲,如果正确完成这个操作不会有问题,但是你的第一个on ready回调的目的是设置页面的HTML,而第二个回调试图基于页面上的HTML初始化播放器控件。请记住,这些事件和IO操作都是异步的,它们将按照任意顺序触发。本质上,这成为了时间问题,你试图在页面上同时初始化控件并生成HTML来显示。

如何解决

为了解决时间问题,你需要同步获取信息的时间、构建模板HTML和初始化控件的时间。有很多方法可以做到这一点,许多框架支持利用promises来帮助控制异步事件被触发的顺序以及回调的调用。

我采取了简单的方法,将你所有的SC.get调用合并为一个,然后在其回调函数中呈现handlebars模板并初始化SoundCloud播放器。


1
没问题,对我来说看起来你遇到了 JavaScript 异步/事件驱动本质的一些问题。当学习这门语言时,这可能是一个相当棘手的陷阱。我已经为答案添加了另一个部分。 - klyd
1
我现在明白了,你注册了两个$(document).ready()回调函数。这没有任何问题,并且它们将按顺序触发。然而,其中一个准备好的回调函数处理异步获取数据,另一个处理为该数据设置DOM/播放器。这两个事件需要以某种方式联系起来。我今晚会尝试为你写一个例子。 - klyd
好的,使用我最新的jsfiddle代码:链接,我成功地使一个玩家的标题更改。有什么提示可以让我的代码工作,以便所有六个玩家都更改为不同的标题?我考虑了这样的解决方案链接(只应用于前两个玩家)。我感觉我可能已经偏离了原来的话题,所以如果这篇文章没有结果,我想我会创建一个关于异步函数的新主题... - user3880485
你可能想要查看我提供的示例。它会更改所有六个玩家的标题。 - klyd
我看了你的演示,每个玩家都有不同的标题,看起来很棒!稍后我会查看你的代码,但是从第一眼看来,如果我想要不同艺术家曲目的标题并且需要多个SC.get命令,那么这可能需要更多的工作? 无论如何,我会检查它,并看看是否能找到解决方案... - user3880485
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接