4
\$\begingroup\$

I am working with a lot of DOM manipulation and need to know several values of an element to determine its position within the DOM and I have written the following to help scrape several of the bits of information I need to proceed.

To me this looks very heavy and there might be some room to help improve the speed / accuracy of returning this information.

Any help in speeding this up or helping with the redundancy issues would be most helpful.

jsFiddle

$.fn.info = function(){
    var $el = this,
        obj = {
            _css: {},
            $win: $(window),
            $doc: $(document),
            x: Math.floor($el.offset().left),
            y: Math.floor($el.offset().top),
            w: Math.floor($el.width()),
            h: Math.floor($el.height()),
            tag: $el.prop("tagName").toLowerCase(),
            computedCss: function(){
                var css = ["width", "height", "float", "color", "font-size", "position", "top", "right", "bottom", "left", "margin-top", "margin-right", "margin-bottom", "margin-left", "color", "display", "visibility"];
                for(var i = 0; i < css.length; i++){
                    if(css[i].indexOf("-") > -1){
                        var temp = css[i].replace("-", "_");
                        this._css[temp] = $el.css(css[i]);
                    }else{
                        this._css[css[i]] = $el.css(css[i]);
                    };

                };
                return this;
            },
            checkVisibility: function(){
                this.visible = (((this.y + this.h) <= (this.top + this.winH)) && (this.y >= this.top));
                this.partial = ((this.y <= (this.top + this.winH)) && ((this.y + this.h) >= this.top));
                return this;
            },
            init: function(){
                this.winH = Math.floor(this.$win.height());
                this.winW = Math.floor(this.$win.width());
                this.docH = Math.floor(this.$doc.height());
                this.docW = Math.floor(this.$doc.width());
                this.top = this.$win.scrollTop();

  /* :compute the box-modal a,b,c,d with {x,y} values: */
  /* a----b */  this.a = { x:this.x, y:this.y };
  /* |    | */  this.b = { x:this.x+this.w, y:this.y };
  /* |    | */  this.c = { x:this.x, y:this.y+this.h };
  /* c----d */  this.d = { x:this.x+this.w, y:this.y+this.h };

                this.computedCss();
                this.checkVisibility();
                return this;
            }
        }.init();

    return obj;
};
\$\endgroup\$
2
  • \$\begingroup\$ FYI you can embed an on-site JS executable if you edit and hit Ctrl+M - no need for an external jsfiddle link =) \$\endgroup\$ Commented Nov 2, 2016 at 16:29
  • \$\begingroup\$ Am I the only one that read "elephant" intead of "element"? \$\endgroup\$ Commented Nov 2, 2016 at 22:50

1 Answer 1

2
\$\begingroup\$

I have written the following to help scrape several of the bits of information I need to proceed.

If this is for development purposes, for gathering information about an element and not actually related to your application, then you won't need this at all. The browser dev tools has a watch panel (F12 -> Sources -> First panel on the right side). You can simply pop in expressions and get their value there.

Now if it is for development, then this plugin is just the opposite of performant. Every time you call jquery.info(), all of these calculations run. That's fine if you actually use all of them, but if not then everything is unnecessary. I would suggest that you just create utility functions and call on a need-to-know basis.

Whenever you call $('.selector').info(), the this inside your function is actually a jQuery set that may contain zero or more matched items. But your logic only assumes one item in the set. How so?

Take $el.offset().left. When the set is empty, the return of offset will not be an object. This means accessing left will throw an error. As shown in the following:

// Access property left of undefined... or something not an object.
$('.non-existent-element').offset().left
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Also, the order of the elements in the set is not to be relied upon. You might be aiming for a specific element, but there might accidentally be another element that matches your selector and jQuery might pick it up first. What's the problem? Methods like offset only pick up data from the first item in the set. If your element isn't that item, you might be grabbing the wrong data.

// Returns 8px top when black is clearly 200px down...
// That's because there's 4 other divs you didn't expect.
alert($('.color').offset().top);
.color{
  width: 50px;
  height: 50px;
}

.invi{
  visibility: hidden;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div class="color invi" style="background:red">Red</div>
<div class="color invi" style="background:blue">Blue</div>
<div class="color invi" style="background:green">Green</div>
<div class="color invi" style="background:yelow">Yellow</div>
<div class="color" style="background:black">Black</div>

  /* :compute the box-modal a,b,c,d with {x,y} values: */
  /* a----b */  this.a = { x:this.x, y:this.y };
  /* |    | */  this.b = { x:this.x+this.w, y:this.y };
  /* |    | */  this.c = { x:this.x, y:this.y+this.h };
  /* c----d */  this.d = { x:this.x+this.w, y:this.y+this.h };

Your a, b, c and d, width, height, x and y can be retrieved from getBoundingClientRect.

this.winH = Math.floor(this.$win.height());
this.winW = Math.floor(this.$win.width());
this.docH = Math.floor(this.$doc.height());
this.docW = Math.floor(this.$doc.width());
this.top = this.$win.scrollTop();

This will return false data when the function was called, then the window is resized or scrolled.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.