[removed] Let user select an HTML element like Firebug?

后端 未结 10 918
醉话见心
醉话见心 2020-11-30 17:55

I want to write a browser (Chrome/FF) extension that needs to select an element on a web page. I would like it to behave like Firebug\'s element inspector does. You click

相关标签:
10条回答
  • 2020-11-30 18:28

    I ended up asking in the Firebug group and got some great help:

    http://groups.google.com/group/firebug/browse_thread/thread/7d4bd89537cd24e7/2c9483d699efe257?hl=en#2c9483d699efe257

    0 讨论(0)
  • 2020-11-30 18:32

    A very basic implementation can be done very easily without jQuery using .onmouseover and e.target:

    var last,
        bgc;
    document.onmouseover = function(e) {
        var elem = e.target;
    
        if (last != elem) {
            if (last != null) {
                last.classList.remove("hovered");
            }
    
            last = elem;
            elem.classList.add("hovered");
        }
    }
    

    With the CSS below if you want the children to change background as well:

    .hovered,
    .hovered * {
        cursor: pointer;
        color: black;
        background-color: red;
    }
    

    Demo


    If you want to select elements only near the edges (or select the parent near the edges and the element itself everywhere else) you could use .getBoundingClientRect.

    var last;
    window.addEventListener("mousemove", function(e) {
      if(last) {
        last.style.background = ''; // empty is enough to restore previous value
      }
        var elem = e.target;
    
      if(elem === document.body || elem === document.documentElement) {
        return;
      }
      var bb = elem.getBoundingClientRect();
      var xr = e.pageX - bb.left; // x relative to elem
      var yr = e.pageY - bb.top; // y relative to elem
      var ew = 10; // edge width
    
      if(
           xr <= ew
        || xr >= bb.width - ew
        || yr <= ew
        || yr >= bb.height - ew
      ){
        elem.style.background = 'red';
        last = elem;
      }
    });
    

    Paired with some borders, this can be pretty usable for selection. Demo

    0 讨论(0)
  • 2020-11-30 18:33

    What you need to do is to create 4 elements for the highlighting. They will form an empty square, and so your mouse events are free to fire. This is similar to this overlay example I've made.

    The difference is that you only need the four elements (no resize markers), and that the size and position of the 4 boxes are a bit different (to mimick the red border). Then you can use event.target in your event handler, because it gets the real topmost element by default.

    Another approach is to hide the exra element, get elementFromPoint, calculate then put it back.

    They're faster than light, I can tell you. Even Einstein would agree :)

    1.) elementFromPoint overlay/borders - [Demo1] FF needs v3.0+

    var box = $("<div class='outer' />").css({
      display: "none", position: "absolute", 
      zIndex: 65000, background:"rgba(255, 0, 0, .3)"
    }).appendTo("body");
    
    var mouseX, mouseY, target, lastTarget;
    
    // in case you need to support older browsers use a requestAnimationFrame polyfill
    // e.g: https://gist.github.com/paulirish/1579671
    window.requestAnimationFrame(function frame() {
      window.requestAnimationFrame(frame);
        if (target && target.className === "outer") {
            box.hide();
            target = document.elementFromPoint(mouseX, mouseY);
        }
        box.show();   
    
        if (target === lastTarget) return;
    
        lastTarget = target;
        var $target = $(target);
        var offset = $target.offset();
        box.css({
            width:  $target.outerWidth()  - 1, 
            height: $target.outerHeight() - 1, 
            left:   offset.left, 
            top:    offset.top 
        });
    });
    
    $("body").mousemove(function (e) {
        mouseX = e.clientX;
        mouseY = e.clientY;
        target = e.target;
    });
    

    2.) mouseover borders - [Demo2]

    var box = new Overlay();
    
    $("body").mouseover(function(e){
      var el = $(e.target);
      var offset = el.offset();
      box.render(el.outerWidth(), el.outerHeight(), offset.left, offset.top);
    });​
    
    /**
     * This object encapsulates the elements and actions of the overlay.
     */
    function Overlay(width, height, left, top) {
    
        this.width = this.height = this.left = this.top = 0;
    
        // outer parent
        var outer = $("<div class='outer' />").appendTo("body");
    
        // red lines (boxes)
        var topbox    = $("<div />").css("height", 1).appendTo(outer);
        var bottombox = $("<div />").css("height", 1).appendTo(outer);  
        var leftbox   = $("<div />").css("width",  1).appendTo(outer);
        var rightbox  = $("<div />").css("width",  1).appendTo(outer);
    
        // don't count it as a real element
        outer.mouseover(function(){ 
            outer.hide(); 
        });    
    
        /**
         * Public interface
         */
    
        this.resize = function resize(width, height, left, top) {
          if (width != null)
            this.width = width;
          if (height != null)
            this.height = height;
          if (left != null)
            this.left = left;
          if (top != null)
            this.top = top;      
        };
    
        this.show = function show() {
           outer.show();
        };
    
        this.hide = function hide() {
           outer.hide();
        };     
    
        this.render = function render(width, height, left, top) {
    
            this.resize(width, height, left, top);
    
            topbox.css({
              top:   this.top,
              left:  this.left,
              width: this.width
            });
            bottombox.css({
              top:   this.top + this.height - 1,
              left:  this.left,
              width: this.width
            });
            leftbox.css({
              top:    this.top, 
              left:   this.left, 
              height: this.height
            });
            rightbox.css({
              top:    this.top, 
              left:   this.left + this.width - 1, 
              height: this.height  
            });
    
            this.show();
        };      
    
        // initial rendering [optional]
        // this.render(width, height, left, top);
    }
    
    0 讨论(0)
  • 2020-11-30 18:38

    Here is a library that written in pure javascript as an alternative.

    TheRoom JS: https://github.com/hsynlms/theroomjs

    // theroom information template for target element
    var template="";
    template += "<div id=\"theroom-info\">";
    template += "  <span id=\"theroom-tag\"><\/span>";
    template += "  <span id=\"theroom-id\"><\/span>";
    template += "  <span id=\"theroom-class\"><\/span>";
    template += "<\/div>";
    template += "";
    template += "<style>";
    template += "  #theroom-info {";
    template += "    position: fixed;";
    template += "    bottom: 0;";
    template += "    width: 100%;";
    template += "    left: 0;";
    template += "    font-family: \"Courier\";";
    template += "    background-color: #ffffff;";
    template += "    padding: 10px;";
    template += "    color: #333333;";
    template += "    text-align: center;";
    template += "    box-shadow: 0px 4px 20px rgba(0,0,0,0.3);";
    template += "  }";
    template += "";
    template += "  #theroom-tag {";
    template += "    color: #C2185B;";
    template += "  }";
    template += "";
    template += "  #theroom-id {";
    template += "    color: #5D4037;";
    template += "  }";
    template += "";
    template += "  #theroom-class {";
    template += "    color: #607D8B;";
    template += "  }";
    template += "<\/style>";
    
    var options = {
      template: template,
      showInfo: true
    };
    
    // initialize
    theRoom.start(options);
    

    codepen demo

    0 讨论(0)
  • 2020-11-30 18:43

    I have recently required such a feature for a project I was working on, turned out that I had to use for sides to create a box because otherwise the event.target when you move the mouse would end up being the selector, and if I were to use z-index: -1 it would be a bit fishy when you have a lot of elements that overlap...etc.

    Here is a version that I have converted from my project for your benefit, it involves jQuery but it is extremely simple to convert to vanilla as only the mousemove & css methods from jQuery are used.

    Step by step instructions.

    First create the 5 HTMLElements that are required.

    <div id="selector">
        <div id="selector-top"></div>
        <div id="selector-left"></div>
        <div id="selector-right"></div>
        <div id="selector-bottom"></div>
    </div>
    

    Secondly create a mousemove event on the document (or your container)

    $(document).mousemove(function(event) { ... });
    

    Then inside the mousemove we will do some basic checking to prevent selecting the HTML, BODY, selector

    var id = event.target.id, tagName = event.target.tagName;
    
    if(id.indexOf('selector') !== -1 || tagName === 'BODY' || tagName === 'HTML') {
       return;
    } 
    

    Then we need to create a object to store our elements like so.

    var elements = {
        top: $('#selector-top'),
        left: $('#selector-left'),
        right: $('#selector-right'),
        bottom: $('#selector-bottom')
    };
    

    After that we store some variables that hold some information about the target element like so.

    var $target = event.target;
        targetOffset = $target.getBoundingClientRect(),
        targetHeight = targetOffset.height,
        targetWidth  = targetOffset.width;
    

    Then all we do is calculate the position & height for all 4 sides of the selector like so.

    elements.top.css({
        left:  (targetOffset.left - 4),
        top:   (targetOffset.top - 4),
        width: (targetWidth + 5)
    });
    
    elements.bottom.css({
        top:   (targetOffset.top + targetHeight + 1),
        left:  (targetOffset.left  - 3),
        width: (targetWidth + 4)
    });
    
    elements.left.css({
        left:   (targetOffset.left  - 5),
        top:    (targetOffset.top  - 4),
        height: (targetHeight + 8)
    });
    
    elements.right.css({
        left:   (targetOffset.left + targetWidth + 1),
        top:    (targetOffset.top  - 4),
        height: (targetHeight + 8)
    });
    

    All of the +aFewPixels is just a little optimization so that there is like 2px gap in between the selector and the target.

    For the CSS this is what I have come up with.

    #selector-top, #selector-bottom {
        background: blue;
        height:3px;
        position: fixed;
        transition:all 300ms ease;
    }
    
    #selector-left, #selector-right {
        background: blue;
        width:3px;
        position: fixed;
        transition:all 300ms ease;
    }
    

    The transition gives the selector a very nice sliding effect.

    Try out a demo http://jsfiddle.net/rFc8E/9/

    Note: This also works for transform: scale(2); eg. when a element is scaled in size.

    Edit: I've just updated this, I noticed that the elements object was inside the event handler, I've moved it outside in the demo, this is quite an important performance improvement because now, the elements object is only created once instead of Hundreds of Thousands if not millions of times inside the mousemove event.

    0 讨论(0)
  • 2020-11-30 18:50

    I wrote an implementation of this using jQuery as a component of another project. The source and documentation are available here: https://github.com/andrewchilds/jQuery.DomOutline

    0 讨论(0)
提交回复
热议问题