Javascript: разрешить пользователю выбирать элемент HTML, например Firebug?

Я хочу написать расширение для браузера (Chrome/FF), которому нужно выбрать элемент на веб-странице. Я хотел бы, чтобы он вел себя как инспектор элементов Firebug. Вы нажимаете стрелку проверки, и затем вы можете наводить/выделять элементы. Когда вы нажимаете на нужный элемент, он проверяется. Меня просто интересует код, позволяющий пользователю выбирать элемент, а не его проверка или что-то подобное.

Поскольку я пишу расширение, было бы неплохо, если бы вы могли предоставить код, отличный от jQuery/Prototype/etc.., поэтому мне не нужно его распространять.


person Chad    schedule 08.03.2010    source источник
comment
Самый простой способ — проверить код Firebug. Если вы установили firebug, вы можете найти код в папке профиля firefox. Расположение по умолчанию на моей Linux-машине ~/.mozilla/firefox/random-chars.default/extensions/[email protected]/content/firebug В Vista это что-то вроде: C:\Users\<user-name>\AppData\Roaming\Mozilla\Firefox\Profiles\random-chars.default\extensions\[email protected]\content\firebug   -  person Amarghosh    schedule 08.03.2010
comment
Что ж, поначалу это казалось отличной идеей. Затем я пошел и посмотрел на код, и там было более 50 файлов разумного размера. Я даже не знаю, с чего начать и, тем более, я не знаю, что копировать/вставлять/изменять. Так что, к сожалению, это определенно не самая простая вещь. Я, вероятно, буду что-то варить в домашних условиях, если не найду другие решения или другие не ответят.   -  person Chad    schedule 09.03.2010


Ответы (10)


Я написал реализацию этого, используя jQuery как компонент другого проекта. Исходный код и документация доступны здесь: https://github.com/andrewchilds/jQuery.DomOutline.

person Andrew Childs    schedule 20.10.2012
comment
Работает отлично. Мне нравится вилка этих парней, потому что она подсвечивает в реальном времени: github.com/smmalmansoori/jQuery.DomOutline - person phreakhead; 16.12.2013

Недавно мне понадобилась такая функция для проекта, над которым я работал, оказалось, что мне пришлось использовать для сторон, чтобы создать поле, потому что в противном случае event.target при перемещении мыши оказался бы селектором, и если бы я использовал z-index: -1 было бы немного подозрительно, если бы у вас было много перекрывающихся элементов... и т.д.

Вот версия, которую я преобразовал из своего проекта для вашего удобства, она использует jQuery, но ее чрезвычайно просто преобразовать в vanilla, так как используются только методы mousemove и css из jQuery.

Пошаговые инструкции.

Сначала создайте необходимые 5 элементов HTML.

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

Во-вторых, создайте событие mousemove в document (или в вашем контейнере)

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

Затем внутри mousemove мы проведем базовую проверку, чтобы предотвратить выбор HTML, BODY, selector

var id = event.target.id, tagName = event.target.tagName;

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

Затем нам нужно создать объект для хранения наших элементов.

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

После этого мы сохраняем некоторые переменные, которые содержат некоторую информацию о целевом элементе, например.

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

Затем все, что мы делаем, это вычисляем position и height для всех 4 сторон селектора.

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)
});

Все +aFewPixels — это всего лишь небольшая оптимизация, так что между селектором и целью остается около 2px промежутка.

Для CSS это то, что я придумал.

#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;
}

transition придает селектору очень приятный эффект скольжения.

Попробуйте демоверсию http://jsfiddle.net/rFc8E/9/.

Примечание. Это также работает для transform: scale(2);, например. когда элемент масштабируется по размеру.

Редактировать: я только что обновил это, я заметил, что объект elements был внутри обработчика событий, я переместил его наружу в демонстрации, это очень важно повышение производительности, поскольку теперь объект elements создается только один раз вместо сотни тысяч, если не миллионы раз внутри события mousemove.

person iConnor    schedule 07.09.2013
comment
Спасибо! Отличные инструкции, и мне нравится эффект слайда в демоверсии! - person Chad; 07.09.2013
comment
Лучшее решение! Спасибо за объяснение! - person MGItkin; 14.09.2016
comment
Блестящее объяснение! - person User24231; 16.05.2020

Один простой способ сделать это — использовать контур вместо границы:

