Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Chrome Dev Tools - Remembering the selected element through page reload

I am building my own type of Chrome dev tools (inspect element) which will also be in the form of a Chrome extension using JavaScript. I would like it to have some similar functionality to Chromes dev tools, one of which I'm finding hard to debunk how they have done it.

Basically, when you right click on any element and click "inspect element" it will open up the dev tools if not already open and hover over the HTML element your right click was targeting.

The bit that I want to replicate is when you refresh the page the while you still have the element selected in dev tools, it will reload all the HTML and directly go to the element you had selected in the dev tools before reloading.

Here is what I mean if it's a little unclear:

HTML:

<div class="1">
    <div class="2">
        <div class="3"></div>
    </div>
</div>

If I was hovering over the div with class of "3" and refreshed the page, Chrome dev tools knows to reload with the dev tools highlighting that exact div. Even if there are multiple divs with that class or in a similar structure it will always hover the correct one.

Would anyone know if the best approach here is to have a big if statement which looks for certain traits of the element such as ideally an id but also a lot of fall-backs if the element does not have an id such as surrounding elements or unique classes/ attributes associated with that element?

I've tried searching for 'Chrome extension node selector' or similar variants but have not been able to find any information.

like image 696
red house 87 Avatar asked Mar 08 '23 15:03

red house 87


2 Answers

There already exists a pretty good "inspect element" extension that you can find here. However, it does not have support for saving the inspected element. But since it's a bounty question I could give you a few hints for improving it towards a working solution.

The main problem I see here is "serializing" the element. A good answer on this topic can be found here. (see fiddle also) It basically sums up to "find the nearest ancestor with an ID and track the path downwards to the inspected element". In your case this could be:

tracePath: function (element, result) {
  if (element.id !== '') {
    result.push({
      id: element.id
    });
    return;
  }
  if (element === document.body) {
    result.push({
      tag: element.tagName
    });
    return;
  }

  var siblings = element.parentNode.childNodes;
  for (var i = 0; i < siblings.length; ++i) {
    var sibling = siblings[i];
    if (sibling === element) {
      result.push({
        index: i,
        tag: element.tagName
      });
      return this.tracePath(element.parentNode, result);
    }
  }
},

The above just stores an array of nodes to follow during deserialization:

find: function (path) {
  var element;
  while (path.length) {
    var current = path.pop();

    if (!current) return element;
    if (current.id) element = document.getElementById(current.id);
    else if (element) {
      if (current.index < element.childNodes.length && current.tag === element.childNodes[current.index].tagName)
        element = element.childNodes[current.index];
      else
        return;
    } else {
      var matches = document.getElementsByTagName(current.tag);
      if (matches.length)
          element = matches[0];
    }
  }
  return element;
},

Once we these two, we only need ways to store/load the selection:

store: function (path) {
  var selection = Object.create({});
  selection[window.location.href] = path;
  chrome.storage.local.set(selection, function () {
    if (chrome.runtime.lastError) {
      console.error(chrome.runtime.lastError)
    }
  })
},

And load:

load: function () {
  var self = this, key = window.location.href;
  chrome.storage.local.get(key, function (found) {
    if (chrome.runtime.lastError) {
      console.error(chrome.runtime.lastError)
    } else if (found && found[key]) {
      var path = found[key],
        element = Util.find(path);

      if (element) {
        // zoom into inspected element
        element.scrollIntoView();
        // add selection to Inspector instance
        self.$selection = element;
        // function similar to layout() - highlights inspected element
        self.select();
      }
    }
  })
}

You could then write a highlighting function, say select for highlighting the inspected element, similar to already existing layout which highlights the ruler.

Saving the element could be done when clicking on it:

registerEvents: function() {
  ...
  document.addEventListener('click', function () {
      var path = [];
      Util.tracePath(this.$target, path);
      this.$selection = this.$target;
      this.select(true);
      this.storeSelection(path);
  }.bind(this));
  ...
},

There's a fork which contains the above modifications that you can find here. You can try it out by downloading it and loading the app folder as an unpacked extension in a new tab under chrome://extensions/. Enjoy!


Screenshots

enter image description here

like image 153
Andrei Roba Avatar answered Apr 28 '23 03:04

Andrei Roba


You could always store the information for the element, such as id, class, font-family, etc. into chrome.storage.local/sync.

To handle a reload, you could do something such as fetching that chrome.storage object and window.location(myElement) inside your custom dev tools tab.

like image 38
James Gould Avatar answered Apr 28 '23 05:04

James Gould