2015-12-26 9 views
17

Sto costruendo un'estensione per Chrome. Sto cercando di far comunicare la mia app con ogni pagina dell'estensione e la pagina che l'utente sta visualizzando nel browser. Devo accedere a dom dall'estensione e quindi aggiornarlo.Modifica contenuto DOM con estensione Chrome

manifest.json 
popup.html 
popup.js 
background.js 
content.js 

e la pagina corrente che l'utente sta visualizzando.

Il mio obiettivo è sul caricamento della pagina modificare il dom e mostrare all'utente la nuova versione della pagina prima che lo vedano mai. in popup.js gli utenti sono autorizzati a inserire le parole chiave nel popup. Le parole chiave vengono salvate su localStorage e mentre visualizzano il web le parole chiave vengono censurate fuori dalla loro vista rendendo la div genitore delle parole chiave nascoste se viene trovata su qualsiasi pagina che stanno visualizzando.

Ho bisogno di aiuto per far sì che ogni pagina sia comunicata e penso che il modo in cui sto nascondendo le div parent in popup.js non funzionerà. Sono confuso su come eseguire l'azione sul dom dal fronte.

Invia dom a background.js Trova le parole chiave nella pagina e cambia le loro div parent in hidden. torna alla pagina di visualizzazione.

Penso che questa riga stia dicendo se abbino qualsiasi url quindi eseguo la mia app ma non ne sono sicuro.

"matches": ["*://*/*"], 

mio manifest.json

{ 
"name": "Wuno Zensoring", 
    "version" : "1.0", 
    "permissions": [ 
    "activeTab", 
    "tabs", 
    "storage" 
    ], 
    "description": "This extension will search the document file for keywords and hide their parent div.", 
    "icons": {     
    "19": "icon19.png", 
    "38": "icon38.png", 
    "48": "icon48.png", 
    "128": "icon128.png" 
    },  
    "background": { 
    "persistent": false, 
    "scripts": ["jquery-1.11.3.min.js","background.js"] 
    }, 
    "content_scripts": [{ 
     "matches": ["*://*/*"], 
     "js":   ["content.js"], 
     "run_at": "document_end", 
     "all_frames": true 
    }], 
    "web_accessible_resources": [ 
     "popup.js", "content.js" 
     ], 
    "browser_action": { 
    "default_icon": "icon.png128", 
    "default_popup": "popup.html", 
    "default_icon": {     
     "19": "icon19.png", 
     "38": "icon38.png", 
     "48": "icon48.png", 
     "128": "icon128.png"   
    } 
    }, 
    "manifest_version": 2 
} 

popup.html

<!doctype html> 
<html> 
    <head> 
    <title>Wuno Zensorship</title> 
    <script src="jquery-1.11.3.min.js"></script> 
     <script src="popup.js"></script> 
    <link rel="stylesheet" type="text/css" href="styles.css"> 
    </head> 
    <body> 
    <img src="icon48.png"> 

<section> 
<form id="form" action="#" method="POST"> 
<input id="description" name="description" type="text" /> 
<input id="add" type="submit" value="Add" /> 
<button id="clearChecked">Clear Checked Items</button> 
<button id="clear">Clear All</button> 
</form> 
<div id="alert"></div> 
<ul id="keyWords"></ul> 
</body> 
</html> 

popup.js

$(document).ready(function() { 
localArray = []; 

if (!localStorage.keyWords) { 
    localStorage.setItem('keyWords', JSON.stringify(localArray)); 
} 

loadKeyWords(); 

function loadKeyWords() { 
    $('#keyWords').html(''); 
    localArray = JSON.parse(localStorage.getItem('keyWords')); 
    for(var i = 0; i < localArray.length; i++) { 
     $('#keyWords').prepend('<li><input class="check" name="check" type="checkbox">'+localArray[i]+'</li>'); 
     } 
    } 

$('#add').click(function() { 
    var Description = $('#description').val(); 
    if($("#description").val() === '') { 
    $('#alert').html("<strong>Warning!</strong> You left the to-do empty"); 
    $('#alert').fadeIn().delay(1000).fadeOut(); 
    return false; 
    } 
    $('#form')[0].reset(); 
    var keyWords = $('#keyWords').html(); 
    localArray.push(Description); 
    localStorage.setItem('keyWords', JSON.stringify(localArray)); 
    loadKeyWords(); 
    return false; 
}); 

$('#clear').click(function() { 
window.localStorage.clear(); 
location.reload(); 
return false; 
}); 

$('#clearChecked').click(function() { 
    currentArray = []; 
    $('.check').each(function() { 
    var $curr = $(this); 
    if (!$curr.is(':checked')) { 
     var value = $curr.parent().text(); 
     currentArray.push(value); 
     localStorage.setItem('keyWords', JSON.stringify(currentArray)); 
     loadKeyWords(); 
    } else { 
     $curr.parent().remove(); 
    } 
    }); 
}); 


// Update the relevant fields with the new data 
function setDOMInfo(info) { 
    $("div p:contains(localStorage.getItem('keyWords')).parent('div').hide()"); 
} 

// Once the DOM is ready... 
window.addEventListener('DOMContentLoaded', function() { 
    // ...query for the active tab... 
    chrome.tabs.query({ 
    active: true, 
    currentWindow: true 
    }, function (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); 
    }); 
}); 


}); // End of document ready function 

