Create sidebar by manipulating the DOM of the loaded webpage - javascript

My simple Chrome extension is injecting a DIV at the top of document.body, then you can drag text from your page into the extension. The problem is that I want the extension DIV not to be located at top, but instead be something like a side bar to the left.
In other words I need to know how to programatically rearrange already loaded DOM structure so that all content is moved to the right and horizontally compressed and then the left area is accessible to further manipulations.
One option I was considering was to do this:
tmp = document.body.innerHTML
document.body.innerHTML = '<table><tr><td id=sidebar></td><td>'
+ tmp + '</td></tr></table>'
But this will be inneficient, will cause rerender and may have other undesired side effects.
By the way current version of the extension will inject every page "on load", but this is just a temporary solution, the side bar must be displayed when the extension button is clicked. This is not part of this question, I know how to do that. Just to let you know that the sidebar creation could be done at any time when user chooses to click the button. That's why using innerHTML is not a good option.
pageload.js
function allowDrop(ev) {ev.preventDefault()}
function drop(ev) {
ev.preventDefault();
var t = 'text', T = Array.prototype.slice.apply(ev.dataTransfer.types)
if (T.indexOf('text/html') >= 0)
t = "text/html"
console.log('text type:', t)
d1.innerHTML += '<div style="display:inline;border:2px solid #000000;">'+ev.dataTransfer.getData(t)+'</div> '
}
function createDragbar(id) {
var n = document.createElement('div')
// n.style.position = "absolute";
n.setAttribute('id', id)
n.style.border = '1px solid #aaaaaa'
n.style.height = 532
n.style.background = "teal"
n.innerHTML = "Drop your text here "
n.ondrop = drop
n.ondragover = allowDrop
document.body.insertBefore(n, document.body.firstChild)
}
createDragbar('d1')
manifest.json
{
"name": "Yakov's Demo Extension",
"description": "Just a demo",
"version": "0.1",
"permissions": [
"activeTab"
],
"content_scripts": [{
"matches": ["http://*/*", "https://*/*", "file://*/*"],
"js": ["pageload.js"]
}],
"manifest_version": 2
}

What about element.insertAdjacentHTML(position, text)?
https://developer.mozilla.org/en-US/docs/Web/API/Element.insertAdjacentHTML

Related

Edit - Selecting an image on Instagram is not working on my chrome extension

