Injecting code into the web page does not work - javascript

This is a follow-up to a question I asked yesterday.
I have a userscript (kind of like GreaseMonkey script, but for Chrome).
The idea is to add a textbox and a button to the page. Then when the user clicks the button, it kicks off a function that does stuff. So I inject the textbox, button and the function into the page, but when the user clicks the button, the Chrome console tells me "Uncaught TypeError: object is not a function". So obviously it does not see the function I just injected and that is specified in the onclick event for the button.
So I have code like this:
initialize();
function initialize() {
var dropDown = document.getElementById("tstGlobalNavigation_ddlChooseProject");
// add a textbox
dropDown.innerHTML = dropDown.innerHTML + " <input type='text' name='txtSearch' style='position:absolute;top:8px;left:800px;width:50px' >";
// add a button
dropDown.innerHTML = dropDown.innerHTML + " <input type='button' name='btnSearch' value='Go' onclick='fn()' style='position:absolute;top:8px;left:860px;width:35px'>";
addScript("var obj = document.getElementById('txtSearch'); "
+ "if (obj != null) { "
+ " var incidentId = document.getElementById('txtSearch').value; "
+ " var currentURL = location.href; "
+ " var splitResult = currentURL.split('/'); "
+ " var projectId = splitResult[4]; "
+ " location.href = 'http://dev.myApp.com/ProductTeam/' + projectId + '/Incident/' + incidentId + '.aspx'; "
+ " }"
, "fn");
}
function addScript(contents, id) {
var head, script;
head = document.getElementsByTagName('head')[0];
script = document.getElementById(id);
if(script != undefined) {
head.removeChild(script);
}
script = document.createElement('script');
script.type = 'text/javascript';
script.id = id;
script.innerHTML = contents;
head.appendChild(script);
}
What am I missing here?

You're not calling the function you created, you're using the id that you give to the script tag... Try changing the code to
addScript("function fn() { var obj = document.getElementById('txtSearch'); "
+ "if (obj != null) { "
+ " var incidentId = document.getElementById('txtSearch').value; "
+ " var currentURL = location.href; "
+ " var splitResult = currentURL.split('/'); "
+ " var projectId = splitResult[4]; "
+ " location.href = 'http://dev.myApp.com/ProductTeam/' + projectId + '/Incident/' + incidentId + '.aspx'; "
+ " } }"
, "fn");
and you will have a fn() function that can be called

The problem is that you're trying to use the onclick element attribute to bind an event handler. These attributes are only parsed when the page is first loaded, at which time the function you're trying to bind as the callback doesn't exist.
Avoid binding event handlers in element on* attributes whenever possible. This is called writing unobtrusive JavaScript.
That said, if you absolutely must stick with using onclick, you can bind to a dummy function which does nothing but turn around and call the function that you inject:
<button onclick="wrapper()"
Where wrapper looks something like this:
function wrapper() {
return functionThatWillEventuallyBeInjected();
}

I'm not sure you can actually create script tags on the fly and push content in it.
You could instead create the script tag and modify the src attribute to some JS file:
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "path/to/your/javascript.js";
document.body.appendChild(script);
If you really need to execute a code stored in a string, maybe you could simply eval() it when required!

Related

access to DOM add dynamiclly, getScript, ajax

