Visual Firebug V1.0发布

Visual Firebug是一个编辑在线页面样式的chrome插件。它的产生是为了让一些不会使用firebug的视觉、交互、运营等同学能够快速、方便地修改在线页面。

当前版本:1.0

下载:https://chrome.google.com/extensions/detail/lomkpheldlbkkfiifcbfifipaofnmnkn?hl=zh-CN

内容索引

  1. 使用方法
    1. 开启插件
    2. 选择节点
    3. 编辑节点样式
    4. 获取节点样式
    5. 保存整页的修改结果
  2. 所有功能介绍
  3. 现有控件
    1. 列表项符号
    2. 字号
    3. 行高
    4. 字体
    5. 字形,包括加粗、斜体、下划线
    6. 字色
    7. 对齐方式
    8. 文字阴影
    9. 背景色
    10. 盒阴影
    11. 边框,包括边框及圆角
    12. 布局,包括内边距及外边距
    13. 宽高,包括宽度及高度

一、使用方法

1、开启插件

开启插件

点击chrome工具条上的图标即可,待插件加载完毕后,会在当前Tab的页面内插入面板,并默认自动开启元素选择功能。

2、选择节点

选择节点

当鼠标移入某个节点时,该节点周围会出现蓝色边框,直接点击该点击,则选中该节点作为待编辑节点。

在节点上右键鼠标则会唤出与该节点相关的其它节点列表,鼠标移出这些列表项后,页面上同样将以蓝色边框的方式显示该列表项所对应的节点,如果该节点没有相关的其他节点列表,则会显示”no-selector”,并且该提示会在2秒钟后消失。相关节点列表包括”Tag选择”及”Class选择”两种方式,”Tag选择”将选中与该节点类型相同的节点,即如果当前节点为一个表格单元格(td),则会选中该单元格所在表格内的所有单元格;而”Class选择”将选中整个页面中拥有该class的所有节点。

选择完节点后,在面板的左上方将出现当前待编辑节点的类型及数量。

3、编辑节点样式

编辑节点

通过面板上的控件编辑节点样式。

4、获取节点样式

获取样式

点击面板右上角”查看样式”按钮,下方将展开一个文本域,显示当前编辑节点的样式,在修改样式的过程中,该区域的样式将即时更新。并且该区域中的样式都进行了缩写处理,运营同学拿到后即可在cms中写入节点的style的属性。

5、保存整页的修改结果

保存整页的修改结果

点击面板右上角”获取修改结果”按钮,页面的左上方将出现一个浮层,显示”loadding”,待加载完之后,该浮层将变大撑满整个屏幕,浮层内的内容正是修改后的html代码,复制代码到一个文本文件,并另存为”xxx.html”即可。视觉、交互同学对某些网页进行微调时,不必再打开Photoshop、Axure等工具,直接打开本工具编辑即可,保存后即可丢给前端,前端看到效果后即可查看代码,在工具生成的代码的基础上code。

二、所有功能介绍

具体功能介绍

三、现有控件

  1. 列表项符号

    列表项符号

  2. 字号

    字号

  3. 行高

    行高

  4. 字体

    字体

  5. 字形,包括加粗、斜体、下划线

    字形,包括加粗、斜体、下划线

  6. 字色

    字色

  7. 对齐方式

    对齐方式

  8. 文字阴影

    文字阴影

  9. 背景色

    背景色

  10. 盒阴影

    盒阴影

  11. 边框,包括边框及圆角

    边框,包括边框及圆角

  12. 布局,包括内边距及外边距

    布局,包括内边距及外边距

  13. 宽高,包括宽度及高度

    宽高,包括宽度及高度

模块化

ghostzhang在CSS森林上发表了6篇关于模块化的文章,详细介绍了基类扩展类的概念和用法。下面我要说的内容是对这一系列文章的补充和完善,简单地浏览一下那6篇文章能帮助你更好地理解,以下代码很好地总结了这些文章的主要内容:


/**
  * @name: mode_name
  * @author: ghostzhang
  * @version: 1.0
  * @type: 基类
  * @explain: Demo
  */