EDIT #2
I've just tried selecting every div element on the website as each image is contained in a div. So, I used querySelectorAll followed with an if statement. The if statement checks the div and makes sure the image (class: FFVAD) is actually inside of the div before proceeding. But it's not working, it's now throwing a getElementsByClassName is not a function error.
My content script:
console.log('injected')
window.onload = function() {
var imagediv = document.querySelectorAll('div')
console.log('selected elements')
var i;
for (i = 0; i < imagediv.length; i++) {
imagediv[i].addEventListener('mouseover', function(el){
if (el.getElementsByClassName('FFVAD').length > 0) { //if the image with the class FFVAD is in the div
el.target.style.border = "7px solid red" //change the certain div's color
}
})
}
}
PREVIOUS QUESTION
I am developing a chrome extension which injects a javascript-written script into Instagram. I am trying to get the src of each image displayed on the users profile.
I have tried using document.querySelectorAll('img') to get every image element, but that isn't working. So, I took a peek in the developer console and noticed each image has a class called FFVAD. So, I used document.getElementsByClassName('FFVAD') (reference here). I also noticed each image has a div element as their parent. So, I tried doing this with my code:
My content script:
console.log('injected')
var image = document.getElementsByClassName('FFVAD') // instagram sets all
their images as the class FFVAD, as I looked in the inspect element window
console.log('selected elements')
while(image.length > 0){ //for every image
image[0].parentNode.addEventListener("mouseover", function (event) { //when the image's parent is hovered
event.style.border = "2px solid red";
});
image[0].parentNode.addEventListener("mouseout", function (event) { //when the image's parent isn't hovered
event.style.border = "none";
});
image[0].parentNode.addEventListener("click", function(event){ //when the image's parent is clicked
var child = event.childNodes; //get the image in the image's div
console.log(child[1].src) //log the src of the image
});
}
My exensions manifest:
{
"manifest_version": 2,
"name": "Testing",
"description": "Test extension",
"version": "0.0.1",
"author": "tester123",
"icons":{
"16": "icon.png",
"48": "icon2.png",
"128": "icon3.png"
},
"content_scripts": [{
"all_frames": false,
"js": ["imgselect.js"],
"matches": [ "https://*.instagram.com/*"],
"run_at": "document_end"
}],
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": [
"tabs"
]
}
I used parentNode to get the parent of the image. I expect to see a red border around the image's div parent when it's hovered and seeing the src of the image in the console but I'm not seeing results. Any help will be appreciated, thank you!
This worked for me!
I looked deeper into the developer console and realized every image on Instagram is inside of multiple divs, so I put together this code that highlights the main div that contains the image, and it works perfectly!
console.log('injected')
function show(e) {
console.log("post found")
var img = this.getElementsByTagName('img')
for (i = 0; i <img.length; i++){
if (img[i].className == "FFVAD"){
console.log(img[i].src)
this.style.border = "5px solid red"
}
}
}
function hide(e){
var img = this.getElementsByTagName('img')
for (i = 0; i <img.length; i++){
if (img[i].className == "FFVAD"){
console.log(img[i].src)
this.style.border = "none"
}
}
}
setInterval(function(){
var imagediv = document.getElementsByClassName('KL4Bh')
var i;
for (i = 0; i < imagediv.length; i++) {
var parent = imagediv[i].parentNode
var secondparent = parent.parentNode
var thirdparent = secondparent.parentNode
var children = parent.children
if (children.length > 1){
children[1].remove()
thirdparent.addEventListener('mouseover', show)
thirdparent.addEventListener('mouseout', hide)
}
}
},1000)

Google Chrome Extension: How to use an external script

I am trying to make a markdown preview tab for a forum I often use that uses markdown in it's formatting but currently does not have any way to preview what that markdown will look like.
I want to use the page down markdown converter however I don't know how to use the files inside the content script
here is my manifest.json
{
"name": "Forum Post Previewer",
"version": "0.1",
"manifest_version":2,
"description":"Adds a preview tab on the post editor",
"permissions": [
"activeTab",
"*://*/*"
],
"content_scripts": [
{
"js": ["previewtab.js"]
}
],
}
and here is the previewtab.js
// Adding the preview tab
var tabs = document.getElementsByClassName("nav nav-tabs");
var list = document.createElement("li");
var tab = document.createElement("a");
tab.innerHTML = "Preview";
tab.setAttribute("data-toggle", "tab");
tab.setAttribute("href", "#tab3");
list.appendChild(tab);
document.getElementById("post-editor").parentElement.firstElementChild.appendChild(list);
var content = document.createElement("div");
content.setAttribute("class", "tab-pane");
content.setAttribute("id", "tab3");
var bar = document.createElement("div");
bar.setAttribute("id", "wmd-button-bar");
var textarea = document.createElement("textarea");
textarea.setAttribute("id", "wmd-input");
textarea.setAttribute("class", "wmd-input");
var preview = document.createElement("div");
preview.setAttribute("id", "wmd-preview");
preview.setAttribute("class", "wmd-panel wmd-preview");
content.appendChild(bar);
content.appendChild(textarea);
content.appendChild(preview);
document.getElementById("post-editor").appendChild(content);
// Using the converter
var converter = Markdown.getSanitizingConverter();
var editor = new Markdown.Editor(converter);
editor.run();
right now I get errors when using the converter because it does not know where Markdown has come from.
Can you help me to find out how to use this external script in a chrome extension
Thanks
Just add the .js files to your extension and include them as content scripts before yours, like this:
"content_scripts": [
{
"js": ["Markdown.Converter", "Markdown.Editor", "Markdown.Sanitizer", "previewtab.js"]
}
],

