Rozszerzenie Chrome, jak wysyłać dane ze skryptu zawartości do popup.html

97

Wiem, że o to pytano w wielu postach, ale szczerze mówiąc, nie rozumiem ich. Jestem nowy w JavaScript, rozszerzeniach Chrome i wszystkim innym i mam to zadanie klasowe. Muszę więc stworzyć wtyczkę, która liczyłaby obiekty DOM na dowolnej stronie przy użyciu żądań międzydomenowych. Do tej pory udało mi się to osiągnąć za pomocą interfejsów API rozszerzeń Chrome. Teraz problem polega na tym, że muszę pokazać dane na mojej stronie popup.html z pliku contentScript.js. Nie wiem, jak to zrobić. Próbowałem przeczytać dokumentację, ale wysyłając wiadomości w przeglądarce Chrome, po prostu nie rozumiem, co mam zrobić.

poniżej znajduje się dotychczasowy kod.

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 

});

Każda pomoc byłaby doceniona i proszę unikać wszelkich noobów, które zrobiłem :)

Sumair Baloch
źródło

Odpowiedzi:

175

Chociaż zdecydowanie jesteś we właściwym kierunku (i właściwie dość blisko końca), w twoim kodzie jest kilka (imo) złych praktyk (np. Wstrzykiwanie całej biblioteki (jquery) do tak trywialnego zadania, deklarowanie niepotrzebnych uprawnień, czynienie zbędnych wywołania metod API itp.).
Nie testowałem twojego kodu osobiście, ale z krótkiego przeglądu uważam, że poprawienie poniższych może skutkować działającym rozwiązaniem (choć niezbyt bliskim optymalnemu):

  1. W manifest.json : Zmień kolejność skryptów zawartości, umieszczając jquery na pierwszym miejscu. Według odpowiednich dokumentów :

    "js" [...] Lista plików JavaScript, które mają być wstawione do pasujących stron. Są one wprowadzane w kolejności, w jakiej pojawiają się w tej tablicy.

    (podkreślenie moje)

  2. W contentscript.js : Przeniesienie chrome.runtime.sendMessage ({...}) bloku wewnątrz w onMessagezwrotnego detektora.


To powiedziawszy, oto moje proponowane podejście:

Kontrola przepływu:

  1. Do każdej strony wstawiany jest skrypt zawartości spełniający określone kryteria.
  2. Po wstrzyknięciu skrypty zawartości wysyłają komunikat do strony zdarzenia (czyli nietrwałej strony w tle), a strona zdarzenia dołącza akcję strony do karty.
  3. Gdy tylko wyskakujące okienko akcji strony zostanie załadowane, wysyła wiadomość do skryptu zawartości, prosząc o potrzebne informacje.
  4. Skrypt zawartości przetwarza żądanie i odpowiada, aby wyskakujące okienko akcji strony mogło wyświetlić informacje.

Struktura katalogów:

          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
źródło
Łał ! dzięki bracie, sprawdzam teraz twoje podejście, czuję, jak wiele problemów stworzyłem z moim kodem. Dzięki dużo to działa jak urok. :)
Sumair Baloch
cześć, przepraszam, nie byłem włączony od jakiegoś czasu, właśnie sprawdziłem twój komentarz. Nie mogę przyjąć żadnej odpowiedzi. mówi, że potrzebujesz do tego 15 punktów reputacji.
Sumair Baloch
Czy mógłbyś pomóc mi rozwiązać bardzo podobny problem? <a href=" stackoverflow.com/questions/34467627/…> Moje obecne spojrzenie na to to prawie twój kod z tej odpowiedzi. Ale nadal nie mogę go uruchomić i nie mogę znaleźć żadnej dobrej pomocy w tej sprawie.
wuno
dlaczego został użyty chrome.tabs.sendMessagew popupjs i chrome.runtime.onMessage.addListenerw content.js dlaczego .tabsdla popupjs i .runtimew content.js
Habib Kazemi
2
@hkm: Ponieważ tak właśnie wysyłasz i odbierasz wiadomości. Wszystko jest w dokumentach .
gkalpak
7

Możesz do tego użyć localStorage. Możesz przechowywać dowolne dane w formacie tabeli skrótów w pamięci przeglądarki, a następnie uzyskiwać do nich dostęp w dowolnym momencie. Nie jestem pewien, czy możemy uzyskać dostęp do localStorage ze skryptu zawartości (wcześniej był zablokowany), spróbuj zrobić to samodzielnie. Oto jak to zrobić za pośrednictwem strony w tle (najpierw przekazuję dane ze skryptu zawartości na stronę w tle, a następnie zapisuję je w localStorage):

w contentScript.js:

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

w eventPage.js (Twoja strona w tle):

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

Następnie możesz uzyskać dostęp do tej zmiennej w popup.js za pomocą localStorage ["total_elements"].

Może możesz uzyskać dostęp do localStorage bezpośrednio ze skryptu zawartości w nowoczesnych przeglądarkach. Wtedy nie musisz przekazywać danych przez swoją stronę w tle.

Przyjemna lektura o localStorage: http://diveintohtml5.info/storage.html

gthacoder
źródło
1
Proszę nie promować używania przestarzałego chrome.extension.onRequest / sendRequest (który przy okazji nie ładuje nietrwałej strony w tle przed wykonaniem). Zamiast tego użyj chrome.runtime. * .
gkalpak
1
@ExpertSystem Proszę, jeśli widzisz takie nieaktualne informacje, zasugeruj / wprowadź zmianę.
Xan
3
@Xan: Więc jesteś następcą w [Google-Chrome-Extension] :) Nie lubię edytować postów ludzi (uważam to za zbyt nachalne). Poza tym, przy tak wielu przestarzałych samouczkach i przykładach (przynajmniej wtedy), uznałem, że lepiej jest mieć wyraźny komentarz na temat tego, dlaczego jest zły w użyciu .extension.xxx, zamiast po prostu pokazywać właściwą drogę (co niektórzy ludzie mogą uważać za „ równie dobra alternatywa ”). Myślę, że to bardziej kwestia stylu. Tak trzymaj !
gkalpak