.mode_name{...}
.mode_name h2{...}
.mode_name .cont{...}
/* @end **/
/**
  * @name: mode_name_b
  * @author: ghostzhang
  * @version: 1.0
  * @type: 扩展类
  * @explain: Demo
  * @dependent: mode_name
  */
.mode_name_b{...}
.mode_name_b h2{...}
.mode_name_b .cont{...}
/* @end **/
/* 针对单个模块的个性化定义 */
.mode_xxxx{...}
.mode_xxxx h2{...}
.mode_xxxx .cont{...}

相信各位参与项目开发的同学遇到相似语义或者相似样式的模块时,都会在代码中体现出基类扩展类的概念,以上代码似乎只是一种实践方式的概念提炼。并且OOCSS所提出的面向对象的概念,也将所谓的从功能模块细化为了页面元素,比如所有的标题都继承.title类,所有的注释都继承.note类,然后在模块级基类中去覆盖这些元素级基类,如.mode_name .title{...}。而这样的细化将带来以下问题:

全局项目页面三种样式作用域的混乱。因为缺少了模块基类后(如以上代码中的.mode-news.mode-links…),各种对象基类(如.title.tag…)就会暴露在外,而全局会有全局的通用元素,项目会有项目级的通用模块,项目中的各个页面同样也会有一些通用模块,这样就很容易产生作用域间的混乱。你不知道.title类的作用域是全局的还是项目的还是页面的;你不知道它们在哪个页面被用过;过了一段时间后你甚至不知道有没有定义过.title这个类,遇到一个标题元素的时候不知道是不是用现成的类可以用……为此我们需要一些显示的声明来处理类的作用域被调用关系等问题。


/*全局:*/
.K2-note{…}
.K2-title{…}
/*项目:*/
.project-note{…}
.project-title{…}
/*页面:*/
#modify-account .note{…}
#modify-account .title{…}

最简单直接的方式就是通过类名前缀来区分不同的作用域,比如口碑网的全局类都以k2为前缀;项目的前缀可以用project,甚至以具体的项目id来区分,比如团购项目的标题元素用.tuan-title;对于页面级的类,建议使用php等服务端语言来搭建本地开发环境,在做demo时,为每个页面配置一个唯一的id,该id既是该页面的文件名也是该页面body元素的id名,然后通过该id去定义元素样式,而不像ghostzhang一样通过一个唯一的类名来区分。

以上方式运用于模块,与基类扩展类结合后(以项目级为例)


/**
  * @name: project_mode_prompt
  * @author: zhouqi
  * @version: 1.0
  * @type: 基类
  * @explain: 操作提示模块
  * @caller:add_pic\modify_pic
  */
.project_mode_prompt{...}
.project_mode_prompt h2{...}
.project_mode_prompt .cont{...}
/* @end **/
/**
  * @name: project_mode_prompt_b
  * @author: zhouqi
  * @version: 1.0
  * @type: 扩展类
  * @explain: 操作提示模块
  * @dependent: project_prompt_name
  * @caller:del_pic
  */
.project_mode_prompt_b{...}
.project_mode_prompt_b h2{...}
.project_mode_prompt_b .cont{...}
/* @end **/
/* 针对modify-account页面的个性化定义 */
#modify-account project_mode_prompt{...}
#modify-account project_mode_prompt h2{...}
#modify-account project_mode_prompt .cont{...}

这里有2个改进,一是为模块增加了显式的作用域声明即类名前缀,开发者及维护者一看到该类名就知道它是一个什么作用域的类;二是在类注释中声明了caller属性,即表明该类被哪些页面调用,这样当修改了该基类后就知道要去哪些页面检查样式是不是乱了。

相关资源

css3 button maker

Demo:

站在别人的肩上

基于button maker

改进:

  • 增加text-shadow、box-shadow、border等css属性
  • 增加:active伪类支持
  • 增加IE hack,实现IE优雅降级
  • 生成样式时,剔除不需要的代码,如:hover时样式和正常状态一样、:active时和:hover时样式一样,这些情况下样式的重复定义都是没有意义的,剔除之。
  • 通过fixed定位,代替js位置计算
  • 增加中文字体集
  • 0px转化为0

