Chrome-Erweiterung zum Senden von Daten aus dem Inhaltsskript an popup.html

94

Ich weiß, dass dies in zahlreichen Posts gefragt wurde, aber ehrlich gesagt verstehe ich sie nicht. Ich bin neu in JavaScript, Chrome Extensions und allem und habe diese Klassenzuordnung. Daher muss ich ein Plugin erstellen, das DOM-Objekte auf einer bestimmten Seite mithilfe domänenübergreifender Anforderungen zählt. Bisher konnte ich dies mithilfe von Chrome Extension-APIs erreichen. Das Problem ist nun, dass ich die Daten auf meiner popup.html-Seite aus der Datei contentScript.js anzeigen muss. Ich weiß nicht, wie ich das machen soll. Ich habe versucht, die Dokumentation zu lesen, aber Nachrichten in Chrome kann ich einfach nicht verstehen, was ich tun soll.

Es folgt der bisherige Code.

manifest.json

{
"manifest_version":2,

"name":"Dom Reader",
"description":"Counts Dom Objects",
"version":"1.0",

"page_action": {
    "default_icon":"icon.png",
    "default_title":"Dom Reader",
    "default_popup":"popup.html"
},

"background":{
    "scripts":["eventPage.js"],
    "persistent":false
},

"content_scripts":[
    {
        "matches":["http://pluralsight.com/training/Courses/*", "http://pluralsight.com/training/Authors/Details/*",                                          "https://www.youtube.com/user/*", "https://sites.google.com/site/*", "http://127.0.0.1:3667/popup.html"],
        "js":["domReader_cs.js","jquery-1.10.2.js"]
        //"css":["pluralsight_cs.css"]
    }
],

"permissions":[
    "tabs",
    "http://pluralsight.com/*",
    "http://youtube.com/*",
    "https://sites.google.com/*",
    "http://127.0.0.1:3667/*"
]

popup.html

<!doctype html>
<html>

    <title> Dom Reader </title>    
    <script src="jquery-1.10.2.js" type="text/javascript"></script>
    <script src="popup.js" type="text/javascript"></script>

<body>
    <H1> Dom Reader </H1>
    <input type="submit" id="readDom" value="Read DOM Objects" />

   <div id="domInfo">

    </div>
</body>
</html>

eventPage.js

var value1,value2,value3;

chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
if (request.action == "show") {
    chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
        chrome.pageAction.show(tabs[0].id);
    });
}

value1 = request.tElements;
});

popup.js

$(function (){
$('#readDom').click(function(){
chrome.tabs.query({active: true, currentWindow: true}, function (tabs){
    chrome.tabs.sendMessage(tabs[0].id, {action: "readDom"});

 });
});
});

contentScript

var totalElements;
var inputFields;
var buttonElement;

chrome.runtime.onMessage.addListener(function (request, sender, sendResponse){
if(request.action == "readDom"){

    totalElements = $("*").length;
    inputFields = $("input").length;
    buttonElement = $("button").length;


}
})

chrome.runtime.sendMessage({ 
action: "show", 
tElements: totalElements, 
Ifields: inputFields, 
bElements: buttonElement 

});

Jede Hilfe wäre dankbar und bitte vermeiden Sie jegliche Noobness, die ich getan habe :)

Sumair Baloch
quelle

Antworten:

171

Obwohl Sie definitiv in die richtige Richtung gehen (und tatsächlich ziemlich kurz vor dem Ende stehen), enthält Ihr Code einige (imo) schlechte Praktiken (z. B. das Einfügen einer ganzen Bibliothek (jquery) für eine so triviale Aufgabe, das Deklarieren unnötiger Berechtigungen und das Überflüssige Aufrufe von API-Methoden usw.).
Ich habe Ihren Code nicht selbst getestet, aber aus einer kurzen Übersicht glaube ich, dass die Korrektur der folgenden Punkte zu einer funktionierenden Lösung führen kann (wenn auch nicht sehr nahe am Optimum):

  1. In manifest.json : Ändern Sie die Reihenfolge der Inhaltsskripte, wobei jquery an erster Stelle steht. Gemäß den relevanten Dokumenten :

    "js" [...] Die Liste der JavaScript-Dateien, die in übereinstimmende Seiten eingefügt werden sollen. Diese werden in der Reihenfolge injiziert, in der sie in diesem Array angezeigt werden.

    (Hervorhebung von mir)

  2. In contentscript.js : Bewegen Sie den chrome.runtime.sendMessage ({...}) Block innerhalb des onMessageHörers Rückruf.