I use jQuery ajax to read context on my page dynamicly. But part of DOM is load in javaScript function call by getScript in ajax .done. But just after I load new content I need to get elements by class and use it in next function. Unfortunatly I can't find elements, that I create in javaScript function, and script with that function I call using jQuery getScript.
ok, my code:
$(document).ready(function() {
$('.link').click(function(){
var subpage = $(this).attr('data-subpage');
var src = 'subpages/'+ subpage + '/' + subpage +'.php';
var script = 'subpages/'+ subpage + '/js/' + subpage +'.js';
$.ajax({
url: src,
context: document.body,
dataType: 'html',
success: function(responseText){
$('#text').html(responseText); // responseText has only container <div id="insideText"></div> that I use in one function and load there by html() several divs. One of this div has class 'translate'. I need it in function Translator
}
})
.done(function(){
$.getScript(script); // here I call script where I load several divs to #insideText
Translator($('.active').attr('data-lang')).getTranslation() // part of function have to find all div.translate, but can't find it if they're load in script call in getScript. And this is a problem.
})
})
})
I hope, I explaine my problem quite clear. If not, plaese ask, I'll try again.
There's some way to do that?
//update//
After this script, call by getScript I still have problem with second 'done':
const Presenter = function(){
var presented, show, fullDesc, cont;
presented = [
{
url: 'demo/colorsGame/',
name: 'Graj w kolorki!',
desc: 'Graj w kolorki! Wybierz taki sam kolor, w jakim napisana jest nazwa wylosowanego koloru. Spiesz się, czas ucieka coraz szybciej i szybciej. Uważaj, bo mózg może cię oszukać i uznać za ważniejsze to, co jest napisane, a nie to co widzisz. Zobacz ile punktów jesteś w stanie zdobyć zanim popełnisz trzy błędy. Ćwicz swoją koncentrację.'
},
{
url: 'demo/sampleUserProfile/',
name: 'Sample User Profil',
desc: 'Mała próbka możliwości reactJS. Wkonany z użyciem biblioteki reactJS, menadzera pakietów webpack oraz na środowisku nodeJS przykładowy profil użytkownika. Like-uj i obserwuj do woli, a jeśli chcesz, wypowiedz się pod profilem.'
}
];
show = function(url, desc, name) {
fullDesc =
"<a href ='" + url + "'>" +
"<h1 class='translate'>" + name + "</h1>" +
"</a>" +
"<div>" +
"<p class='translate'>" + desc + "</p>" +
"</div>";
cont =
"<div id='webmin' class='clearfix'>" +
"<div>" +
fullDesc +
"</div>" +
"<div>" +
"<iframe src='" + url + "' scrolling='no'>" +
"ups, twoja przeglądarka nie obsługuje ramek" +
"</iframe>" +
"</div>"+
"</div>";
return cont;
};
return {
show: show,
presented: presented
}
};
display = function(){
var len, url, name, desc;
len = Presenter().presented.length;
for(let i = 0; i <= len; i++){
url = Presenter().presented[i].url;
name = Presenter().presented[i].name;
desc = Presenter().presented[i].desc;
$('#insidetext.apps').append(Presenter().show(url, desc, name));
}
};
display();
Hmm, I'm wondering, if iframe is not a problem here? And don't let script to be done to end?
getScript is just another ajax call and returns an xhr object which exposes done and fail methods. Use those methods to ensure your dom is properly loaded before trying to access it.
$(document).ready(function() {
$('.link').click(function(){
var subpage = $(this).attr('data-subpage');
var src = 'subpages/'+ subpage + '/' + subpage +'.php';
var script = 'subpages/'+ subpage + '/js/' + subpage +'.js';
$.ajax({
url: src,
context: document.body,
dataType: 'html'
})
.done(function(responseText){
$('#text').html(responseText);
$.getScript(script).done(function() {
Translator($('.active').attr('data-lang')).getTranslation() ;
}
})
})
})
Also, you should implement the .fail methods, in case your ajax requests fail.

Binded click event firing on page load

I have a function called getMedia() which fires an ajax call to get some content. I also have some filters at the top and onclick I've binded an even to destory the content grid and fetch new content but it is firing off when the document loads.
$(document).ready(function() {
var numposts = $(".social__board").data('numposts');
$.each(accounts, function(key, value) {
getMedia(".board", key, value, numposts);
});
setGridFilter(".filters", ".board", ".grid-item");
});
and then in the same file I have the setGridFilter function.
function setGridFilter(filters, board, griditem) {
$(filters + " li").first().addClass("active");
$(document).on("click", $(filters + " li"), function(){
$(griditem).remove();
$(this).addClass('active');
$(this).siblings().removeClass("active");
var source = $(this).data("source");
var numposts = $(board).data('numposts') * 3; // MORE POSTS FOR SINGLE CHANNEL
var username = $(board).attr("data-" + source);
console.log("Source: " + source + "Numposts: " + numposts + "Username" + username);
getMedia(board, source, username, numposts);
});
}

Adding Knockout Template at run time empties HTML DOM

Adding knockout templates at runtime empties the HTML DOM
var templateEngine = new ko.nativeTemplateEngine();
templateEngine.addTemplate = function (templateName, templateMarkup) {
document.write("<script type='text/html' id='" + templateName + "'>" + templateMarkup + "<" + "/script>");
};
templateEngine.addTemplate("gridTable","<table></table");
All the earlier content goes and The DOM becomes
<html><head><script type="text/html" id="gridView"><table></table></script></head></html>
As #nemsev said,
eveloper.mozilla.org/en-US/docs/Web/API/Document/write: calling document.write on a closed (loaded) document automatically calls document.open which will clear the document
So, I modified my code to,
var templateEngine = new ko.nativeTemplateEngine();
templateEngine.addTemplate = function (templateName, templateMarkup) {
//document.write("<script type='text/html' id='" + templateName + "'>" + templateMarkup + "<" + "/script>");
var scriptTag = document.createElement("script");
scriptTag.type = "text/html";
scriptTag.id = templateName;
scriptTag.innerHTML = templateMarkup;
var node = document.getElementsByTagName("head")[0];
node.appendChild(scriptTag);
};
templateEngine.addTemplate("gridTable","<table></table");
I would recommend you to create separate div in your html structure and then load the template in that div. For instance you can use jquery for it.
$("#someDiv").append(templateMarkup);

Use jQuery to insert a script which includes a function?

I am trying to append a line of JavaScript to a div dynamically, but it seems that when I run the .append() call, the browser sees a function() call within that line as an actual function instead of an actual string:
var startDate = 0
...
function insertCalendar(){
var divName = "div[name=StartDate" + startDateNum + "]";
$(divName).append("<script>Calendar.setup({ trigger : 'StartDate" + startDateNum + "', inputField : 'StartDate" + startDateNum + "', onSelect : function() { this.hide() } });</script>");
startDateNum++;
}
At the call to .append() the JavaScript console returns "Uncaught SyntaxError: Unexpected end of input," and it seems to mess with the function insertCalendar. I'm fairly certain I'm not missing a quote...
Let me know if this is too vague as this is my first question here. Thanks!
You can create a script tag, and then add the javascript.
var jstag=document.createElement('script');
jstag.setAttribute("type","text/javascript");
jstag.appendChild(document.createTextNode(" Javascript code goes here "));
document.appendChild(jstag);
Got the answer thanks to Anzz, mixed it with some AJAX:
var startDateNum = 0;
...
function setNewCal(){
var url = "getCal.php?startDate=" + startDateNum;
xmlHttp.open("GET", url, true);
xmlHttp.onreadystatechange = addCalendar;
xmlHttp.send(null);
}
function addCalendar(){
var divName = "StartDate" + startDateNum + "Div";
var response = xmlHttp.responseText;
if (xmlHttp.readyState == 4){
var calscript = document.createElement("script");
calscript.setAttribute("type", "text/javascript");
calscript.appendChild(document.createTextNode(response));
document.getElementById(divName).appendChild(calscript);
startDateNum++;
}
}

NS_ERROR_XPC_BAD_CONVERT_JS with document.write

I'm pulling in a 3rd party JavaScript file which makes use of document.write, but what's being written needs to be manipulated - preferably before it hits the page. What I've come up with is the following:
// Hijack document.write to buffer all output...
var dwrite = document.write;
var hijacked = '';
document.write = function(content) {
hijacked += content;
};
// Call the script...
dwrite("<script type='text/javascript' src='http://www.example.com/file.js'></script>");
// Manipulate the output...
hijacked
.replace(/a/gi, '4')
.replace(/e/gi, '3')
.replace(/i/gi, '1')
.replace(/o/gi, '0');
// Write the output into the page...
dwrite(hijacked);
// Restore document.write and free our buffer...
document.write = dwrite;
hijacked = null;
With this, I'm getting NS_ERROR_XPC_BAD_CONVERT_JS wherever I attempt to call dwrite. Can anyone offer a suggestion on why this is happening? I don't see why calling document.write through a different name would blow up.
UPDATE I'm seeing this in Firefox 4.0.1.
I tried this and it worked. Basically I replaced document.write after using it.
document.write(""
+ "<script>"
+ "var hijacked = '';"
+ "var dw = document.write;"
+ "document.write = function(content) { hijacked += content; }"
+ "<" + "/script>"
+ "<script type='text/javascript' src='test.js'><" + "/script>"
+ "<script>"
+ "document.write = dw;"
+ "dw = null;"
+ "document.write(hijacked.replace(/e/gi, '4'));"
+ "<" + "/script>");

Categories