我正在尝试学习 Handlebars.js 并想出了一种在我正在制作的网站中使用它的方法。这是一个单页网站,将有两个容器,每个容器包含三个 div,其中将通过其 API 包含嵌入式 Soundcloud 播放器。
当在 Handlebars 处理的脚本标签中包含包含 API 请求的 div 时,该站点的行为非常不可靠,并且仅显示六个玩家中的一部分。它不是很一致,但可以一直显示不同的玩家。问题似乎出在 Soundcloud javascript SDK 中,但我觉得在那里挖掘太多并不熟悉。
因此我想到了一些方法来排除播放器 div(参见代码),以便它们立即加载而不是作为 javascript 处理,但仍然显示在艺术家下方 - 占位符 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>
一个解决方案当然是为每个 Artist - Title 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(在 Handlebars 上下文的变量 playerData 中)获取变量 hey(“heya”)的第一个值。当它收到警报时它会弹出真实的标题,我怎样才能让 title1 使用这个值而不是在更多的 javascript 中嵌套变量(因为这就是导致前面提到的玩家出现奇怪的错误的原因)?
最佳答案
注意:在整个评论中,此答案已发生巨大变化。如果您想查看此答案的演变,请查看早期修订。
在掌握了您的 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(...)
。
从技术上讲,如果操作正确,这不会成为问题,但您的第一个就绪回调的目的是设置页面的 HTML,而您的第二个回调试图根据页面上的 html 初始化播放器控件。请记住这些事件和 IO 操作是异步的,它们会以任何顺序触发。本质上这变成了时间问题,您试图初始化页面上的控件,同时生成 HTML 以显示在页面上。
它是如何修复的
要解决时间问题,您需要在获取信息、构建模板 HTML 和初始化控件时进行同步。有很多方法可以做到这一点,许多框架都支持 promises 的想法。帮助控制触发异步事件及其回调的顺序。
我采用了简单的方法,将您所有的 SC.get
调用合并为一个,然后在它的回调中渲染 Handlebars 模板并初始化 SoundCloud 播放器。
关于javascript - 从脚本标签中排除 HTML,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25271420/