background.js

chrome.runtime.onMessage.addListener(function (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(function (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); 
    } 
}); 
+2

Senza guardare l'intero codice, creando un nuovo DOM nella pagina di sfondo e inviandolo allo script di contenuto fa ** non ** suona liek un buon piano :) Si romperà la maggior parte delle pagine. La manipolazione del DOM dovrebbe avvenire sul posto (ad es. Iniettando uno script con la logica necessaria nella pagina). – gkalpak

+1

Basta inserire la logica di manipolazione DOM in uno script di contenuto e inviare le parole chiave (anziché inviare l'HTML, filtrarlo in background o nella pagina popup e inviarlo nuovamente allo script di contenuto). – gkalpak

+0

Non risolvere completamente il problema, ma ti suggerisco di dare un'occhiata a questa soluzione per cercare contenitori di testo http://stackoverflow.com/a/18089011/1705006. La ricerca di nodi padre diretti è più efficiente e più logica dei nodi div. – karmiphuc

risposta

11

È necessario utilizzare questa query per inviare dati a DOM dalla scheda corrente visualizzata.

chrome.tabs.executeScript(null, { 
     code: 'var config = ' + JSON.stringify(getKeywords) 
    }, function() { 
     chrome.tabs.executeScript(null, {file: 'custom.js'}); 
    }); 

e nel file custom.js è possibile scrivere voi funzione che si desidera applicare sull'elemento DOM. Mi piace se vuoi nascondere qualcosa di quello che ti serve nella query custom.js. Quindi, se volessi utilizzare quell'esempio, dovresti modificarlo in base alle tue esigenze.

var all = document.getElementsByTagName("div"); 
var searchValue=config.toString().split(','); 
alert('Example:' + searchValue[0]); 
for(j=0; j < searchValue.length; j++) { 
for(i=0; i < all.length; i++) { 
    if(all[i].innerHTML.indexOf(searchValue[j]) > -1){ 
    all[i].innerHTML = "" 
    } 
} 
} 
1

Si dovrebbe inviare il comando dal background.js o popup.js, che ricevono che e cambiare la dom in content.js. Il seguente codice mostra uno scenario semplice: fare clic su browserAction e aggiungere un div alla pagina corrente. Puoi usare la stessa logica per mostrare/nascondere qualsiasi elemento.

manifest.json

{ 
    "name": "Test", 
    "version": "1.0", 
    "permissions": [ 
    "tabs" 
    ], 
    "description": "Test", 
    "background": { 
    "persistent": false, 
    "scripts": [ 
     "background.js" 
    ] 
    }, 
    "content_scripts": [ 
    { 
     "matches": [ 
     "*://*/*" 
     ], 
     "js": [ 
     "content.js" 
     ], 
     "run_at": "document_end", 
     "all_frames": true 
    } 
    ], 
    "browser_action": { 
    "title": "Test" 
    }, 
    "manifest_version": 2 
} 

background.js

chrome.browserAction.onClicked.addListener(function() { 
    chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { 
     chrome.tabs.sendMessage(tabs[0].id, {command: "append"}, function(response) { 
      console.log(response.result); 
     }); 
    }); 
}); 

content.js

chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { 
    console.log(request.command); 

    var div = document.createElement('div'); 
    var label = document.createElement('span'); 
    label.textContent = "Hello, world"; 
    div.appendChild(label); 
    document.body.appendChild(div); 

    sendResponse({result: "success"}); 
}); 
+0

Sei in grado di cambiare l'esempio che hai mostrato di usare la mia situazione? – wuno

3

cerco di rispondere a questa domanda semplice come sempre, perché fare meno modifiche al tuo codice imparerai velocemente il percorso da seguire.

