I have a webextension, firefox but I think this applies to chrome as well, which executes (chrome.tabs.executeScript, called in the background.js) a content script on a browserAction.onClicked listener. This content script more or less creates an dom element on the current tab using some data from the page. This element has some html code inside and everything works swimmingly -- except I'm trying to get it's syntax highlighted. Now, there are a few different libraries which do this (Prism, HighlightjS, Rainbow, and some others).
The general idea with these libraries, if I'm not mistaken, is to call colorify, or some such function, on a <code> div and then using a css sheet highlight that syntax. I have tried a number of things to get both/either the relevant js or css scripts to be sourced onto a page:
such as, executeScript along with the content script in my background.js, in this case I'd include this script inside my background declaration in the manifest.json:
chrome.tabs.executeScript(null, {
file: "/content_scripts/highlight.pack.js"
});
chrome.tabs.executeScript(null, {
file: "/content_scripts/my_content_script.js"
});
I've also tried loading it in via the manifest (which is also what I've tried with the css):
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content_scripts/highlight.pack.js"],
"css" : ["content_scripts/solarized-light.css"]
}
],
In either case I've then tried a few things to load this/these libraries into the content script which is doing the aforementioned main lifting (creating a div on the page):
I've tried simply letting the library, such as Prism do it's thing (by putting my <code> inside a <pre> and giving it the appropriate className, or even calling it manually from the content script:
var Prism = { highlightElement: function() {} };
window.addEventListener('load', () => {var Prism = window.Prism; });
Or more simply
Rainbow.color(div, function() {
document.body.appendChild(div);
}
// or
window.Rainbow.color( //... etc
or simpliest of all:
window.hljs.initHighlightingOnLoad();
// or
hljs.initHighlightingOnLoad();
Part of my problem, and why I am discouraged, is that when I open the inspector/debugger sometimes I'll see the js file sourced, but I won't be able to call any functions from the console. And, moreover, the css files never seem to get sourced.
So the basics of what I'm looking to do are to call a 3rd party javascript library inside my content script and source a css sheet from my webextension into the activeTab my content script is modifying.
How should I include the libraries?
How can I debug the content script, to test how to use the libraries?
So #Xan helped be get around to debugging this using a breakpoint inside the content script inside the Firefox console.
I ran the background script, which executes the content script
My manifest includes the css necessary for syntax highlighting. I could also include the js file here, but instead I add it to the background.js as something I execute (chrome.tabs.executeScript()) So that way it's only run if I need it. (Otherwise it'll run all the time.
In the inspector Debugging tab I put a breakpoint inside my main content script
Inside the breakpoint I was able to use the console to call the syntax highlighting library as if it's a global variable.
From there I was able to test how to call the script etc. I eventually called highlightjs's hljs object without a window specifier. Just calling, inside my content script:
hljs.highlightBlock(myDiv);
Related
I'm starting fresh with a new blank Intel project and I haven't even started coding yet. I'm just setting up my file tree and making sure the html and javascript pages are connected via the right anchors and script paths. My first .js file won't work though.
I've included a screen shot of test code and the errors. I know the syntax is correct because it works when I put it in <script> tags in the index.html file.
I'm getting "document not defined" and "alert not defined" errors or the js page though. I don't know what that means.
I've considered that my script tag src path in the index file is incorrect, but all the paths are relative in the commented out template script tags intel provides on the index page right out of the box, so why would I have to use an absolute path?
My path is: js/Test.js and it's the last script tag before the body.
Index.html file
*****UPDATE****
So I've tried a few things and it's still not working but I HAVE managed to get my errors down to just one inexplicable "missing semicolon", which will turn into an "unnecessary semicolon" error if I place it.
Any way as per the first screen shot you'll see that I wasn't placing the document object inside of an explicitly declared variable. Once I did that and accessed it through dot syntax instead of an equal sign then I stopped getting the error. I included this screenshot to show my work before I made the changes.
so the problem I went on to have is that unless every function or dom object was declared with "Var", I'd get an error. This includes the alert() function which I don't think I've ever seen needing to be declared that way, but I gave the code editor what it wanted and this last screenshot is the results. It's not working, BUT I'm not getting the errors I was before, except for the missing/unnecessary semicolon paradox. Removing it or including it throws an error.
JavaScript can be loaded before or after the HTML, however the way it is done is slightly different depending on how you do it.
For example if you wish to include your JavaScript files within the head of the HTML file then you must wrap your JavaScript code with either DOMContentLoaded or jQuery's $(document).ready().
The common misconception of using window.onload will not fix the issue where the elements have not loaded in correctly.
The Mozilla Developer Network states on this page:
The DOMContentLoaded event is fired when the initial HTML document has been completely loaded and parsed, without waiting for stylesheets, images, and subframes to finish loading. A very different event - load - should be used only to detect a fully-loaded page. It is an incredibly popular mistake to use load where DOMContentLoaded would be much more appropriate, so be cautious.
That quote in itself should prove that onload should not be relied on if you want the full DOM to be loaded properly before you start manipulating it. Instead you should do the following:
Vanilla
document.addEventListener("DOMContentLoaded", function (e) {
/** DOM has been fully loaded here, so manipulation can begin. **/
/** Your code here. **/
});
jQuery
$(document).ready(function () {
/** DOM has been fully loaded here, so manipulation can begin. **/
/** Your code here. **/
});
Click this link to see the difference between vanilla and jQuery.
The second way you can load JavaScript is by having all the script tags within the body but after all the HTML, that way it is guaranteed to load after the HTML.
Example
Try this working example I quickly coded up.
HTML
Replace the content within the body tag of your HTML to the following:
<button id="myJsTest">Click Me!</button>
<div id="clickCounter">Click Count: 0</div>
Replace the contents of your JavaScript with the following:
JavaScript
document.addEventListener("DOMContentLoaded", function() {
var clickCount = 0;
document.getElementById("myJsTest").addEventListener("click", function() {
clickCount++;
document.getElementById("clickCounter").innerText = "Click Count: " + clickCount;
});
});
Then use the Emulate tab in Intel XDK to test it.
Additional Information
When I use Intel XDK and I have an error, I quickly load the file in to the browser and check the console. It can be a really helpful and effective way of squashing those pesky little bugs.
Try using window.alert as alert is defined in the window object.
The reason you're seeing all those "error" messages in the editor window is because you've got the various JSLint/Hint tools loaded in the editor. They're trying to keep you honest and save you lots of time in the debugger chasing potential syntax errors.
The editor inside the XDK is Brackets, it is using standard Brackets extensions to provide those JSLint/Hint tools. You can download it and run it standalone on your system and edit directly within it, you don't have to use the editor inside the XDK (or you can use any other editor you like).
Because the Lint/Hint tools only look at one file at a time, and because your app is normally spread over multiple files, they don't know much about about what's defined in your other files. Likewise, those hint/lint tools need to be told that you're using some of the standard global methods and properties that are expected to be found in a browser (but which may not be found in other JavaScript environments, because JavaScript is no longer limited to just a browser environment -- in fact, your XDK app, aka Cordova app, runs inside a "webview" not in a browser, but that's another story...)
So, you should follow some standard practice of setting up your JSHint/Lint directives at the top of your JS files. For example, this is a good starting point:
/*jslint browser:true, devel:true, white:true, vars:true */
/*global $:false, intel:false */
See the JSHint documentation for details... and see the "Blank Cordova Starter App" in the "Start a New Project" section of the Projects tab for a better blank template to start (there is no real difference between a blank template and a demo app, they are structured identically).
For a more complete and even more instructive app, see the "Hello, Cordova" sample app. Both of those apps can also be found on the Intel XDK GitHub repo.
You are adding the js file that is <script src="js/Test.js"></script> inside header tag.
So js will be first loaded and and it will attach all events to it. But when js is loaded button id="jsTest" is not present because DOM is not loaded.
Solutions :-You can follow either of the approach
Add your js files after DOM is ready
<body>
<button id ="js/Test.js">Test JS</button>
// other HTML tags
<script src = "js/Test.js></script>
</body>
Use window.onload
The load event fires at the end of the document loading process.
window.onload = testJsFile(){
//Your code goes here
}
I will prefer to use the first approach since that also address other issues like page loading time
Try to put the line outside your JS function
document.getElementById(......
inside your html page between
<script>HERE</script>
If this is still no working. Try to add onClick attribute to the button like this:
<button id="" onClick="testJsFile()">
Its also good to use google chrome element inspection while devlopping cuse it will give you error msg for all these things.
Little things like this drive me crazy!
Working with a typical Joomla website and all it's complexity.
Start with a functioning website. Edit a particular javascript file of a template to add a simple function:
function socialShare(title, url) {
alert("goop!");
}
I reference it in a link:
<a class="icon-facebook"
onclick="socialShare('blog-entries', '11-blog-article/15-oppressive-tyranny&title=Triumph Over Tyranny')"> </a>
The function is included along with 37 other external scripts in a file at the end of the page, included within the body element like this:
<script src="/bts/templates/vg_progressive/js/articleRev.js"></script>
I inserted the sociaShare function definition as the first thing in the include articleRev.js file. The list of external scripts include core functionality like bootstrap, jquery etc etc.
I can see the function in the included script file with firebug's debugger & the include line in the page source near the bottom. BUT IT DOESN'T GET EXECUTED!! If I include the exact same function within a element anywhere on the page it works just fine. For some reason the instance in the external script file is not within the scope of that page / article, tho I can see it clearly in the page source.
I can also put the function within script tags as the last thing inside the body and it is within scope and works fine. I discovered I can define the function in a different included javascript file that is found in the very same folder and it also works fine.
Like I said, things like this make me pull my hair out! What can cause this behavior? How can I narrow it down?
As per Ed Cotrel, here are 2 files which [may] be helpful, tho I don't think so. Rename articleRev.txt to .js and pageSrc.txt to pageSrc.html. As for your comments Ed, I believe I've stated the issue as clearly as I can.
The desired behavior is to see an alert box when the anchor tag is clicked. Simple. The anchor tag displays a social media icon based on one of the class definitions. The onclick attribute should call the javascript function socialShare and display an alert box containing a text message with the 2 parameters. The alert never shows up. If the socialSharing function is moved to the main.js script file located in the same folder and included the same way in the same place in the page flow it works. Is that clearer?
1) http://thecomingbitsharesrevolution.website/media/articleRev.txt
2) http://thecomingbitsharesrevolution.website/media/pageSrc.txt
I am very new to HTML and the complete web world wrt development. I am trying to create a chrome extension and have decided upon showing the extension UI to the user by injecting a content script when the browser action is clicked. I have created an HTML page for the UI to be shown and now I need to load this HTML page as a small widget somewhere on the right top of the current page (to appear like a popup). From the different things looked up on the internet, I have decided to use shadow DOM but have no clue how to do all this. Can someone help me with a sample code to help me go ahead with this? I am not able to write a javascript that would be used as the content script to do the above mentioned job.
Edit 1: After some more reading, I found out out that we need to create the element hierarchy using javascript and cannot directly make use of any created HTML page. Is that correct? And if we have to make use of javascript, should make use of calls to document.createElement and add element? or document.write?
Just compiling reference from the responses I've got in this thread:
My Background.js:
function hello() {
chrome.tabs.executeScript(null, { file: "injectedScripts/jquery-2.1.3.min.js" }, function () {
chrome.tabs.executeScript(null, { file: "injectedScripts/a.js" });
});
}
// Supposed to Called when the user clicks on the browser action icon.
chrome.browserAction.onClicked.addListener(hello);
Injected Script a.js:
$.get(chrome.extension.getURL("popup.html"), function (data) {
//$(data).appendTo('body');
// Or if you're using jQuery 1.8+:
$($.parseHTML(data)).appendTo('body');
});
Also added popup.html and a.js paths to web_accessible_resources in manifest.json
For Chrome extension content scripts you'll only be able to inject JavaScript/CSS. You won't be able to have you're own HTML popup page like when using browserAction.
With JavaScript you'll need to dynamically create elements and insert them into the DOM. You're on the right track. You can use document.createElement or a library like jQuery to help with DOM manipulation as you get more familiar with web dev.
With a content script you can inject a script tag into the DOM in order to access variables in the original page (as explained in this question).
I want to avoid injecting my code into every page and instead only do that when the user clicks on the extension icon.
When I tried using the same code as for the content script the values were undefined, although the script was inserted correctly.
Is this possible? Otherwise is using a content script and communicating with it the preferred solution?
Here is the code I'm using:
var scr = document.createElement("script");
scr.type="text/javascript";
scr.innerHTML = "setInterval('console.log(window.testVar)', 1000)"
document.body.appendChild(scr)
Manifest excerpt:
"permissions": [
"tabs",
"http://*/*", "https://*/*"
],
"background": {
"scripts": ["inject.js"]
},
Nope. That's not possible. You may inject a script, but it only have an access to DOM and it could make DOM manipulations. It can not read javascript variables or execute functions in the context of the current page. Your javascript code is run in a sandbox and you are restricted only to the DOM elements.
You can create a new background script with the following code:
chrome.browserAction.onClicked.addListener( function() {
chrome.tabs.executeScript( { file: 'inject.js' } );
});
Your inject.js should stay as part of the extension, but you don't need to mention it in the manifest. This way, it will be run as a content script each time you press the extension icon. You didn't include the part of the manifest where you define the browserAction, but you just need to not specify a default_popup.
You can utilize a background script to execute JavaScript within the context of the page, however, your JavaScript is executed in an isolated environment from the JavaScript that is loaded from within the page.
Consider the following:
In page.html:
<button id="myButton" onclick="console.log('Message from within the page');" />
In background script:
chrome.tabs.executeScript({
code: '$("#myButton").click(function() { console.log("Message from background script."); });'
});
Now, if you click the button within the page, you will find both log messages in the console window. The JavaScript contexts are isolated from each other, with the notable exceptions of messaging via postMessage and through the shared DOM.
I'm trying to run a javascript command in a content script using Personalized-Web (a Chrome extension). I'm new to javascript & jquery, but I've found that entering this code:
javascript:jQuery("div.photo-container").die();
into my browser bar on a particular page achieves the desired result: it undoes a .live call performed in one of the page's javascripts.
However, if I include that same code or $("div.photo-container").die(); in a content script, it does not work. I've also attempted including this script tag in the page context:
<script type="text/javascript">
$("div.photo-container").die();
</script>
and chrome claims that $ or jQuery are not defined. However, the page's own javascripts don't include or refer to the jQuery source at any point, as far as I can tell.
So, what's the difference between the browser bar, the content script, and the in-page <script> tag? How can I use one of the 'automatic' methods (i.e., not paste it into the browser bar)?
If you inject that script tag into the page, it should work. For example:
// Runs in the context of the webpage.
var injectScript = function() {
$("div.photo-container").die();
}
// Runs in the context of the content script. We basically just append the DOM
// with the injected script and execute it so it will run.
var script = document.createElement('script');
script.appendChild(document.createTextNode('(' + injectScript + ')();'));
document.body.appendChild(script);
The above should work assuming your page has defined the $. If the $ doesn't exist, that means jQuery didn't load yet. Make sure your manifest has document_end defined as well.
...
"content_scripts": [
{
"matches": ["http://www.rim.com/*"],
"js": ["content_script.js"],
"run_at": "document_end"
}
],
...
If that doesn't work then Sometimes depending on the website, it is good to lazy load or smart load till the content (script) gets loaded because the webdesigner is asynchronously loading the scripts or content. In that case, do some research to see how jquery is being loaded. For example, do a timer with setTimeout or DOM events to figure out when it is time to run your injection. I do that many times on difficult websites such as Google+ Extensions.