Element DOM Tree jQuery plugin – Firebug like functionality | RockingCode

Element DOM Tree jQuery plugin – Firebug like functionality

110

Rate this

Have you even seen G+ Feedback page style? It works pretty much like firebug, when you find something working work, you an ordinary user, will point and click which element is buggy so they will know what they need to do.

Here we’ll see a plugin that allows you to do such functionality. It gives you firebug-like selectors but with one really big advantage, it returns the correct element tree, so you’ll know exactly which element is broken. For instance, if you have a bug report on “#content p” you may never know that actually the broken paragraph was the 10th one.

And we’ll learn a little bit about DOM tree and how to get things easily done without messing around with jQuery (when we don’t need it, of course).

 

Demo & Download

I’ve done a simple test page so you can see how this DOM tree thing works. See the demo or download our sample files so you can easily play and try new things.

DOM tree? What?

No, I’m not talking about this tree.

DOM (Document Object Model) defines a standard way to access and manipulate HTML elements. So window.document means same thing for every browser. It works pretty much like a tree (duh!), where you have parent and children for every element (unless the first and last one, or we’ll enter in an infinite loop).

When you type $(“body”) you are actually saying “Hey jQuery, get this window.document.body element and bring it here in a way that we can manipulate it”. As you can imagine we can actually access and modify things directly via JavaScript, without jQuery. Every “node” stores info about itself and its children, so with a couple of lines we can access precious data about it!

Let’s go through our plugin, now.

HTML & JS

We’ll use jQuery Boilerplate and BootStrap. Both tools help us a lot here, and I think you should be using them

After putting a simple dummy content, we’ll get started with functionality.

Activate & De-activate functions

I think it’s not that good have this thing enabled all the time, so we’ll create a simple button and bind clicks on it:

1
<a href="#" id="elemtree" class="btn">Start debugging!</a>
1
2
3
4
5
6
7
8
<script type="text/javascript">
    $(function(){
        $("#elemtree").click(function(event){
            event.preventDefault();
            $("#container").eltree();
        });
    });
</script>

Now once plugin is activated all other click functions should be disabled. So what we’ll do is to add this to our plugin file:

1
2
3
4
5
6
7
8
9
10
11
12
Plugin.prototype.init = function () {
    var obj = this.obj,
        $this = "",
        debugDiv = "",
        debugCss = "",
        debugJS = "",
        debug = this.options.debug;
 
    obj.addClass("debugging").click(function(event){
        event.preventDefault(); // so links wont be opened while debugging
    });
 };

As you may notice we have a couple of variables there. One really important is the “debug”, since it is our debugging panel. It’s default value is:

1
2
3
defaults = {
    debug: "<div id='eltreeDebug'><span id='eltreeCSSSel'></span><span id='eltreeJSSel'></span><a href='#' class='btn primary' id='eltreeStop'>Stop debugging</a></div>"
 };

So, when you call it you create that bigger panel, CSS panel, JS panel and disable button. Disable function will be the easiest one, we’ll just reload page since disabling binds can be bad in some browsers (try enabling / disabling binds 100 times and you’ll know what I’m talking about).

1
2
3
4
//prepare remove functions
$("#eltreeStop").click(function(){
    window.location.reload(); // Instead of unbinding everything, just reload page. Now, IE, you can take a deep breath
});

Finding the element’s DOM tree

Now to understand our logic you must to know about 2 DOM accessing functions:

  • parentNode – When #content is your current element, for instance, you parentNode is “body”. It goes up one level on DOM tree
  • childNodes – Again, if your current element is #content childNodes will be all its children (h2, p, blockquote..). It is an array of elements

Our code logic will be:

  • User clicks element
  • Loop starts; Condition: current element isn’t window.element
    • create a parent var
    • check for all child nodes of this parent var, which one is our element
    • now if this element has id, store it, otherwise if it has class, store it,otherwise just get his tagname
    • element’s parent will be our current element

Ok, let’s translate it to JS and put a few other variables to store values that we’ll need:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
function selectors(elem) {
    var css = "",
        continueCss = 1,
        js = "",
        parent = "",
        ret = new Array();
 
    while (elem !== window.document) {
        parent = elem.parentNode;
 
        //js selector
        x=0;
        while(  ( $(parent.childNodes[x])[0] !== elem ) && (x < parent.childNodes.length) ) {
            x++;
        }
        //now we have our childNode!
        js = x + "," + js;
 
        //CSS selector
        if (continueCss) {
            if(elem.id) {
                css = elem.nodeName + '#' + elem.id + " " + css;
                continueCss = 0;
            } else if(elem.className) {
                css = elem.nodeName + '.' + elem.className + " " + css;
            } else {
                css = elem.nodeName + " " + css;
            }
        }
        //let's go up one level
        elem = elem.parentNode;
    }
    //let's make our js selector useful
    js = (js.slice(0, -1)).split(",");
    for(x=0; x< js.length; x++) {
        js[x] = "childNodes[" + js[x] + "]";
    }
    js = "window. document. " + js.join(". ");
 
    ret[0] = css.toLowerCase(); //css
    ret[1] = js; //js
    return ret;
}

