티스토리 가이드에 보면 '<s_t3></s_t3>' 치환자는 필수라는 내용이 나옵니다.
참고 : 티스토리 스킨 가이드 - 공통 치환자
다른 포스트에서도 언급했었지만 '<s_t3></s_t3>'와 같은 치환자는 티스토리 전용 기능을 위해 이것저것 잔뜩 불러온다는 문제가 있습니다.
(참고 : [티스토리 스킨] 티스토리의 치환자는 자체 스크립트도 불러온다. )
이런 기능들을 사용하지 않는다면 사이트 속도만 느려지게 만들죠.
* 주의 *
이 치환자를 제거한 이후에 나오는 에러들은 상당한 자바스크립트(javascript) 지식이 있어야 해결할 수 있는 문제입니다.
자신이 없다면 제거하지 마시길 바랍니다.
제거로 인한 책임은 모두 자신에게 있음을 명심하세요.
기초적인 자바스크립트에 관한 질문은 받지 않습니다.
일단'<s_t3></s_t3>' 을 제거하고 블로그로 들어가 봅시다.
제일 눈에 띄는 건 댓글 관련 동작과 티스토리 단축키 기능입니다.
만약 티스토리의 댓글 펼치기 기능을 쓰고 있다면 해당 기능도 에러가 납니다.
이런 기능들이 들어 있는 파일이 'common.js'입니다.
소스를 추적해 들어가 보면 댓글 말고도 인증 관련 내용들이 있는데....
인증 관련 내용은 이제는 사용하지 않는 것으로 보입니다.
여기서 함수명으로 사이트 검색을 해보면 호출되지 않는 함수도 많습니다.
스킨에 업로드할 .js 파일을 생성합니다.
(저는 'TistoryCustom.js'로 생성했습니다.)
밑에는 제가 정리한 'common.js'의 내용입니다.
사용하시면서 문제가 발생하면 댓글 남겨주시길 바랍니다.
function addComment(submitButton, entryId) {
(function($) {
var oForm = findFormObject(submitButton);
if (!oForm) {
return false;
}
var data = {
key: 'tistory'
}
var $captchaInput = $("#inputCaptcha");
if ($captchaInput.length > 0) {
if (!$captchaInput.val()) {
alert('그림문자를 입력해 주세요.');
return false;
}
data.captcha = $captchaInput.val();
}
if (oForm["name"]) {
data.name = oForm["name"].value
}
if (oForm["password"]) {
var passwd = oForm["password"].value.trim();
if (passwd.length == 0) {
alert('비밀번호를 입력해 주세요.');
return false;
}
var shaObj = new jsSHA("SHA-256", "TEXT");
shaObj.update(md5(encodeURIComponent(passwd)));
data.password = shaObj.getHash("HEX");
}
if (oForm["homepage"]) {
data.homepage = oForm["homepage"].value;
}
if (oForm["secret"] && oForm["secret"].checked) {
data.secret = 1;
}
if (oForm["comment"]) {
data.comment = oForm["comment"].value
}
if (submitButton && submitButton.setAttribute) {
submitButton.setAttribute('disabled', true);
}
$.ajax({
url: oForm.action + '?__T__=' + (new Date()).getTime(),
method: 'post',
data: data
}).done(function(r) {
if (entryId == 0) {
window.location = addUriPrefix("/guestbook");
return;
}
var data = r.data;
var $comments = $("#entry" + entryId + "Comment"),
$recentComments = $("#recentComments"),
$commentCountOnRecentEntries = $("#commentCountOnRecentEntries" + entryId);
$comments.html(data.commentBlock);
$recentComments.html(data.recentCommentBlock);
for (var i = 0 ; i < data.commentViews.length ; i++) {
$("#commentCount" + entryId + "_" + i).html(data.commentViews[i]);
}
$commentCountOnRecentEntries.html("(" + data.commentCount + ")");
if (typeof window.needCommentCaptcha !== "undefined") {
captchaPlugin.init('complete');
}
}).fail(function(r) {
alert(r.responseJSON.data);
}).always(function() {
if (submitButton && submitButton.setAttribute) {
submitButton.setAttribute('disabled', false);
}
})
})(tjQuery);
}
function commentVisibility(id) {
var visibility = document.getElementById('commentVisibility_'+id);
if(visibility.innerHTML == "[승인완료]")
return false;
var request = new HTTPRequest("POST", addUriPrefix("/admin/comment/approve.php"));
visibility.innerHTML = "[승인중]";
request.onVerify = function() {
try {
var result = eval("(" + this.getText() + ")");
return (result.error == false)
}
catch (e) {
return false;
}
};
request.onSuccess = function() {
document.getElementById('commentVisibility_'+id).innerHTML = "[승인완료]"
};
request.onError = function() {
document.getElementById('commentVisibility_'+id).innerHTML = "[승인실패]"
};
request.send('id=' + id + '&approved=1');
}
var openWindow='';
function alignCenter(win,width,height) {
if(navigator.userAgent.indexOf("Chrome") == -1)
win.moveTo(screen.width/2-width/2,screen.height/2-height/2);
}
function deleteComment(id) {
var width = 450;
var height = 550;
try { openWindow.close(); } catch(e) { }
openWindow = window.open( addUriPrefix("/comment/manage/") + id, "tatter", "width="+width+",height="+height+",location=0,menubar=0,resizable=0,scrollbars=0,status=0,toolbar=0");
openWindow.focus();
alignCenter(openWindow,width,height);
}
function deleteGuestbookComment(id, guestbookWrittenPage) {
var width = 450;
var height = 550;
try { openWindow.close(); } catch(e) { }
openWindow = window.open(addUriPrefix("/comment/manage/" + id + (guestbookWrittenPage ? "?guestbookWrittenPage=" + guestbookWrittenPage: "")), "tatter", "width="+width+",height="+height+",location=0,menubar=0,resizable=0,scrollbars=0,status=0,toolbar=0");
openWindow.focus();
alignCenter(openWindow,width,height);
}
function commentComment(parent) {
var visibility = document.getElementById('commentVisibility_'+parent);
if(visibility === null || visibility.innerHTML == "[승인완료]") {
var width = 450;
var height = 550;
try {
openWindow.close();
} catch (e) {
}
openWindow = window.open( addUriPrefix("/comment/comment/") + parent, "tatter", "width=" + width + ",height=" + height + ",location=0,menubar=0,resizable=0,scrollbars=0,status=0,toolbar=0");
openWindow.focus();
alignCenter(openWindow, width, height);
}
else {
alert('승인 대기중인 댓글에는 답글을 작성할 수 없습니다.');
return false;
}
}
function guestbookCommentComment(parent, guestbookWrittenPage) {
var visibility = document.getElementById('commentVisibility_'+parent);
if(visibility === null || visibility.innerHTML == "[승인완료]") {
var width = 450;
var height = 550;
try {
openWindow.close();
} catch (e) {
}
openWindow = window.open(addUriPrefix("/comment/comment/" + parent + (guestbookWrittenPage ? "?guestbookWrittenPage=" + guestbookWrittenPage : "")), "tatter", "width=" + width + ",height=" + height + ",location=0,menubar=0,resizable=0,scrollbars=0,status=0,toolbar=0");
openWindow.focus();
alignCenter(openWindow, width, height);
}
else {
alert('승인 대기중인 댓글에는 답글을 작성할 수 없습니다.');
return false;
}
}
function editEntry(parent) {
var apiFrame = document.getElementById('editEntry');
apiFrame.contentWindow.postMessage(parent, TistoryBlog.tistoryUrl);
}
window.addEventListener('message', function(event) {
if (event.origin !== TistoryBlog.tistoryUrl) {
return;
}
if (event.data === 'reload') {
window.document.location.reload()
}
}, false);
function guestbookComment(parent) {
var width = 450;
var height = 550;
try { openWindow.close(); } catch(e) { }
openWindow = window.open(addUriPrefix("/comment/comment/") + parent, "tatter", "width="+width+",height="+height+",location=0,menubar=0,resizable=0,scrollbars=0,status=0,toolbar=0");
openWindow.focus();
alignCenter(openWindow,width,height);
}
function showTooltip(text) {
if (typeof text != 'undefined' && text.length > 0) {
var $layer = tjQuery('body .layer_tooltip');
tjQuery(".desc_g", $layer).html(text);
$layer.show();
setTimeout(function() {
$layer.hide();
}, 1000);
}
}
function deleteEntry(id) {
if (!confirm("이 글 및 이미지 파일을 완전히 삭제합니다. 계속하시겠습니까?")) {
return;
}
var request = new HTTPRequest("POST", addUriPrefix("/admin/entry/delete/"));
request.onSuccess = function() {
window.location.reload();
};
request.onError = function () {
alert('삭제에 실패 했습니다');
};
request.send("ids="+id);
}
function followBlog(blogId, $target, url, device) {
if (!!initData.user) {
var requestUrl = addUriPrefix("/subscription/");
return tjQuery.ajax({
method: "POST",
// dataType: "jsonp",
url: requestUrl,
data: {
blogId: blogId,
type: "follow",
token: TistoryBlog.token,
url: url,
device: device
},
xhrFields: {
withCredentials: true
}
}).done(function (r) {
tjQuery(".btn_subscription").addClass("following");
tjQuery(".btn_subscription .txt_post,.btn_subscription .txt_state").html('구독중');
showTooltip("이 블로그를 구독합니다.");
}).fail(function (r) {
showTooltip("구독 실패");
}).always(function() {
$target.data("doing", false);
});
} else {
window.location = window.appInfo.loginUrl + "?redirectUrl=" + encodeURIComponent(window.location.href);
}
}
function unfollowBlog(blogId, $target, url, device) {
if (!!initData.user) {
var requestUrl = addUriPrefix("/subscription/");
tjQuery.ajax({
method: "POST",
// dataType: "jsonp",
url: requestUrl,
data: {
blogId: blogId,
type: "unfollow",
token: TistoryBlog.token,
url: url,
device: device
},
xhrFields: {
withCredentials: true
}
}).done(function (r) {
tjQuery(".btn_subscription").removeClass("following");
tjQuery(".btn_subscription .txt_post,.btn_subscription .txt_state").html('구독하기');
showTooltip("이 블로그 구독을 취소합니다.");
}).fail(function (r) {
showTooltip("구독 취소 실패");
}).always(function() {
$target.data("doing", false);
});
} else {
window.location = window.appInfo.loginUrl + "?redirectUrl=" + encodeURIComponent(window.location.href);
}
}
function reloadEntry(id) {
var password = document.getElementById("entry" + id + "password");
if (!password)
return;
document.cookie = "GUEST_PASSWORD=" + encodeURIComponent(password.value) + ";path=";
window.location.reload();
}
loadedComments = new Array();
loadedTrackbacks = new Array();
function viewTrigger(id, category, categoryId) {
var request = new HTTPRequest("GET", addUriPrefix("/"+category+"/view/" + id));
var target = document.getElementById('entry'+id+(category == 'comment' ? 'Comment' : 'Trackback'));
if(target == null)
return false;
request.onSuccess = function() {
target.innerHTML = this.getText("/response/result");
target.style.display = 'block';
category == 'comment' ? loadedComments[id] = true : loadedTrackbacks[id] = true;
if(categoryId > -1)
location = location.toString();
if(typeof window.needCommentCaptcha !== "undefined"){
captchaPlugin.init();
}
findFragmentAndHighlight();
};
request.onError = function() {
alert('실패 했습니다');
};
request.send();
}
function highlight() {
var hash = new RegExp("^#(comment\\d+)").exec(window.location.hash);
if(hash && (el = document.getElementById(hash[1]))){
var highlightColor = el.getAttribute("activecommentbackground") || "#FFFF44";
highlightElement(hash[1], 0, el.style.backgroundColor, highlightColor);
}
}
function highlightElement(id, amount, origColor, highlightColor) {
var el = document.getElementById(id);
if (!el) {
return;
}
el.style.backgroundColor = (amount % 2)
? highlightColor
: origColor;
if (++amount < 7) {
setTimeout(function (){
highlightElement(id, amount, origColor, highlightColor)
},200);
}
}
function toggleLayerForEntry(id, category, categoryId, mode) {
if((category == 'comment' ? loadedComments[id] : loadedTrackbacks[id])) {
try {
var obj = document.getElementById('entry'+id+(category == 'comment' ? 'Comment' : 'Trackback'));
if(mode == undefined)
obj.style.display = (obj.style.display == "none") ? "block" : "none";
else
obj.style.display = (mode == 'show') ? "block" : "none";
} catch (e) {
}
return true;
} else {
if(categoryId) {
viewTrigger(id,category, categoryId);
} else {
viewTrigger(id,category, -1);
}
}
}
function ObserverForAnchor(evetObj) {
var lo = location.toString();
var queryString = lo.substr(lo.lastIndexOf('/')+1);
if(queryString.indexOf('#')>-1) {
var qsElements = queryString.split('#');
if(qsElements[1].indexOf('comment')>-1) {
var category = 'comment';
} else if(qsElements[1].indexOf('trackback')>-1) {
var category = 'trackback';
}
if(category) {
entryid = qsElements[0];
categoryId = qsElements[1].substr(category.length);
toggleLayerForEntry(entryid,category,categoryId, 'show');
}
}
}
window.addEventListener("load", ObserverForAnchor, false);
function addUriPrefix(path) {
return TistoryBlog.basePath + path;
}
위에서 만든 파일을 업로드하고
html에서 위에서 만든 파일을 로드하도록 넣습니다.
'<s_t3></s_t3>'를 넣던위치에 넣습니다.
<body class="bg-faded">
<!--<s_t3></s_t3>-->
<script src="./images/TistoryCustom.js" charset="euc-kr" ></script>
이것 말고도 몇 가지 에러가 더 날 수 있습니다.
아래 코드를 추가하여 예방할 수 있습니다.
function findFragmentAndHighlight(n){ }
'STD'를 찾을 수 없어 나는 에러입니다.
var STD = {};
STD.addEventListener = function(obj){};
'STD'에 'event'가 없어서 에러 나는 경우
STD.event = function (event) { };
loadedComments는 배열입니다.
var loadedComments = new Array();
자체 하이라트기능이....에러가 납니다.
function highlight() {}
만약 자체 하이라이트 기능을 사용하려면 아래 코드를 추가합니다.
function highlight()
{
var hash = new RegExp("^#(comment\\d+)").exec(window.location.hash);
if (hash && (el = document.getElementById(hash[1])))
{
var highlightColor = el.getAttribute("activecommentbackground") || "#FFFF44";
highlightElement(hash[1], 0, el.style.backgroundColor, highlightColor);
}
}
function highlightElement(id, amount, origColor, highlightColor)
{
var el = document.getElementById(id);
if (!el)
{
return;
}
el.style.backgroundColor = (amount % 2)
? highlightColor
: origColor;
if (++amount < 7)
{
setTimeout(function ()
{
highlightElement(id, amount, origColor, highlightColor)
}, 200);
}
}
이렇게 구현하면 티스토리 자체의 'PageMaster'라는 기능이 빠지게 됩니다.
여기에 어떤 기능들이 있는지 분석하지 않았지만 제가 확인한 기능 중 추가해야 할 것들을 추가합니다.
댓글 바로가기를 해보면 주소에 해쉬로 불리는 샾(#)이 붙는걸 알 수 있습니다.
원래 이렇게 해쉬가 붙으면 브라우저에서 자동으로 해당 'id'를 가진 엘리먼트(element)를 찾아 이동시켜주지만....
티스토리 같은 경우 페이지 로드를 하는 동안은 이 기능이 동작하지 않습니다.
그래서 유틸을 이용하여 처리하고 있었는지 'PageMaster'를 빼면 동작하지 않습니다.
구현은 쉽습니다.
기존 해쉬를 제거했다가 다시 붙여주면 됩니다.
let sHash = window.location.hash;
if (sHash)
{
//기존 해쉬를 지우고
window.location.hash = "";
//약간 대기했다가 스크롤
setTimeout(function () { window.location.hash = sHash;}, 100);
}
애드센스 전체화면 광고를 쓰고 있다면 브라우저 자체 해쉬 기능이 제대로 동작하지 않을 수 있습니다.
이때는 해쉬 동작을 'scrollTo()'로 바꿔야 합니다.
let sHash = window.location.hash;
if (sHash)
{
//대상 찾기
let domTarget = document.getElementById(window.location.hash.replace("#", ""));
if (domTarget)
{
//약간 대기했다가 스크롤
setTimeout(function ()
{
//대상까지 스크롤
window.scrollTo(domTarget.offsetLeft, domTarget.offsetTop);
}, 100);
}
}
최종 완성된 파일 :
유저가 이런 기능을 선택할 수 있도록 해주면 좋을 텐데 말이죠.....
결국 유저가 직접 한땀한땀 찾아서 해결하는 수밖에 없습니다 ㅎㅎㅎ