Chrome Extension วิธีการส่งข้อมูลจากสคริปต์เนื้อหาไปยัง popup.html


97

ฉันรู้ว่าสิ่งนี้ถูกถามในโพสต์มากมาย แต่จริงๆแล้วฉันไม่เข้าใจ ฉันเพิ่งเริ่มใช้ JavaScript, ส่วนขยายของ Chrome และทุกอย่างและฉันได้รับมอบหมายชั้นเรียนนี้ ดังนั้นฉันต้องสร้างปลั๊กอินที่จะนับวัตถุ DOM ในหน้าใดก็ได้โดยใช้คำขอข้ามโดเมน จนถึงตอนนี้ฉันสามารถทำได้โดยใช้ Chrome Extension API ตอนนี้ปัญหาคือฉันต้องแสดงข้อมูลในหน้า popup.html ของฉันจากไฟล์ contentScript.js ฉันไม่รู้ว่าต้องทำอย่างไรฉันได้ลองอ่านเอกสาร แต่การส่งข้อความใน chrome ฉันไม่เข้าใจว่าต้องทำอย่างไร

ต่อไปนี้เป็นรหัสจนถึงตอนนี้

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 

});

ความช่วยเหลือใด ๆ จะได้รับการชื่นชมและโปรดหลีกเลี่ยงความโง่เขลาที่ฉันทำ :)

คำตอบ:


176

แม้ว่าคุณจะอยู่ในทิศทางที่ถูกต้อง (และใกล้จะถึงจุดจบ) แต่ก็มีแนวทางปฏิบัติที่ไม่ดีหลายอย่าง (imo) ในโค้ดของคุณ (เช่นการฉีดไลบรารีทั้งหมด (jquery) สำหรับงานที่ไม่สำคัญเช่นนี้การประกาศสิทธิ์ที่ไม่จำเป็นทำให้ไม่จำเป็น เรียกวิธี API ฯลฯ )
ฉันไม่ได้ทดสอบโค้ดของคุณด้วยตัวเอง แต่จากภาพรวมโดยย่อฉันเชื่อว่าการแก้ไขสิ่งต่อไปนี้อาจทำให้ได้โซลูชันที่ใช้งานได้ (แม้ว่าจะไม่ใกล้เคียงกับที่ดีที่สุด):

  1. ในmanifest.json : เปลี่ยนลำดับของสคริปต์เนื้อหาโดยใส่ jquery ก่อน ตามเอกสารที่เกี่ยวข้อง :

    "js" [... ] รายการไฟล์ JavaScript ที่จะแทรกลงในหน้าที่ตรงกัน สิ่งเหล่านี้ถูกฉีดตามลำดับที่ปรากฏในอาร์เรย์นี้

    (เน้นเหมือง)

  2. ในcontentscript.js : ย้ายchrome.runtime.sendMessage ({ ... })บล็อกภายในonMessageโทรกลับฟัง


ที่กล่าวว่านี่คือแนวทางที่ฉันเสนอ:

การควบคุมการไหล:

  1. สคริปต์เนื้อหาจะถูกแทรกลงในแต่ละเพจที่ตรงกับเกณฑ์บางอย่าง
  2. เมื่อแทรกแล้วสคริปต์เนื้อหาจะส่งข้อความไปยังเพจเหตุการณ์ (หรือที่เรียกว่าเพจพื้นหลังที่ไม่ถาวร) และเพจเหตุการณ์จะแนบเพจแอ็คชันกับแท็บ
  3. ทันทีที่โหลดป๊อปอัปเพจแอ็คชันระบบจะส่งข้อความไปยังสคริปต์เนื้อหาเพื่อขอข้อมูลที่ต้องการ
  4. สคริปต์เนื้อหาประมวลผลคำร้องขอและตอบสนองเพื่อให้ป๊อปอัปเพจแอ็คชันแสดงข้อมูลได้

โครงสร้างไดเรกทอรี:

          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>

ว้าว ! ขอบคุณพี่ชายตรวจสอบแนวทางของคุณตอนนี้ฉันรู้สึกว่ามีปัญหามากมายที่ฉันสร้างขึ้นด้วยรหัสของฉัน ขอบคุณมากงานนี้เหมือนมีเสน่ห์ :)
Sumair Baloch