Output function

If you’re planning to use it as a feedback system, it’s these lines that you should change. After we have our selectors this function just display them in our debug panels:

1
2
3
4
5
6
function logThis(elem, css, js ) {
    var sel = selectors(elem);
    //if you want to do something else with results (like sending to a feedback plugin) add stuff here
    css.text( sel[0] );
    js.text( sel[1] );
}

Final result!

This is our final plugin code, check it out:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/*
 *  Project: Rocking Element Tree (a.k.a. eltree)
 *  Description: Adds a firebug-like functionality on page, that gives you element tree and properties by clicking on it
 *  Author: Rochester Oliveira
 *  License: GNU General Public License ( http://en.wikipedia.org/wiki/GNU_General_Public_License )
 */
 
// awesome structure from http://jqueryboilerplate.com/
;(function ( $, window, document, undefined ) {
    // Create the defaults once
    var pluginName = 'eltree',
        defaults = {
            debug: "<div id='eltreeDebug'><span id='eltreeCSSSel'></span><span id='eltreeJSSel'></span><a href='#' class='btn primary' id='eltreeStop'>Stop debugging</a></div>"
        };
 
    // The actual plugin constructor
    function Plugin( element, options ) {
        this.element = element;
 
        this.options = $.extend( {}, defaults, options) ;
 
        this._defaults = defaults;
        this._name = pluginName;
 
        this.obj = $(this.element);
 
        this.init();
    }
 
    Plugin.prototype.init = function () {
        var obj = this.obj,
            $this = "",
            debugDiv = "",
            debugCss = "",
            debugJS = "",
            debug = this.options.debug;
 
        //add our debugger box and cache
        debugDiv = $(debug).appendTo($("body"));
        debugCss = debugDiv.children("#eltreeCSSSel");
        debugJS = debugDiv.children("#eltreeJSSel");
 
        //prepare remove functions
        $("#eltreeStop").click(function(){
            window.location.reload(); // Instead of unbinding everything, just reload page. Now, IE, you can take a deep breath
        });
        //now we'll add those logger functions
        obj.addClass("debugging").click(function(event){
            event.preventDefault(); // so links wont be opened while debugging
            logThis( event.target, debugCss, debugJS ); //and let's add this to our logger spans
        });
    };
    function logThis(elem, css, js ) {
        var sel = selectors(elem);
        //if you want to do something else with results (like sending to a feedback plugin) add stuff here
        css.text( sel[0] );
        js.text( sel[1] );
    }
    function selectors(elem) {
        var css = "",
            continueCss = 1,
            js = "",
            parent = "",
            ret = new Array();
 
        while (elem !== window.document) {
            parent = elem.parentNode;
 
            //js selector
            x=0;
            while(  ( $(parent.childNodes[x])[0] !== elem ) && (x < parent.childNodes.length) ) {
                x++;
            }
            //now we have our childNode!
            js = x + "," + js;
 
            //CSS selector
            if (continueCss) {
                if(elem.id) {
                    css = elem.nodeName + '#' + elem.id + " " + css;
                    continueCss = 0;
                } else if(elem.className) {
                    css = elem.nodeName + '.' + elem.className + " " + css;
                } else {
                    css = elem.nodeName + " " + css;
                }
            }
            //let's go up one level
            elem = elem.parentNode;
        }
        //let's make our js selector useful
        js = (js.slice(0, -1)).split(",");
        for(x=0; x< js.length; x++) {
            js[x] = "childNodes[" + js[x] + "]";
        }
        js = "window. document. " + js.join(". ");
 
        ret[0] = css.toLowerCase(); //css
        ret[1] = js; //js
        return ret;
    }
    // A really lightweight plugin wrapper around the constructor, preventing against multiple instantiations
    $.fn[pluginName] = function ( options ) {
        return this.each(function () {
            if (!$.data(this, 'plugin_' + pluginName)) {
                $.data(this, 'plugin_' + pluginName, new Plugin( this, options ));
            }
        });
    }
})(jQuery, window, document);

What do you think?

Don’t be shy and share your thoughts with us