.highlight { outline: 4px solid #07C; }

Просто добавьте и удалите этот класс для любого элемента, который вы хотите выбрать/отменить выбор (код ниже не проверен должным образом):

document.body.addEventListener("mouseover", function(e) {
    e.stopPropagation();
    e.target.addEventListener("mouseout", function (e) {
        e.target.className = e.target.className.replace(new RegExp(" highlight\\b", "g"), "");
    });
    e.target.className += " highlight";
});

Поскольку вы используете контур (который поддерживается Chrome) вместо границы, элементы не будут прыгать. Я использую нечто подобное в моем расширении EasyReader.

person antonj    schedule 30.08.2010
comment
это не работает, когда вы хотите выделить изображения <img/> - person fingerprints; 21.08.2019

Средство выбора элементов HTML (ванильный JS)

Выберите и выделите любой элемент HTML на странице только с помощью Vanilla JS! Протестировано в Chrome, FF и Opera, не работает в IE.

Как это работает:

То, что вам нужно, на самом деле очень просто. Вы можете просто создать пустой блок div с фоном в JS и перемещать его, чтобы выделить поверх наведенных элементов. Вот JS-код:

const hoverBox = document.createElement("div");
console.log("hoverBox: ", hoverBox);
hoverBox.style.position = "absolute";
// change to whatever highlight color you want
hoverBox.style.background = "rgba(153, 235, 255, 0.5)";
// avoid blocking the hovered element and its surroundings
hoverBox.style.zIndex = "0";
document.body.appendChild(hoverBox);
let previousTarget;
document.addEventListener("mousemove", (e) => {
    let target = e.target;
    if (target === hoverBox) {
        // the truely hovered element behind the added hover box
        const hoveredElement = document.elementsFromPoint(e.clientX, e.clientY)[1];
        if (previousTarget === hoveredElement){
            // avoid repeated calculation and rendering
            return;
        } else{
            target = hoveredElement;
        }
    } else{
        previousTarget = target;
    }
    const targetOffset = target.getBoundingClientRect();
    const targetHeight = targetOffset.height;
    const targetWidth = targetOffset.width;
    // add a border around hover box
    const boxBorder = 5;
    hoverBox.style.width = targetWidth + boxBorder * 2 + "px";
    hoverBox.style.height = targetHeight + boxBorder * 2 + "px";
    // need scrollX and scrollY to account for scrolling
    hoverBox.style.top = targetOffset.top + window.scrollY - boxBorder + "px";
    hoverBox.style.left = targetOffset.left + window.scrollX - boxBorder + "px";
});

Посмотреть демо
Я также сделал пакет npm для средства выбора элементов со многими другими пользовательскими настройками, такими как цвет фона, ширина границы, переход и т. д. Вот страница GitHub.

person AlienKevin    schedule 06.04.2019
comment
Посмотрел кучу, и этот самый простой и чистый - person Michael Terry; 10.05.2019
comment
Настройки для эмуляции инспектора Chrome: hoverBox.style.zIndex = 9999; hoverBox.style.opacity = .5; hoverBox.style.pointerEvents = нет; - person Michael Terry; 11.05.2019

В итоге я спросил в группе Firebug и получил большую помощь:

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

person Chad    schedule 13.03.2010
comment
Можете ли вы код с нами, пожалуйста? хочу сделать что-то подобное - person Vaibhav Garg; 16.04.2010
comment
Я делаю нечто подобное в одном из своих проектов, но мне нужно немного этого. После того, как элемент выбран, я хочу получить уникальный селектор для этого элемента. Любая идея, как это сделать? Прямо сейчас я его взламываю... Если выбранный элемент имеет атрибут ID, все в порядке. В противном случае я проверяю класс и нахожу индекс выбранного элемента с аналогичным классом, и если это не так, я беру имя тега, повторяя тот же процесс. Но этот метод совершенно не надежен. Есть ли лучшая альтернатива? - person Ashit Vora; 31.05.2012

Вот библиотека, написанная на чистом javascript в качестве альтернативы.

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

person hsynlms    schedule 13.04.2018

На Stackoverflow был задан аналогичный вопрос, и на него было много хороших ответов: -plugin">Кто-нибудь знает библиотеку или плагин javascript инспектора DOM?

Для тех, кто ищет быстрое и грязное решение:

http://userscripts.org/scripts/review/3006 — самый простой. Просто поместите код в теги <script></script>, и все готово.

https://github.com/josscrowcroft/Simple-JavaScript-DOM-Inspector/blob/master/spector.js немного лучше и по-прежнему очень легко интегрируется.

Для более сложного инспектора элементов вы можете проверить SelectorGadget, как указано Уди. Код выбора инспектора находится в http://www.selectorgadget.com/stable/lib/interface.js

person Community    schedule 12.10.2011
comment
Simple Javascript DOM inspector выглядит очень многообещающе и представляет собой простую библиотеку. - person Vimalraj Selvam; 03.03.2016

Также проверьте это:

http://rockingcode.com/tutorial/element-dom-tree-jquery-plugin-firebug-like-functionality/

Я нашел это довольно проницательным... и здесь есть демонстрация:

http://rockingcode.com/demos/elemtree/

Надеюсь это поможет.

person abourget    schedule 20.03.2013

Очень простую реализацию можно сделать очень легко без jQuery, используя .onmouseover и 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");
    }
}

С помощью приведенного ниже CSS, если вы хотите, чтобы дети также меняли фон:

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

Демо


Если вы хотите выбрать элементы только рядом с краями (или выбрать родителя рядом с краями, а сам элемент везде), вы можете использовать .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;
  }
});

В сочетании с некоторыми рамками это может быть очень полезно для выбора. Демо

person Zach Saucier    schedule 19.05.2016

Что вам нужно сделать, так это создать 4 элемента для подсветки. Они образуют пустой квадрат, поэтому события мыши могут запускаться свободно. Это похоже на этот пример наложения, который я сделал.

Разница в том, что вам нужны только четыре элемента (без маркеров изменения размера), а размер и положение четырех полей немного отличаются (чтобы имитировать красную рамку). Затем вы можете использовать event.target в своем обработчике событий, потому что по умолчанию он получает самый верхний элемент.

Другой подход заключается в том, чтобы скрыть дополнительный элемент, получить elementFromPoint, вычислить и вернуть его обратно.

Они быстрее света, я могу вам сказать. Даже Эйнштейн согласился бы :)

1.) Наложение/границы elementFromPoint — [Demo1] FF требуется версия 3.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.) границы при наведении курсора — [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);
}
person gblazex    schedule 31.05.2015