สวัสดีขออภัยฉันไม่ได้ไปมาสักระยะหนึ่งเพิ่งตรวจสอบความคิดเห็นของคุณ ฉันไม่สามารถยอมรับคำตอบใด ๆ มันบอกว่าคุณต้องมีคะแนนชื่อเสียง 15 คะแนนจึงจะทำได้
Sumair Baloch

คุณช่วยฉันแก้ปัญหาที่คล้ายกันนี้ได้ไหม <a href=" stackoverflow.com/questions/34467627/…>ภาพปัจจุบันของฉันที่นี่เป็นรหัสของคุณจากคำตอบนี้ แต่ฉันยังไม่สามารถใช้งานได้และดูเหมือนจะไม่พบความช่วยเหลือที่ดีใด ๆ ในเรื่องนี้
wuno

ทำไมคุณถึงใช้chrome.tabs.sendMessageใน popupjs และ chrome.runtime.onMessage.addListenerใน content.js ทำไมถึง.tabsเป็น popupjs และ.runtimeใน content.js
Habib Kazemi

2
@hkm: เพราะนั่นคือวิธีที่คุณส่งและรับข้อความ มันทั้งหมดในเอกสาร
gkalpak

7

คุณสามารถใช้ localStorage ได้ คุณสามารถจัดเก็บข้อมูลใด ๆ ในรูปแบบตารางแฮชในหน่วยความจำของเบราว์เซอร์แล้วเข้าถึงได้ตลอดเวลา ฉันไม่แน่ใจว่าเราสามารถเข้าถึง localStorage จากสคริปต์เนื้อหาได้หรือไม่ (ถูกบล็อกก่อนหน้านี้) ให้ลองทำด้วยตัวเอง ต่อไปนี้เป็นวิธีดำเนินการผ่านหน้าพื้นหลังของคุณ (ฉันส่งผ่านข้อมูลจากสคริปต์เนื้อหาไปยังหน้าพื้นหลังก่อนจากนั้นบันทึกใน localStorage):

ใน contentScript.js:

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

ใน eventPage.js (หน้าพื้นหลังของคุณ):

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

จากนั้นคุณสามารถเข้าถึงตัวแปรนั้นใน popup.js ด้วย localStorage ["total_elements"]

บางทีคุณอาจเข้าถึง localStorage ได้โดยตรงจากสคริปต์เนื้อหาในเบราว์เซอร์สมัยใหม่ จากนั้นคุณไม่จำเป็นต้องส่งข้อมูลผ่านหน้าพื้นหลังของคุณ

อ่านดีเกี่ยวกับ localStorage: http://diveintohtml5.info/storage.html


1
โปรดอย่าส่งเสริมการใช้chrome.extension.onRequest / sendRequest ที่เลิกใช้แล้ว(ซึ่ง btw ไม่โหลดหน้าพื้นหลังที่ไม่ถาวรก่อนดำเนินการ) ใช้chrome.runtime. *แทน
gkalpak

1
@ExpertSystem โปรดหากคุณเห็นข้อมูลที่ล้าสมัยดังกล่าวแนะนำ / ทำการแก้ไข
Xan

3
@ Xan: คุณคือผู้สืบทอดใน [Google-Chrome-Extension] :) ฉันไม่ชอบการแก้ไขโพสต์ของคนอื่น (ฉันคิดว่ามันล่วงล้ำเกินไป) นอกจากนี้ยังมีบทเรียนจำนวนมากดังนั้นล้าสมัยและตัวอย่าง (อย่างน้อยก็กลับมาแล้ว) ผมพบว่ามันดีกว่าที่จะมีความคิดเห็นอย่างชัดเจนเกี่ยวกับเหตุผลที่มันเป็นสิ่งที่ไม่ดีที่จะใช้.extension.xxxแทนเพียงแค่แสดงให้เห็นวิธีการที่เหมาะสม (ซึ่งบางคนอาจจะคิดว่าเป็น " ทางเลือกที่ดีพอ ๆ กัน ") ฉันเดาว่าเป็นเรื่องของสไตล์มากกว่า ดีแล้วทำต่อไป !
gkalpak
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.