Element DOM Tree jQuery plugin – Firebug like functionality | RockingCode的更多相关文章

  1. How do I pull a native DOM element from a jQuery object? | jQuery Learning Center

    How do I pull a native DOM element from a jQuery object? | jQuery Learning Center How do I pull a na ...

  2. [Javascript] Deep Search nested tag element in DOM tree

    // For example you want to search for nested ul and ol in a DOM tree branch // Give example <ol&g ...

  3. jQuery plugin : bgStretcher 背景图片切换效果插件

    转自:http://blog.dvxj.com/pandola/jQuery_bgStretcher.html bgStretcher 2011 (Background Stretcher)是一个jQ ...

  4. Signs of a poorly written jQuery plugin 翻译 (Jquery插件开发注意事项,Jquey官方推荐)

    原文链接:http://remysharp.com/2010/06/03/signs-of-a-poorly-written-jquery-plugin/ 原文作者:remy sharp So far ...

  5. The ultimate jQuery Plugin List(终极jQuery插件列表)

    下面的文章可能出自一位奥地利的作者,  列出很多jQuery的插件.类似的网站:http://jquerylist.com/原文地址: http://www.kollermedia.at/archiv ...

  6. transfered jquery plugin practice!

    jQuery插件开发入门 发表于 2014年05月23日 by 天涯孤雁 被浏览 20,897 次 ======2014-5-27 更新======= Require.js中使用jQuery 插件请查 ...

  7. JQuery plugin ---- simplePagination.js API

    CSS Themes "light-theme" "dark-theme" "compact-theme" How To Use Step ...

  8. [jQuery] 自做 jQuery Plugin - Part 1

    有時候寫 jQuery 時,常會發現一些簡單的效果可以重複利用.只是每次用 Copy & Paste 大法似乎不是件好事,有沒有什麼方法可以讓我們把這些效果用到其他地方呢? 沒錯,就是用 jQ ...

  9. ollicle.com: Biggerlink – jQuery plugin

    ollicle.com: Biggerlink – jQuery plugin Biggerlink – jQuery plugin Purpose Demo Updated for jQuery 1 ...

随机推荐

  1. Android RelativeLayout常用属性介绍

    下面介绍一下RelativeLayout用到的一些重要的属性: 第一类:属性值为true或false android:layout_centerHrizontal 水平居中 android:layou ...

  2. Extjs springmvc session 超时 处理

    如果你的项目使用ExtJS作为表现层,你会发现,SESSION超时控制将是一个问题.本文将就自己的经验,来解决这一问题,当然,解决问题并非只有一种方法,我只是提出我的方法.首先,做超时控制,必需使用过 ...

  3. hdu 2604Queuing dp+ 矩阵快速幂

    题目链接 给一个长度为n的字符串, 每个字符可以使f或m. 问你不包含子串fmf以及fff的字符串数量有多少. 令0表示mm结尾, 1表示mf, 2表示ff, 3表示fm. 那么 f(n+1, 0) ...

  4. poj 1769 Minimizing maximizer 线段树维护dp

    题目链接 给出m个区间, 按区间给出的顺序, 求出覆盖$ [1, n] $ 至少需要多少个区间. 如果先给出[10, 20], 在给出[1, 10], 那么相当于[10, 20]这一段没有被覆盖. 令 ...

  5. hdu 2685 I won't tell you this is about number theory 数论

    题目链接 根据公式 \[ gcd(a^m-1, a^n-1) = a^{gcd(m, n)}-1 \] 就可以很容易的做出来了. #include <iostream> #include ...

  6. 百度地图Label 样式:label.setStyle

    创建文本标注对象设置样式的时候,其中的backgroundColor属性居然还支持透明啊,不过改变数值好像对效果没有影响 var numLabel = new BMap.Label(num); num ...

  7. Delphi中的Rtti函数

    TTypeKind,类型类别,tkclass,tkinteger,tkstring等.类,属性都是一种类型. ttypedata,是一个record包括ttypekind.是一个类的描述.TTypeK ...

  8. Unix/Linux环境C编程入门教程(5) Red Hat Enterprise Linux(RHEL)环境搭建

    Unix/Linux版本众多,我们推荐Unix/Linux初学者选用几款典型的Unix/Linux操作系统进行学习. 通过./a.out ./Y.out执行出结果,证明C++程序编译成功,也就说明li ...

  9. Numpy之ndarray与matrix

    1. ndarray对象 ndarray是numpy中的一个N维数组对象,可以进行矢量算术运算,它是一个通用的同构数据多维容器,即其中的所有元素必须是相同类型的. 可以使用array函数创建数组,每个 ...

  10. twisted的一些代码

    之前用swoole(1.7.19)写的一段程序在数据量大的时候存在内存泄漏,改为twisted(15.4)实现,自测无误,记录如下(两者cpu占用率90%时吞吐rps能从120提升到1000). #! ...