需要改进的:

  • 目前只支持两种色值的渐变,3种较为理想,并且不支持自定义渐变的方向
  • IE最好有更少的降级
  • 拖动条最好用html5的range控件来实现
  • 脚本可以再完善

价值:

接手“前端和视觉之间的故事”这个团队内部的项目已经有一段时间了,一直没有找到一个让视觉同学了解前端可以做什么前端有什么新技术的好方法,一旦这个做不好,让视觉协助我们前端做一些渐进增强的东西就很困难。

无意中我看到了Chris Coyier的button maker,我想这是一个有效解决问题的好方法,视觉用我们提供的工具进行设计,设计完成后,前端的代码也完成了,太棒了。为此我在征得Chris Coyier的同意后,借鉴了他的代码,进行了如上提到的改进,给视觉看后,似乎很认可,希望能真的运用到设计中去。

用:visited窥探用户访问历史

更新

  1. window.getComputedStyle(node,pseudo)[property]不能取:visited的值,这里的“pseudo”应该是”Pseudo-Element(伪元素)”而不是“伪类”。

起因

mozilla发布了firefox4 beta 1,更新了一些CSS属性,不知道你有没有注意到其中有一条关于:visited伪类的更新,为了保护用户的隐私,限制绝了绝大多数的css样式应用到:visited元素上,这其中就包含了一些我们常用于访问过链接的CSS属性,比如backgroundborder-colorfont。比如我的博客,对访问过的链接,就会用content属性生成一个勾,提示读者这个链接他已经访问过了。我尝试了几个常用的CSS属性,只有color依然有效。

既然color仍然有效,不知道mozilla这样做有什么意义?在这个越来越注重可用性的时代,不考虑:visited样式绝不是一个好的做法,说句题外话,各位设计师们应该在设计中考虑更多的css伪类样式,而不局限于:hover,按下一个按钮、表单获得焦点这些动作没有反馈是一件令人沮丧的事。言归正传,限制那么多的CSS属性运用于:visited将大大降低设计师的创作空间。访问后变色本身又是一个不受大多数人喜欢的标识方法,a,a:visited{color:XXX;}是绝大多数网站的做法。只能用两个字形容我的现在的心情:“我操”。

如何窥探

我没有去查这个隐私问题到底是怎么发生的,我的猜测就是用脚本去遍历链接,判断它的:visited样式有没有生效,生效了就说明访问过了。这里原理类似我的这篇文章:Browser Detector With CSS真想问问mozilla为什么不从js下刀呢,为什么不去限制window.getComputedStyle(node,pseudo)[property]:visited的值?IE知道这事笑了,“哈哈,我不支持用js取伪类样式!”。[1]基本代码如下,请忽视转换色值的函数:

[Demo]用firefox4看看是不是只有颜色生效了? [Demo]看看你访问过哪里?