Das heißt, hier ist mein vorgeschlagener Ansatz:

Kontrollfluss:

  1. In jede Seite wird ein Inhaltsskript eingefügt, das bestimmten Kriterien entspricht.
  2. Nach dem Injizieren senden die Inhaltsskripte eine Nachricht an die Ereignisseite (auch als nicht persistente Hintergrundseite bezeichnet), und die Ereignisseite fügt der Registerkarte eine Seitenaktion hinzu.
  3. Sobald das Seitenaktions-Popup geladen ist, sendet es eine Nachricht an das Inhaltsskript und fragt nach den benötigten Informationen.
  4. Das Inhaltsskript verarbeitet die Anforderung und antwortet, damit das Popup für Seitenaktionen die Informationen anzeigen kann.

Verzeichnisaufbau:

          root-directory/
           |_____img
                 |_____icon19.png
                 |_____icon38.png
           |_____manifest.json
           |_____background.js
           |_____content.js
           |_____popup.js
           |_____popup.html

manifest.json:

{
  "manifest_version": 2,
  "name": "Test Extension",
  "version": "0.0",
  "offline_enabled": true,

  "background": {
    "persistent": false,
    "scripts": ["background.js"]
  },

  "content_scripts": [{
    "matches": ["*://*.stackoverflow.com/*"],
    "js": ["content.js"],
    "run_at": "document_idle",
    "all_frames": false
  }],

  "page_action": {
    "default_title": "Test Extension",
    //"default_icon": {
    //  "19": "img/icon19.png",
    //  "38": "img/icon38.png"
    //},
    "default_popup": "popup.html"
  }

  // No special permissions required...
  //"permissions": []
}

background.js:

chrome.runtime.onMessage.addListener((msg, sender) => {
  // First, validate the message's structure.
  if ((msg.from === 'content') && (msg.subject === 'showPageAction')) {
    // Enable the page-action for the requesting tab.
    chrome.pageAction.show(sender.tab.id);
  }
});

content.js:

// Inform the background page that 
// this tab should have a page-action.
chrome.runtime.sendMessage({
  from: 'content',
  subject: 'showPageAction',
});

// Listen for messages from the popup.
chrome.runtime.onMessage.addListener((msg, sender, response) => {
  // First, validate the message's structure.
  if ((msg.from === 'popup') && (msg.subject === 'DOMInfo')) {
    // Collect the necessary data. 
    // (For your specific requirements `document.querySelectorAll(...)`
    //  should be equivalent to jquery's `$(...)`.)
    var domInfo = {
      total: document.querySelectorAll('*').length,
      inputs: document.querySelectorAll('input').length,
      buttons: document.querySelectorAll('button').length,
    };

    // Directly respond to the sender (popup), 
    // through the specified callback.
    response(domInfo);
  }
});

popup.js:

// Update the relevant fields with the new data.
const setDOMInfo = info => {
  document.getElementById('total').textContent = info.total;
  document.getElementById('inputs').textContent = info.inputs;
  document.getElementById('buttons').textContent = info.buttons;
};

// Once the DOM is ready...
window.addEventListener('DOMContentLoaded', () => {
  // ...query for the active tab...
  chrome.tabs.query({
    active: true,
    currentWindow: true
  }, tabs => {
    // ...and send a request for the DOM info...
    chrome.tabs.sendMessage(
        tabs[0].id,
        {from: 'popup', subject: 'DOMInfo'},
        // ...also specifying a callback to be called 
        //    from the receiving end (content script).
        setDOMInfo);
  });
});

popup.html:

<!DOCTYPE html>
<html>
  <head>
    <script type="text/javascript" src="popup.js"></script>
  </head>
  <body>
    <h3 style="font-weight:bold; text-align:center;">DOM Info</h3>
    <table border="1" cellpadding="3" style="border-collapse:collapse;">
      <tr>
        <td nowrap>Total number of elements:</td>
        <td align="right"><span id="total">N/A</span></td>
      </tr>
      <tr>
        <td nowrap>Number of input elements:</td>
        <td align="right"><span id="inputs">N/A</span></td>
      </tr>
      <tr>
        <td nowrap>Number of button elements:</td>
        <td align="right"><span id="buttons">N/A</span></td>
      </tr>
    </table>
  </body>
</html>
gkalpak
quelle
Wow ! Danke Bruder, überprüfe jetzt deinen Ansatz. Ich spüre, wie viele Probleme ich mit meinem Code erstellt habe. Vielen Dank, das funktioniert wie ein Zauber. :)
Sumair Baloch
Hallo, Entschuldigung, ich bin seit einiger Zeit nicht mehr dabei. Ich habe nur Ihren Kommentar überprüft. Ich kann keine Antwort akzeptieren. Es heißt, Sie benötigen 15 Reputationspunkte, um dies zu tun.
Sumair Baloch
Würde es Ihnen etwas ausmachen, mir bei der Lösung eines sehr ähnlichen Problems zu helfen? <a href=" stackoverflow.com/questions/34467627/…> Mein aktueller Versuch ist so ziemlich Ihr Code aus dieser Antwort. Aber ich kann ihn immer noch nicht zum Laufen bringen und kann keine gute Hilfe in dieser Angelegenheit finden.
wuno
warum Sie chrome.tabs.sendMessagein popupjs und chrome.runtime.onMessage.addListenerin content.js verwendet haben warum .tabsfür popupjs und .runtimein content.js
Habib Kazemi
2
@hkm: Denn so senden und empfangen Sie Nachrichten. Es ist alles in den Dokumenten .
Gkalpak
7

Sie können dafür localStorage verwenden. Sie können alle Daten in einem Hash-Tabellenformat im Browserspeicher speichern und dann jederzeit darauf zugreifen. Ich bin nicht sicher, ob wir über das Inhaltsskript (es wurde zuvor blockiert) auf localStorage zugreifen können. Versuchen Sie es selbst. So geht's über Ihre Hintergrundseite (ich übergebe Daten vom Inhaltsskript zuerst an die Hintergrundseite und speichere sie dann in localStorage):

in contentScript.js:

chrome.runtime.sendMessage({
  total_elements: totalElements // or whatever you want to send
});

in eventPage.js (Ihre Hintergrundseite):

chrome.runtime.onMessage.addListener(
    function(request, sender, sendResponse){
       localStorage["total_elements"] = request.total_elements;
    }
);

Dann können Sie mit localStorage ["total_elements"] auf diese Variable in popup.js zugreifen.

Möglicherweise können Sie in modernen Browsern direkt über das Inhaltsskript auf localStorage zugreifen. Dann müssen Sie die Daten nicht über Ihre Hintergrundseite weitergeben.

Gute Lektüre über localStorage: http://diveintohtml5.info/storage.html

gthacoder
quelle
1
Bitte fördern Sie nicht die Verwendung der veralteten chrome.extension.onRequest / sendRequest (die übrigens vor der Ausführung keine nicht persistente Hintergrundseite laden). Verwenden Sie stattdessen chrome.runtime. * .
Gkalpak
1
@ExpertSystem Wenn Sie solche veralteten Informationen sehen, schlagen Sie eine Bearbeitung vor.
Xan
3
@Xan: Du bist also der Nachfolger von [Google-Chrome-Extension] :) Ich mag es nicht, Beiträge von Leuten zu bearbeiten (ich finde es zu aufdringlich). Außerdem fand ich es bei so vielen veralteten Tutorials und Beispielen (zumindest damals) besser, einen expliziten Kommentar dazu zu haben, warum es schlecht ist, es zu verwenden .extension.xxx, anstatt nur den richtigen Weg aufzuzeigen (was manche Leute vielleicht als " ebenso OK Alternative "). Es ist eher eine Frage des Stils, denke ich. Mach weiter so !
gkalpak