Drupal 7:Nivo Slider模块报错的问题

Nivo Slider是Drupal的一个制作幻灯片的模块,效果比views slideshow好得多

今天在尝试安装使用这个模块的时候却颇费了一番功夫。

使用Nivo Slider需要安装以下几个模块:

  1. Nivo Slider模块本身,这个不用说;
  2. Libraries API,相当多的模块依赖这个库,应该都已经装了;
  3. Nivo Slider jQuery plugin,这个是Nivo Slider的一些界面动画所依赖的jQuery库;
  4. jquery_update,这个是使当前jQuery库升级到最新版的模块;

问题

问题就出在jquery_update这个模块上,启用这个模块后,我的business主题二级菜单失效了

看了一下浏览器的log,是business/js/superfish.js报错:

image11

探究

打开superfish.js看看:

image12

顶部有版权声明和开源许可,貌似是一个使用jQuery的开源组件?

因为是启用了jquery_update模块才报错的,推测是有可能它使用的jQuery版本与最新版不符导致的。

去网上搜了一下,果然有Superfish这个组件,最新版是1.9

下载下来最新的Superfish.js复制进business/js覆盖,菜单栏果然没问题了。

image13

接下来用Nivo Slider设置首页幻灯片,可是图片是显示,就是不动啊,看下log:

image14

看来还是jQuery的问题,又百度了一下这个错误提示

找到了这个:http://blog.csdn.net/dracotianlong/article/details/18195141

jQuery 1.7版本以后,live接口被删除了,必须换成on

解决

按照错误提示找到sites/all/nivo-slider/jquery.nivo.slider.pack.js

打开,把里面的两处live()改成on(),保存、刷新页面。

bingo,问题解决!

Drupal 7:如何将Custom Search模块水平摆放

项目需要用到自定义搜索框,Drupal自带的区块只能显示一个简单的搜索框,进入到搜索页面才能进行高级搜索。

Custom Search 可以在提供比系统自带搜索区块更多的功能,如下图:

image8

问题

Custom Search 有很多设置选项,标签、显示的文字啊什么的,

美中不足的是,它的排版是垂直排列的,而项目要求这三个元件水平排列,在同一行显示,

探究

我在设置里并没有找到可以水平排列的选项,粗粗看了一眼它的模版文件,也没找到代码位置

那就只好祭出万能的 CSS 修改大法了!

  1. 这三个元件的父div靠右对齐
  2. 文本框左移,分类框上移、左移,搜索按钮上移
.region-header{
  text-align: right;
  margin-bottom: -40px;
}

.form-item-custom-search-blocks-form-1 {
  margin-right: 150px;
}

.form-item-custom-search-types {
  margin: -42px 49px 0 0;
}

#edit-submit {
  margin-top: -28px;
}

效果图

image9

还有问题

但是有个问题,注意到我修改“搜索”按钮的时候是用的id定位的:#edit-submit

后来发现了一些问题,在很多页面——比如文章的编辑页面有别的提交按钮,这个时候这个搜索按钮的id会变成edit-submit--2等,可是它的class也是跟别的提交按钮共用的。跟文本框和分类框不一样,Custom Search并没有给这个按钮自定义class名,我无法精确定位搜索按钮。

继续探究

想了一想,CSS好像有属性选择器,试一下:

input[value=搜索] {
  margin-top: -28px;
}

貌似可以了,嗯,输入文字点击,进到高级搜索和搜索结果页面:

image10

摔!所有的搜索按钮都跟着上移了….

在这个问题上卡了半天,一直在琢磨怎么定位这个搜索按钮…

解决

突然灵光一闪,CSS是不是还有个兄弟元素选择器啊?

试试看:

.form-item-custom-search-types+.form-actions {
  margin-top: -28px;
}

bingo!完美!

…好吧,我承认这样写代码有点傻….不过好歹问题解决了!~

最后的最后

update 2014-4-25 :

果然有点傻,把三个框全部设display: inline就行了….

