有的时候需要在文章内设置一个折叠框,里面的文字可以是某段文字的引申、来源和参考,或是一张不太重要图片,或是一段代码,通过折叠框,可以让文章看起来更加简洁和利索,这要比一块一块色彩斑斓的页面(比如您正在看的本页面)要好得多吧!

有的时候还需要在文章内设置一个密码保护框,里面可以放一些不想让无关的人看到,只想在部分范围内流传的玩意,这要比单独为一篇文章设置密码保护更加灵活些。

我没有上面任何一个要求,但是这个蛋疼的小功能还是被我做出来了,虽然一点都不难。

两个功能的示例没有用短代码生成,而是直接往文章里面写的 HTML。

这是一段被折叠的文字 A展开

这是一段被折叠的文字 B展开

这是一段被折叠的文字 C展开

这是一段被保护的文字确定

好吧,主题 functions.php 内加入:

//折叠
function xcollapse($atts, $content=null){
	extract(shortcode_atts(array("title"=>""),$atts));
	return '<div style="margin:0.5em 0;width:98%"><div class="xControl"><span class="xTitle">'.$title.'</span><a href="javascript:void(0)" class="collapseButton xButton">展开</a><div style="clear:both"></div></div><div class="xCollapse" style="display:none">'.$content.'</div></div>';
}
add_shortcode('collapse','xcollapse');
//密码保护
function xprotect($atts, $content=null){
	extract(shortcode_atts(array("title"=>"", "password"=>""),$atts));
	$content = str_replace(" ","&space;",$content);
	$content = base64_encode(urlencode($content));
	$password = str_replace(" ","&space;",$password);
	$password = base64_encode(urlencode($password));
	return '<div style="margin:0.5em 0;width:98%"><div class="xControl" id="'.$password.'"><span class="xTitle">'.$title.'</span><a href="javascript:void(0)" class="protectButton xButton">确定</a><br><input type="text" class="password"><div style="clear:both"></div></div><div class="xProtect" style="display:none">'.$content.'</div></div>';
}
add_shortcode('protect','xprotect');

主题 JavaScript 内加入:

jQuery(document).ready(
function($){
//折叠 1 :只对本框操作。请选其中一个,不能同时使用。
$('.collapseButton').click(function(){
	if($(this).parent().parent().find('.xCollapse').is(':hidden')){$(this).html('收缩')}else{$(this).html('展开')};
	$(this).parent().parent().find('.xCollapse').slideToggle('slow');
});
//折叠 2 : A 框展开,其它已展开的框自动收缩。请选其中一个,不能同时使用。
$('.collapseButton').click(function(){
	$(this).removeClass("collapseButton").parent().parent().find('.xCollapse').removeClass("xCollapse");
	$('.xCollapse').slideUp('fast');
	$('.collapseButton').html('展开');
	$(this).addClass("collapseButton").parent().parent().find('div:last').addClass("xCollapse");
	if($(this).parent().parent().find('.xCollapse').is(':hidden')){$(this).html('收缩')}else{$(this).html('展开')};
	$(this).parent().parent().find('.xCollapse').slideToggle('fast');
	$(this).attr({id:"collapse_this"});
	$('html, body').animate({ scrollTop: $('#collapse_this').offset().top - 20}, 400);
});
//密码保护
$('.protectButton').click(function(){
	var password = $(this).parent().attr('id');
	var passwordInput = $(this).parent().find('.password').attr('value');
	password = atob(password);
	password = decodeURIComponent(password);
	password = password.replace(/&space;/g," ")
	if (password == passwordInput){
		var content = $(this).parent().parent().find('.xProtect').html();
		content = atob(content);
		content = decodeURIComponent(content);
		content = content.replace(/&space;/g," ")
		$(this).parent().parent().find('.xProtect').html(content);
		$(this).parent().parent().find('.xProtect').slideDown();
		$(this).fadeOut('normal');
		$(this).parent().find('.password').fadeOut('normal');
	}else{
		alert('您输入的密码不正确!');
	};
});
//密码保护用到的 JavaScript base64 解密
function atob(str) {
	var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
	var invalid = {
		strlen: (str.length % 4 != 0),
		chars: new RegExp('[^' + chars + ']').test(str),
		equals: (/=/.test(str) && (/=[^=]/.test(str) || /={3}/.test(str)))
	};
	if (invalid.strlen || invalid.chars || invalid.equals) alert('无效的 Base64 编码');
	var decoded = [];
	var c = 0;
	while (c < str.length) {
		var i0 = chars.indexOf(str.charAt(c++));
		var i1 = chars.indexOf(str.charAt(c++));
		var i2 = chars.indexOf(str.charAt(c++));
		var i3 = chars.indexOf(str.charAt(c++));
		var buf = (i0 << 18) + (i1 << 12) + ((i2 & 63) << 6) + (i3 & 63);
		var b0 = (buf & (255 << 16)) >> 16;
		var b1 = (i2 == 64) ? -1 : (buf & (255 << 8)) >> 8;
		var b2 = (i3 == 64) ? -1 : (buf & 255);
		decoded[decoded.length] = String.fromCharCode(b0);
		if (b1 >= 0) decoded[decoded.length] = String.fromCharCode(b1);
		if (b2 >= 0) decoded[decoded.length] = String.fromCharCode(b2);
	}
	return decoded.join('');
};
});

设定的 CSS 样式(与本页面主题的 CSS 相叠加):

.xControl,.xProtect,.xCollapse{border:1px solid silver;display:block;padding:2px 2px}
.xProtect,.xCollapse{border-top:none}
.xButton,.password{float:right}
.password{height:14px}
.xTitle{float:left}

司徒原指出了一个错误,在页面最开始就调用会出现代码失效,原因是此代码必须在页面加载完成后再执行,所以需要用jQuery(document).ready()包围

调用:
折叠内容:[collapse title="折叠内容的标题"]折叠内容[/collapse]
密码保护:[protect title="保护内容的标题" password="密码"]保护内容[/collapse]
其实完全没有必要将 JavaScript 加入到主 JavaScript 文件中,可以将其保存到单独的文件中,然后需要调用的时候在文章中加入<script src="/abc.js"></script>的HTML代码就可以引用了(本页面就是这么干的)。

原理:
后台 urlencode 内容和密码,再 base64_encode 内容和密码(可以避免 JavaScript base64_decode 时的中文乱码问题,但会造成所有的空格变成“+”,于是还得先将所有空格替换成其他内容,解密以后在替换回来显示就是正常的了),客户端利用 JavaScript 输入的密码与解码后的原始密码比较(如果要把输入的明文密码加密为密文再与原始密码密文比较还得加个 base64_encode 的 JavaScript 代码,这理论上会提高安全性,但是没必要,真懂 JavaScript 断点调试的人根本不担心解不开这个东西),一致则对内容密文解密,显示明文。

注意:
密码保护内的内容和密码不要太复杂,内容有代码什么的可能会解密出乱码。
所有的密文和密码都是在客户端的网页内容内的,有心的人很容易就能获取密文,再懂点电脑知识的话很容易破解出明文,所以这只是一个蒙骗对电脑不太懂得人的方法...

  1. 话说要不要加一个允许默认展开的参数?(逃

  2. 来挖个坟,用了这个之后发现会有个未闭合的''标签,然后犯强迫症了……

    把函数关联注销掉之后发现 WordPress 输出的是
    ‘《p》[/collapse]《/p》’

    然后拼合的时候用的是’.$content.‘……汗

    Edit: 评论的尖括号标签是没被转义还是直接被丢了啊……