a:visited{color:#f00;}

var Color = {
    "toHex" : function(rgb){
        var a = rgb.replace(/rgb\(|\)/g,"").split(","),
              hex = "";
        for (var i = 0;i < 3;i++){
            var b = parseInt(a[i]).toString(16);
            hex += (b.length === 1)?"0"+b:b;
        }
        return "#" + hex;
    },
    "toComplate" : function(hex){
        if (hex.length === 4){
            var hex = hex.toLowerCase(),
                  newHex = "";
            for (var i = 0;i < 3;i++){
                var a = hex.substring(i+1,i+2);
                newHex += a + a;
            }
            return "#" + newHex;
        }else{
            return hex;
        }
    }
};
function getColor(node,property){
    if(node.currentStyle){
        return Color.toComplate(node.currentStyle[property]);
    }
    if(window.getComputedStyle){
        return Color.toHex(window.getComputedStyle(node,null)[property]);
    }
}
var links = document.getElementsByTagName("a");
for(var i = 0,j = links.length;i<j;i++){
    var color = getColor(links[i],"color");
    if(color === "#ff0000"){
        alert("你访问过:"+links[i].firstChild.nodeValue);
    }
}

iframe自适应高度

同域、子页面高度不会动态增加

这种情况最简单,直接通过脚本获取字页面实际高度,修改iframe元素高度即可。但有二点必须注意:

  1. 如果页面内有绝对定位或者没有清浮动的元素,情况有些复杂,不同浏览器处理结果不同,甚至包括Webkit内核的浏览器,具体请看这个Demo。所以你要么进行浏览器检测,要么用Math.max计算一个最大值,要么你想别的方法。
  2. iframe所包含页面可能非常大,需要很长的加载时间,为此直接计算高度的时候,很可能页面还没下载完,高度计算就会有问题。所以最好在iframeonload事件中计算高度。这里还要注意的是,IE下必须使用微软事件模型obj.attachEvent来绑定onload事件。而别的浏览器直接obj.onload = function(){}也可以。

(function(){
    var frame = document.getElementById("frame_content_parent"),
        setIframeHeight = function(){
            var frameContent = frame.contentWindow.document,
                frameHeight = Math.max(frameContent.body.scrollHeight,frameContent.documentElement.scrollHeight);
            frame.height = frameHeight;
        };
    if(frame.addEventListener){
        frame.addEventListener("load",setIframeHeight,false);
    }else{
        frame.attachEvent("onload",setIframeHeight);
    }
})();

同域、子页面高度会动态增加

原理与第一种情况一样,多一个计时器,一直检测字页面高度,当子页面高度和iframe的高度不一致时,重新设置iframe的高度。这边也可以加一个try在js出错时,加一个足够的高度。


(function(){
    var _reSetIframe = function(){
        var frame = document.getElementById("frame_content_parent")
        try {
            var frameContent = frame.contentWindow.document,
                bodyHeight = Math.max(frameContent.body.scrollHeight,frameContent.documentElement.scrollHeight);
            if (bodyHeight != frame.height){
                frame.height = bodyHeight;
            }
        }
        catch(ex) {
            frame.height = 1800;
        }
    }
    if(frame.addEventListener){
        frame.addEventListener("load",function(){setInterval(_reSetIframe,200);},false);
    }else{
        frame.attachEvent("onload",function(){setInterval(_reSetIframe,200);});
    }
})();

同域、子页面高度会动态增加、脚本可能完全失效

第二个例子中,考虑到了脚本出错的情况,但是万一脚本根本不执行了呢,那iframe中的内容就会因为iframe的高度不够而显示不了。为此我们通常事先设置一个足够的高度,为了前端控制方便,我觉得写在CSS文件中比较合适,需要修改时只改CSS就行了。这里我设置了selector{ height:1800px; }。需要注意的是,写在样式表里的样式,不能直接用node.style[property]来取,对于微软模型,要用node.currentStyle[property](题外话:悲剧的IE模型不支持CSS伪类),对于W3C模型,要用window.getComputedStyle(node,null)[property]来取。我这里图方便直接用了YUI。

这里又有一个问题,设置iframe的高度大于其包含页面的高度时,各个浏览器的处理不一样。例如在Firefox下,必须计算body元素的高度,而html元素的高度等于iframe的高度,然而当恰巧这个页面又有绝对定位未清浮动元素时,又不能通过body元素来取,显然第一种方法缺点更小一些。具体请看这个Demo

从上面这个Demo可以看到,除IE浏览器外,别的浏览器计算出来的都是iframe的高度,即CSS里设置的#frame_content_parent{ height:1800px; }。而IE计算出来的是iframe所引用页面的实际高度。


#frame_content_parent{ height:1800px; }

(function(){
    var $ = YAHOO.util.Dom,
        frame = $.get("frame_content_parent");
    function reSetIframe(){
        var frameContent = frame.contentWindow.document,
            bodyHeight = Math.max(frameContent.documentElement.scrollHeight,frameContent.body.scrollHeight);
        if (bodyHeight != $.getStyle(frame, "height")){
            $.setStyle(frame, "height", bodyHeight + "px");
        }
    }
    if(frame){
        $.setStyle(frame,"height","auto");
        setInterval(reSetIframe,300);
    }
})();

跨域

这里提供一个Iframe代理的方法,简单地说一下原理。假设有3个页面,分别是主页面A.html,字页面B.html,代理页面C.html。其中A与B是跨域的,而A和C是同域的。它们的关系:A包含B,B包含C。很显然A和B,以及B和C,因为跨域不能相互通信,而A和C同域,可以相互通信。为此我们就想到让C页面告诉A页面,B页面到底有多少高。因为B和C也是跨域的不能相互通信,所以想在C页面中,直接window.parent.document.body.scrollHeight这样是行不通的,所以我们只能让B页面自己计算自身的高度,然后通过某种方法告诉C页面,再由C页面告诉A页面。这里的一个方法就是在B页面生成一个Iframe节点,然后设置它的src属性,在这个地址上附加一个参数,即B页面计算出来的高度,然后C页面就可以通过window.location获取这个地址栏中的地址,提取出高度值,通过window.top找到A页面,设置A页面的Iframe的高度。基本的原理就是这样,看代码吧:

DEMO


//B页面脚本
//任务:计算其实际高度,然后生成一个iframe节点,将高度作为代理页面C的地址的一部分赋值给Src属性
(function(){
    var agent_iframe = document.createElement("iframe"),
        b_height = Math.max(document.documentElement.scrollHeight,document.body.scrollHeight);
    agent_iframe.src = "http://demo.zhouqicf.com/js/2010/iframe_height/agent_iframe_once.html#" + b_height;
    document.body.appendChild(agent_iframe);
    agent_iframe.style.display = "none";
})();

//C页面脚本
//任务:获取请求地址中的高度值,将其赋值给A页面的Iframe的高度
window.top.document.getElementById("frame_content_parent").height = parseInt(window.location.hash.substring(1),10);

跨域、字页面高度动态变化

这里结合了第2、第4两种方法,我的想法是在B页面通过一个计时器,不停计算B页面的高度,一但变化,马上修改iframe标签的src属性,而C页面也有计时器不断监听src的变化,改变Aiframe标签的高度。需要注意的是仅仅修改src属性后面的锚点值(如“#1234”),页面并不会刷新,不会重新请求,这也是在C页面增加计时器的原因。

DEMO


//B页面脚本
(function(){
    var getHeight = function(){
        return Math.max(document.documentElement.scrollHeight,document.body.scrollHeight);
    };
    var preHeight = getHeight(),
        agent_iframe;
    var createIframe = function(height){
        agent_iframe = document.createElement("iframe");
        agent_iframe.style.height = "0";
        agent_iframe.style.width = "0";
        agent_iframe.style.border = "none";
        agent_iframe.src = "http://demo.zhouqicf.com/js/2010/iframe_height/agent_iframe.html#" + height;
        document.body.appendChild(agent_iframe);
    }
    createIframe(preHeight);
    var checkHeight = function(){
        var currentHeight = getHeight();
        if(currentHeight != preHeight){
            agent_iframe.src = "http://demo.zhouqicf.com/js/2010/iframe_height/agent_iframe.html#" + currentHeight;
            preHeight = currentHeight;
        }
        setTimeout(checkHeight,500);
    }
    setTimeout(checkHeight,500);
})();

//C页面脚本
(function(){
    var preHeight = parseInt(window.location.hash.substring(1),10),
        ifrmae = window.top.document.getElementById("frame_content_parent");
    ifrmae.height = preHeight;
    setInterval(function(){
        var newHeight = parseInt(window.location.hash.substring(1),10);
        if (newHeight !== preHeight){
            ifrmae.height = newHeight;
            preHeight = newHeight;
        }
    },500);
})();

这里还有另一种方案,就是让iframe每一次都重新请求,这样C页面就不需要计时器了,但是如果2次计算高度重复的话,就会导致src属性的值相同,这样浏览器就很可能不重新请求该页面了,那么C页面中的脚本也就不运行了。要修复这个问题很简单,只要在每次计算出来的src属性上增加一个随机数的参数就行了。比如http://demo.zhouqicf.com/js/2010/iframe_height/agent_iframe.html?temp=123123423712937#1563


//B页面关键脚本
agent_iframe.src = "http://demo.zhouqicf.com/js/2010/iframe_height/agent_iframe.html?a=" + Math.random() + "#" + currentHeight;

//C页面脚本
window.top.document.getElementById("frame_content_parent").height = parseInt(window.location.hash.substring(1),10);