Drupal 7:PHP输出的页面多出空行的问题

最近接到一个活,是用Drupal7搭一个网站,实现简单的分角色发布文章的功能,架构是XAMPP+PHP+MySQL+Drupal7。 以下是我在实际项目中遇到的一些问题和解决办法。

问题

我下载了一个Drupal 7的主题,然后覆写它的模板,以实现自定义布局。 覆写完以后发现,页面上方会多出一栏空行。

正常的网页:

image1

不正常的网页,顶部多了一行空行:

image2

探究

F12审查元素看看:

image3

这让我百思不得其解啊,拿修改前的和修改后的文件一行一行的对比,完全没有问题啊!

image4

然后突然看到了的底下状态栏,两个文件好像不一样:

image5

解决

UTF-8 with BOM是什么东西?试着改回成UTF-8试试,改回来果然就好了!

原因

百度了一下,UTF-8 BOM:

BOM——Byte Order Mark,就是字节序标记

在UCS 编码中有一个叫做”ZERO WIDTH NO-BREAK SPACE”的字符,它的编码是FEFF。而FFFE在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输 字符”ZERO WIDTH NO-BREAK SPACE”。这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little- Endian的。因此字符”ZERO WIDTH NO-BREAK SPACE”又被称作BOM。

UTF-8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符”ZERO WIDTH NO-BREAK SPACE”的UTF-8编码是EF BB BF。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。

UTF- 8编码的文件中,BOM占三个字节。如果用记事本把一个文本文件另存为UTF-8编码方式的话,用UE打开这个文件,切换到十六进制编辑状态就可以看到开 头的FFFE了。这是个标识UTF-8编码文件的好办法,软件通过BOM来识别这个文件是否是UTF-8编码,很多软件还要求读入的文件必须带BOM。可是,还是有很多软件不能识别BOM。

在Firefox早期的版本里,扩展是不能有BOM的,不过Firefox 1.5以后的版本已经开始支持BOM了。现在又发现,PHP也不支持BOM。PHP在设计时就没有考虑BOM的问题,也就是说他不会忽略UTF-8编码的文件开头BOM的那三个字符。 ~~~

果然,PHP是不支持BOM的,会把BOM头显示成一个空字符。

为什么我会把修改过的文件保存成有BOM的UTF-8呢?

想到项目刚开始的时候我用的是Notepad++,后来用的是Sublime Text 3,会不会是编辑器的问题?

果不其然,Notepad++的保存格式是这样的:

image6

Sublime Text 3的保存格式是这样的:

image7

摔!两个编辑器默认的UTF-8根本不是一个东西啊!

从此成了Notepad++一生黑!!!

jQuery Mobile:JS绑定的按钮动作不正确的问题

在项目过程中,需要一组图片设置长按响应:长按弹出一个对话框,点击确认删掉当前图片,然后刷新页面。

在最开始的时候,我是这样写的:

for(i = 0; i < bookNum; i++){
    (function(){
        var index = i;
        var bookHref = $('a#book_'+index);
        bookHref.bind("taphold", {index: index}, tapholdHandler);
    })();
}

function tapholdHandler(event) {
    $("#dialogDelete").popup("open");
    var dialogCommit = $('#dialogCommit');
    dialogCommit.click(function(){
        //删除书,然后刷新页面。
    });
    $('#dialogCancel').click(function(){
        $("#dialogDelete").popup("close");
    });
}

然后发现,第一次长按删除的时候是正常的,第二次开始就乱七八糟的,多删了很多东西。

通过各种输出log调试,发现删除第一张图片的时候正常,删除第二张图片的时候会连续删两张,删除第三张图片的时候会连续删三张……

探究

这就让我有点怀疑,是不是每次刷新页面的时候注册监听的问题?

百度了一下,果然如此,跟Java不同,JavaScript给界面元素绑定事件并不会替换原来绑定过的事件,而是会重复绑定。