Normalmente scrivo le seguenti righe di codice quando l'utente finale preme un pulsante START su popup (fare riferimento al popup.js):

 
chrome.runtime.sendMessage({key: 'popupInit'}, function (response) { 
; 
}); 
window.close(); // this line closes the popup 

Ciò che è molto importante è capire la risposta non è un sistema di comunicazione, ma semplicemente una risposta al volo proveniente dal corrispondente ascoltatore. Il listener per me è background.js. In questo file è possibile sfruttare la localStorage modo che si può avere qualcosa di simile:

chrome.tabs.onUpdated.addListener(function(tabid, changeInfo, tab) { 
    if (typeof changeInfo.status == 'string' && changeInfo.status == 'complete') { 
    chrome.tabs.query({active: true, currentWindow: true}, function (tabs) { 
     var keyString = JSON.parse(localStorage.getItem('keyWords')); 
     chrome.tabs.sendMessage(tabs[0].id, {key: 'init', wordToHide: keyString}, function (response) { 
     ; 
     }); 
    }); 
    } 
}); 

chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { 
    var rq = request.key; 
    if (rq != undefined && typeof rq == 'string') { 
    switch (rq) { 
     case 'popupInit': 
     chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { 
      var keyString = JSON.parse(localStorage.getItem('keyWords')); 
      chrome.tabs.sendMessage(tabs[0].id, msgToSend, function(response) { 
      ; 
      }); 
     }); 
     break; 
    } 
    } 
}); 

Il chrome.tabs.onUpdated.addListener è importante perché ogni volta che la pagina corrente attiva si viene aggiornato può inviare il messaggio allo script di contenuto ottenendo i valori dalla localstorage, lo stesso accade quando il popup è chiuso e se ci sono parole in localstorage puoi usare chrome.runtime.onMessage.addListener per ascoltare il segnale: popup chiuso così inizia il lavoro nello script di contenuto inviandogli un messaggio con l'array di parole.

Ora diamo un'occhiata al file content.js.

// Listen for messages from the backgound.js 
chrome.runtime.onMessage.addListener(function (msg, sender, response) { 
    // First, validate the message's structure 
    if ((msg.key === 'init') && (Object.prototype.toString.call(msg.wordToHide) === '[object Array]')) { 

    var elements = document.querySelectorAll("body, body *"); 
    var results = []; 
    var child; 
    var regwordToHide = []; 
    if (msg.wordToHide.length > 0) { 
     msg.wordToHide.forEach(function(element, index, array) { 
     regwordToHide.push(new RegExp('\\b' + element + '\\b', 'g')); 
     }); 
    } 
    for(var i = 0; i < elements.length; i++) { 
     child = elements[i].childNodes[0]; 
     if (elements[i].hasChildNodes() && child.nodeType == 3) { 
     var nodeStr = child.textContent; 
     if (nodeStr.trim().replace(/\n\r\t/g, '').length > 0 && nodeStr.trim().charAt(0) != '<') { 
      regwordToHide.forEach(function(element, index, array) { 
      nodeStr = nodeStr.replace(element, ''); 
      }); 
      child.textContent = nodeStr; 
     } 
     } 
    } 
    document.getElementsByTagName("html")[0].style.visibility = "visible"; 
    } 
}); 
document.getElementsByTagName("html")[0].style.visibility = "hidden"; 

cerco di nascondere l'intera pagina: prestare attenzione a questo perché può essere complicato (ricordate l'utente finale deve sempre vedere qualcosa mentre la pagina si sta caricando ....).

Dopo, il content.js attende un messaggio proveniente dallo sfondo e quando questo accade lo script di contenuto inizia il suo lavoro! Questo è tutto.

Ci scusiamo per la confusione nella mia scrittura. Se hai bisogno di qualche altro aiuto fammi sapere. Ho testato il tuo ext e l'ho corretto nei posti che puoi vedere.

Per la comunicazione tra i componenti in un'estensione Chrome è necessario ricordare:

  • lo sfondo è il grande ascoltatore
  • il popup comunicare con lo sfondo
  • sfondo comunicare con il contenuto

Per inviare/ricevere messaggi è possibile dare un'occhiata a Chrome messaging

  1. la funzione chrome.runtime.sendMessage ({saluto: "ciao"}, funzione (risposta) { console.log (response.farewell); }); è USEND inviare
  2. chrome.runtime.onMessage.addListener è il server (ricevitore)

Il sendResponse e la risposta sono utilizzati solo per un controllo al volo per quanto riguarda la comunicazione: qualcosa di simile: Ciao -> Ok ho capito, ora continuo da solo.

Ho completato anche la tua parola sostituendo parte!