Chrome extension disables js triggers

I have made a little Chrome extension that injects some code in the current page.
This extension has a weird behaviour though, whenever the code is injected, none of the page's Javascript triggers seem to work anymore.
Would one of you have any idea what that happens? On top of fixing the code I'd really like to know why this happens.
Example : on this page : http://www.acti.fr/success-story/ghd/ if the extension injects the picture, I cannot click on either the menu or "continuer la lecture" at the bottom.
Here are the manifest and the actual code :
manifest.json
{
"manifest_version": 2,
"name": "wpi",
"description": "just an other extension",
"version": "1.0",
"content_scripts": [{
"matches": ["http://*/*", "https://*/*"],
"js": ["my-style.js"]
}]
}
my-script.js :
function wpkm_check_content(wpkm_text) {
var wpkm_word = wpkm_text.split(" ");
var wpkm_c = wpkm_word[0].localeCompare("Wordpress");
if (wpkm_c == 1)
return (1);
return (0);
}
var wpkm_html = '<div id="wpkm-bloc" style="position:absolute;right:10px;top:10px;z-index:99999">';
wpkm_html += '<img id="wpkm-img" src="https://nathanarnold.files.wordpress.com/2009/02/ssim51.gif">';
wpkm_html += '</div>';
var wpkm_sdomain = document.domain;
var wpkm_request = new XMLHttpRequest();
wpkm_request.open('GET', '/license.txt', true);
wpkm_request.onreadystatechange = function(){
if (wpkm_request.readyState === 4){
if (wpkm_request.status === 200
&& wpkm_check_content(wpkm_request.responseText) == 1) {
document.body.innerHTML += wpkm_html;
}
else {
console.log("Oh no, it does not exist!");
}
}
};
wpkm_request.send();
Any hints will be appreciated :D
You're effectively reassigning the entire innerHTML of the document body by using += append operator which causes reevaluation and recreation of the entire page and of course all previously attached event handlers aren't reattached automatically.
Use insertAdjacentHTML instead:
document.body.insertAdjacentHTML("beforeend", wpkm_html);

Content script & jQuery. Download/get DOM from different web page