还是上一节说的问题,Java的机制是“设置监听”,JavaScript的机制是“绑定事件”。 明白这一点后就好解决了。因为我删掉一张图片以后会刷新一下页面,即会重复绑定一次。

解决

解决办法就是在每次bind()绑定之前,先用unbind()解绑一次。

for(i = 0; i < bookNum; i++){
    (function(){
        var index = i;
        var bookHref = $('a#book_'+index);
        bookHref.unbind("taphold",tapholdHandler);
        bookHref.bind("taphold", {nid: json_books[index].nid},tapholdHandler);
    })();
}
function tapholdHandler(event) {
    $("#dialogDelete").popup("open");
    var dialogCommit = $('#dialogCommit');
    dialogCommit.unbind("click");
    dialogCommit.click(function(){
        //删除书,然后刷新页面
    });
    $('#dialogCancel').click(function(){
        $("#dialogDelete").popup("close");
    });
}
jQuery Mobile:JS批量设置按钮动作的问题

刚刚接触JavaScript,用的是JQuery Mobile框架,很多东西还不熟悉,写的代码难免会想当然得用写Java和Android时的惯性思维。

问题

今天遇到一个问题,我动态生成了若干个按钮,id命名为Comment_1Comment_2……在设置按钮监听的时候,由于按钮总数是无法事先知道的,所以不可能为每一个按钮写一个监听,于是我用了循环来重复设置监听,代码如下:

for(i = 0; i < commentNum; i++){
    $('a#Comment_'+i).click(function(){
        sessionStorage.curComment = comments[i].cid;
        $.mobile.changePage("commentInfo.html");
    });
}

然而,在测试代码的时候发现,第3行一直报错:

Uncaught TypeError: Cannot read property 'cid' of undefined

comments[i]未定义。在中间加了一个log输出i,发现点击任何一个按钮,i的值都是commentNum

这种写法在Java和Android里是正确的,但是为什么在JS里不对了呢?

探究

Google了一下,大致明白了一点点。

在Java中,UI的界面事件(比如按钮按下、触摸、滑动)是由虚拟机进行监听的,监听到了点击动作,就一层一层向下传递,直到被按钮接收到,然后进行处理。在Java/Android中这个监听是需要提前向虚拟机进行“注册”的,下面是Android中典型的循环设置监听的代码:

Button[] buttons = {btn1,btn2,btn3,btn4,btn5};
for(int i = 0; i < buttons.length; i++){
    buttons[i].setOnClickListener(new OnClickListener(){
        @Override
        public void onClick(View v) {
            //do something
        }
    });
}

这段代码在UI界面生成完毕前运行,for循环每循环一次,buttons数组中就有一个按钮都被设置了监听,直到全部设置完毕。UI界面生成完毕后,用户点击每个按钮就会触发对应的动作。

然而,与Java不同,JavaScript并没有类似的监听机制,而使用的是看上去很类似的“事件绑定”机制。当一个按钮被按下的时候,click()内的代码才会被执行,而一般用户按下按钮的时候,js内的代码都已经执行完毕了,For循环也早已经循环完毕,这个时候i的值为CommentNum,执行comments[i]就会报错——i超过了数组下标范围。

解决

那么怎么才能给这些按钮设置监听/绑定呢?如果我们仍然要用For循环进行绑定的话,可以使用以下代码:

for(i = 0; i < commentNum; i++){
    (function(){
        var index = i;
        $('a#Comment_'+index).click(function(){
            sessionStorage.curComment = comments[i].cid;
            $.mobile.changePage("commentInfo.html");
        });
    })();
}

这里利用了JS的闭包原理,每次循环都会新建一个闭包,里面的变量值(在这里是index的值)在运行完毕后并不会被销毁,而是一直保存(参考:JS闭包的作用原理),当你按下按钮时,click()被触发,这个时候index就是其对于的id号了。

PS:其实并不推荐这样使用循环绑定事件,过多的闭包会影响性能,最好是用js的事件冒泡机制来实现,有兴趣的可以研究一下。