Facebookのように、ページの最後までスクロールすると次のアイテムが自動で読み込まれるみたいなサイト、最近ほんと増えましたよね。
で、SharePointでもこれできないかな?と考えていたところ、こちらの記事
jQueryでページ最下部までスクロールした際に外部HTML(JSON形式)化した要素を追加する方法 | BlackFlag
を読みまして、SharePoint2013でトライしてみることにしました。
サイト内に作成したカスタムリスト(ここではtestというリスト名で作成、列はTitleとBodyだけ)からRESTサービスでリストアイテムを呼び出し、読み込んでいます。
(ほんとは標準のリストビューで無限スクロールできればカッコよかったのですが、私の技量ではどうにも無理なのでこの形になりました・・・)
なお、下記ソースは上記BlackFlagさんの記事からほとんどいただいておりますので、ぜひ元記事もご参照ください。
※CSSは、ほぼそのまま拝借させていただいております。ありがとうございます!
[追記]
最初のソースでは、アイテムへのリンクをクリックして表示したのち前のページに戻ると、スクロール位置が最初に戻ってしまい、読み込んでいた内容が消えてしまって「ああああ・・・」という感じだったので、リンクをクリックしたらアイテムをダイアログで表示させるようにしてみました。これで、少しは実用に近づいてきたでしょうか・・・?
[さらに追記]
超・有名ブログSharePoint Maniacsの著者、シンプレッソ・コンサルティング中村様から、このようにAjaxによる非同期通信でリストアイテムを表示するのがSharePointのパフォーマンス改善に効果があり、今年のSharePoint カンファレンス(米国)でもレスポンスアップに関するセッションで紹介されていた、とのコメントをいただきました。
私がこの記事を書いたときは、パフォーマンス改善のことはまったく念頭になかったので追記させていただきます。
なお、上記SharePointカンファレンスのセッションはこちらです。
http://channel9.msdn.com/events/SharePoint-Conference/2014/SPC3993
ぜんぜんSharePointの機能じゃないじゃんよ~、というツッコミはさておき、こういうやり方もあるよ、ということで。
HTML
<link rel="stylesheet" type="text/css" href="/[your site]/[your library]/jsonSample.css" />
<script type="text/javascript" src="https://code.jquery.com/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="/[your site]/[your library]/jasonSample.js"></script>
<div id="loadarea"></div>
jsonSample.js
$(function(){
var setArea = $('#loadarea'),
loadNum = 1, // 読み込む個数
loadTxt = 'Now Loading...', // Loading中の表示テキスト
fadeSpeed = 500; // フェードスピード
var onclickStr;
var txt;
$.ajax({
url: "/[your site]/_api/web/lists/getbytitle('test')/items?$select=*",
method: "GET",
headers: { "Accept": "application/json; odata=verbose" },
success : function(data){
for (var i=0; i<5; i++) {
onclickStr = "openInDialog('/[your site]/Lists/test/DispForm.aspx?ID=" + data.d.results[i].ID + "',500,500,true,true,false); return:false;";
txt = data.d.results[i].Body;
if(txt.length > 200){txt = txt.substr(0, 200) + "...";}
$('<div id="' + data.d.results[i].Title + '" class="loadItem"><h3>'
+ data.d.results[i].Title + '</h3><br>'
+ txt + ' <a href="javascript:void(0)" onclick='
+ onclickStr + '>' + '[read more]'+ '</a></div>').appendTo(setArea);
}
}
});
$("#s4-workspace").scroll(function(){
var winHeight = $("#s4-workspace").height();
var posBottom = $("#s4-bodyContainer").height() - winHeight;
if($("#s4-workspace").scrollTop() >= posBottom) {
var adjScrTop = $("#s4-workspace").scrollTop();
$("#s4-bodyContainer").animate({scrollTop:(adjScrTop)-1},0);
$.ajax({
url: "/[your site]/_api/web/lists/getbytitle('test')/items?$select=*",
method: "GET",
headers: { "Accept": "application/json; odata=verbose" },
success : function(data){
var dataLengh = data.d.results.length,
loadItemLength = setArea.find('.loadItem').length,
setAdj = (dataLengh)-(loadItemLength),
setBeg = (dataLengh)-(setAdj);
if(!(dataLengh == loadItemLength)){
setArea.append('<div id="nowLoading">' + loadTxt + '</div>');
if(loadItemLength == 0){
for (var i=0; i<loadNum; i++) {
onclickStr = "openInDialog('/[your site]/Lists/test/DispForm.aspx?ID=" + data.d.results[i].ID + ",500,500,true,true,false'); return:false;";
txt = data.d.results[i].Body;
if(txt.length > 200){txt = txt.substr(0, 200) + "...";}
$('<div id="' + data.d.results[i].Title + '" class="loadItem"><h3>'
+ data.d.results[i].Title + '</h3><br>'
+ txt + ' <a href="javascript:void(0)" onclick='
+ onclickStr + '>' + '[read more]'+ '</a></div>').appendTo(setArea)
.css({opacity:'0'}).animate({opacity:'1'},fadeSpeed);
}
} else if(loadItemLength > 0 && loadItemLength < dataLengh){
if(loadNum < setAdj){
for (var i=0; i<loadNum; i++) {
v = i+setBeg;
onclickStr = "openInDialog('/[your site]/Lists/test/DispForm.aspx?ID=" + data.d.results[v].ID + "',500,500,true,true,false); return:false;";
txt = data.d.results[v].Body;
if(txt.length > 200){txt = txt.substr(0, 200) + "...";}
$('<div id="' + data.d.results[v].Title + '" class="loadItem"><h3>'
+ data.d.results[v].Title + '</h3><br>'
+ txt + ' <a href="javascript:void(0)" onclick='
+ onclickStr + '>' + '[read more]'+ '</a></div>').appendTo(setArea)
.css({opacity:'0'}).animate({opacity:'1'},fadeSpeed);
}
} else if(loadNum >= setAdj){
for (var i=0; i<setAdj; i++) {
v = i+setBeg;
onclickStr = "openInDialog('/[your site]/Lists/test/DispForm.aspx?ID=" + data.d.results[v].ID + "'500,500,true,true,false); return:false;";
txt = data.d.results[v].Body;
if(txt.length > 200){txt = txt.substr(0, 200) + "...";}
$('<div id="' + data.d.results[v].Title + '" class="loadItem"><h3>'
+ data.d.results[v].Title + '</h3><br>'
+ txt + ' <a href="javascript:void(0)" onclick='
+ onclickStr + '>' + '[read more]'+ '</a></div>').appendTo(setArea)
.css({opacity:'0'}).animate({opacity:'1'},fadeSpeed);
}
}
} else if(loadItemLength == dataLengh){
return false;
}
} else {
return false;
}
},
complete : function(){
$('#nowLoading').each(function(){
$(this).remove();
});
return false;
}
});
return false;
}
});
//add button go back to page top
var topBtn = $('#page-top');
topBtn.hide();
$('#s4-workspace').scroll(function () {
if ($(this).scrollTop() > 400) {
topBtn.fadeIn();
} else {
topBtn.fadeOut();
}
});
topBtn.click(function () {
$('#s4-workspace').animate({
scrollTop: 0
}, 500, "swing");
return false;
});
});
//Open item in dialog
function openInDialog(pageUrl, dlgWidth, dlgHeight, dlgAllowMaximize,dlgShowClose,needCallbackFunction){
var options = {
url: pageUrl,
width: dlgWidth,
height: dlgHeight,
allowMaximize: dlgAllowMaximize,
showClose: dlgShowClose
};
if(needCallbackFunction){
options.dialogReturnValueCallback = Function.createDelegate(null, CloseDialogCallback);
}
SP.SOD.execute('sp.ui.dialog.js', 'SP.UI.ModalDialog.showModalDialog', options);
}
function CloseDialogCallback(dialogResult, returnValue){
if(dialogResult == SP.UI.DialogResult.OK){
//do something
;
}else if(dialogResult == SP.UI.DialogResult.cancel){
;
}else{
;
}
}
jsonSample.css
/* #loadarea
--------------------------- */
#loadarea {
margin: 0 auto;
width: 500px;
text-align: left;
}
#loadarea .loadItem {
padding: 10px 0;
width: 500px;
line-height: 160%;
border-bottom: #666 1px dotted;
}
/* #nowLoading
--------------------------- */
#nowLoading {
padding: 5px 0;
width: 100%;
text-align: center;
color: #fff;
}
/* =======================================
ClearFixElements
======================================= */
#loadarea .loadItem:after {
content: ".";
height: 0;
clear: both;
display: block;
visibility: hidden;
}
#loadarea .loadItem {
display: inline-block;
overflow: hidden;
}
/* =======================================
Page Top Button
======================================= */
#page-top
{
position: fixed;
bottom: 20px;
right: 50px;
}
#page-top a
{
background: #666;
text-decoration: none;
color: #fff;
width: 50px;
padding: 8px 0;
text-align: center;
display: block;
border-radius: 3px;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
}
#page-top a:hover
{
text-decoration: none;
background: #999;
}
Gistにも置きました。
JS
https://gist.github.com/marineko/7a1061a633f5e21560ee
CSS
https://gist.github.com/marineko/8c4dcfddf7fa45b59956
手前味噌ですが、こちらの記事で実装した「TOPへ戻る」ボタンも併用しています。ページが長くなってもにゅるっと最初に戻れていい感じです。
スクロールするとトップへ戻るボタンを表示してみた
そのほか、プログラムを書くにあたり参考にさせていただいた記事です。
☆RESTサービスの使い方
SharePoint 2013 で REST サービスを使用したプログラミング方法 – Japan SharePoint Support Team Blog – Site Home – TechNet Blogs.
SharePoint 2013 – CRUD on List Items Using REST Services & jQuery | Plus Consulting Blog.
☆アイテムをダイアログで開く
SharePoint 2010 ダイアログで開く input タグ | クリエ・イルミネート ブログ.