I'm trying to modify img src attribute by getting the url from different page. Thing is i need to find it by inspecting DOM as it's not static data; i will be looking via classes and IDs.
My knowledge of chrome extensions is very limited at the time. Basicly i just started.
Look at the "PSEUDO CODE" part of background.js
manifest.json
{
"manifest_version" : 2 ,
"name": "#####" ,
"version": "1.0" ,
"description": "#####" ,
"browser_action":
{
"name": "#####" ,
"icons": ["icon.png"] ,
"default_icon": "icon.png"
},
"content_scripts": [
{
"js": [ "jquery.min.js", "background.js" ] ,
"matches": [ "http://*.#####.com/encounters/promospp.phtml"] ,
"run_at": "document_end"
}]
}
background.js
var l = document.getElementsByTagName("div");
for (var i = 0; i < l.length; i++)
{
var obj = l[i].parentNode;
if (l[i].getAttribute("class") && l[i].getAttribute("class") == "user_contact")
{
var div = l[i];
var id = div.getAttribute("id").replace("u_", "0");
var profileUrl = "../" + id + "/";
var imgs = div.getElementsByClassName("userpic");
log("found img.userpic : " + imgs.length);
if (imgs && imgs.length > 0)
{
var img = imgs[0];
var alink = document.createElement('a');
img.parentNode.appendChild(alink);
alink.setAttribute("href", profileUrl);
alink.appendChild(img);
// PSEUDO CODE - the unknown
//
// download profileUrl page html
// search for given div element
// pull src attribute value from it
// apply it to img here
}
}
}
So in essence. How to download different page and work with it
Since you are including it already and tagged your question with it, I am going to answer with jQuery, I hope you don't mind. So first I rewrite what code you have in jQuery:
$('div.user_contact').each(function(){
var id = $(this)[0].id.replace('_u','0');
var profileUrl = "../" + id + "/";
var imgs = $(this).find('.userPic');
if(imgs.length > 0){
var alink = $(document.createElement('a'));
$(this).append(alink);
$(alink).attr('href',profileUrl);
$(alink).append(imgs[0]);
//Here is where you get the page and search for the div you want
$.get(profileUrl,function(data){
//Since I don't know the layout of what you are looking for
//I will just put in some placeholder
$(imgs).first().attr('src',$('img.youWant',data).first().attr('src'));
});
// Since $.get is asynchronous, doing it like this might cause problems
// if there is more than one div.user_contact.
// In the case where there are a low number of them and you are fine running
// blocking synchronous code, then you can do it with this instead:
// $.ajax(profileUrl,{async:false,success:function(data){
}
});
You'll also need to include permissions in your manifest for the site you are $.geting from. Something like this:
"permissions":["*://*.badoo.com/*"]
Using BeardFist solution + few fixes final code looks like this:
$('div.user_contact').each(function()
{
var id = $(this)[0].id.replace('u_','0');
log(id);
var profileUrl = "../" + id + "/";
log(profileUrl);
var imgs = $(this).find('.userpic');
if(imgs.length > 0)
{
var alink = $(document.createElement('a'));
$(imgs[0]).parent().append(alink);
$(alink).attr('href',profileUrl);
$(alink).append(imgs[0]);
$.get(profileUrl, function(data)
{
$(imgs[0]).attr('src',$('img.pf_phts_b_pht', data).attr('src'));
});
}
});
It works excelent. Entire html is inside data and it even stays loged and stuff :)

Apply Angular.js to new DOM Element in Chrome Extension Content Script

Okay, so I've been at this for a while.
In the red box, I want to have some angular functionality (ng-repeats, data binding, etc...) This red box appears when text on any webpage is double clicked. However, I can't seem to find out how to actually get angular wired/hooked up to the text box example in the red popup.
It seems fairly trivial to use angular in a badge popup in chrome, as well as on options pages, etc...but I can't seem to get it working in this instance.
inject.js (which is included as a content script in manifest, below)
var displayPopup = function(event) {
var mydiv = document.createElement('div');
var $div = $('#divid').closest('.sentence');
mydiv.innerHTML = getSelectionText();
mydiv.innerHTML += currentSentence.innerHTML;
//Next line is where I want to apply some angular functionality
mydiv.innerHTML += '<div ng-app="myApp" scroll-to-me><input type="text" ng-model="data.test"><div ng-model="data.test">{{data.test}}</div></div>';
mydiv.id = "popup";
mydiv.style.position = "fixed";
mydiv.style.top = event.clientY + "px";
mydiv.style.left = event.clientX + "px";
mydiv.style.border = "4px solid #d00";
mydiv.style.background = "#fcc";
$("body").append(mydiv);
$.getJSON('http://local.wordly.com:3000/words/definitions/test', function(data) {
console.log(data);
});
}
and my manifest.json content script array looks like:
"content_scripts": [
{
"matches": [
"https://www.google.com/*"
],
"css": [
"src/inject/inject.css"
]
},
{
"matches": [
"http://*/*",
"https://*/*"
],
"js": [
"js/angular/angular.js", "app.js", "js/jquery/jquery.js", "src/inject/inject.js"
]
}
]
and app.js, also included in manifest, for just some skeletal app to get up and running.
var myApp = angular.module("myApp", []);
myApp.factory('Data', function(){
//return {message: "I'm data from a service"};
});
myApp.controller("SecondCtrl", function($scope, $http){
});
You need to bootstrap manually if you’re injecting the markup after the page loads. Angular will only run ng-app if it’s present when the document is loaded. Afterwards, you pass the module name to angular.bootstrap:
angular.bootstrap(mydiv, ['myApp'])
Example.

Categories