Dynamic GetElementById - it's null - javascript

hey i create an Element with id and an eventListener. so when i click a link i want to get the id.
But i don't have Control of this created Element. "cannot set property ... of null".
function insertLink(text,link,window,underline)
{
var doc = document.getElementById("iframe_editor").contentWindow.document;
var sel = doc.getSelection();
var count = parseInt(document.getElementById("counter").value);
if (sel.rangeCount > 0)
{
count ++;
document.getElementById("counter").value = count;
var range= sel.getRangeAt(0);
myParent=document.getElementById("iframe_editor").contentWindow.document.body;
alink=document.createElement("a");
var text = document.createTextNode(document.getElementById("linktext").value);
alink.href = document.getElementById("linkhref").value;
alink.id = "testid"+count;
alink.appendChild(text);
myParent.appendChild(alink);
range.insertNode(alink);
alink.addEventListener( 'click', function(){
loadElement(count.toString());
},count );
}
}
function loadElement(t)
{
alert(t);
document.getElementById("testid"+t).innerHTML = "<a href = ''>Test</a>";
}
When i check the variable t in loadElement i get back the right number. But i cannot Change this element with innerhtml.
why not?

The link you inserted does not exist in your actual web page, but inside your iframe. So, you have to access it from the iframe:
document.getElementById("iframe_editor").contentWindow.document.getElementById("testid"+t).innerHTML = "<a href = ''>Test</a>";

Related

Button created dynamically cant accses

I am learning javascript, and i am trying to get accses to a button that is created dynamically within a function. The first function createHtml just gets data and creates elements and store them in a variable that i add to the html element and store them in the beerContainer, and that works. I cant however accses the button that is created in the function createHtml. How can i get the alert to pop up when im clicking on saveBeer?
let btn = document.querySelector('#myBtn');
let beerContainer = document.querySelector('#beer-data');
function createHtml(result) {
var html = '';
for(let i = 0; i < result.data.length; i++) {
html += "<h3>"+ result.data[i].name +"</h3>"
html += "<p>"+ result.data[i].description +"</p>"
html += "<button id='save-beer' data-id='"+ result.data[i].id+"'>Save Beer</button>"
}
beerContainer.innerHTML = html;
}
btn.addEventListener('click', function(event) {
fetchApiData(createHtml);
});
var saveBeer = document.querySelector('.save-beer');
saveBeer.addEventListener('click', function(event) {
alert("Hi");
});
try using
var saveBeer = document.querySelector('#save-beer'); // access by id
rather than
var saveBeer = document.querySelector('.save-beer'); // trying to access by class which does not exist
Note the '#' rather than '.' in the querySelector argument

Anchor dynamic element with click event

I am trying to dynamically add an anchor element through Javascript. The problem I have is the onclick event is not firing. I believe the problem is how I am generating the HTML. I am creating an array and then push my HTML code to the array. After I have created my output I am joining the array and then adding it to the div tag I have.
var itemLink = new Object();
itemLink.LinkName = "Edit User";
itemLink.LinkListClass = "";
itemLink.LinkListRole = "";
itemLink.LinkFunction = function() {
//do something specific with rowItem variable
alert(rowItem);
}
var aTag = document.createElement("a");
aTag.setAttribute('class', 'btn btn-primary');
aTag.innerHTML = itemLink.LinkName;
aTag.setAttribute('href', '#');
var rowItem = 'abc1111'; //would be setting the rowId or some sort of identifier
aTag.onclick = itemLink.LinkFunction;
var output = [];
output.push('<table>');
output.push('<thead>');
output.push('<tr><th>col1</th><th>col2</th></tr>');
output.push('</thead>');
output.push('<tbody>');
output.push('<tr><td>col1 data</td><td>col2 data</td></tr>');
output.push('</tbody></table>')
var d1 = document.createElement('div');
d1.appendChild(aTag);
output.push(d1.innerHTML);
var mainView = document.getElementById('mainViewer');
mainView.innerHTML = output.join('');
<div id="mainViewer"></div>
When I generate the output without the use of the array and joining of the output, the anchor element gets created and the onclick event works just fine.
Any ideas?
I will have multiple anchor links and I don't want to hardcode the function name. I want the onclick event to fire whatever function the itemLink Object has set.
What's the problem? You bind a function to a temp DOM element, then append its html, not its events (that's how innerHTML works). So when a link appended to the DOM, it's a different DOM link, so although the link looks the same it's not.
So, what is the solution? to push a DOM element instead of string, something like this:
//var itemLink = new Object();
//itemLink.LinkName = "Edit User";
//itemLink.LinkListClass = "";
//itemLink.LinkListRole = "";
//itemLink.LinkFunction = function() {
//do something specific with rowItem variable
//alert(rowItem);
//}
var itemLink = {
LinkName: "Edit User",
LinkListClass: "",
LinkListRole: "",
LinkFunction: function() {
//do something specific with rowItem variable
alert(rowItem);
}
};
var aTag = document.createElement("a");
aTag.setAttribute('class', 'btn btn-primary');
aTag.innerHTML = itemLink.LinkName;
aTag.setAttribute('href', '#');
var rowItem = 'abc1111'; //would be setting the rowId or some sort of identifier
aTag.onclick = itemLink.LinkFunction;
var output = [];
output.push('<table>');
output.push('<thead>');
output.push('<tr><th>col1</th><th>col2</th></tr>');
output.push('</thead>');
output.push('<tbody>');
output.push('<tr><td>col1 data</td><td>col2 data</td></tr>');
output.push('</tbody></table>')
var mainView = document.getElementById('mainViewer');
mainView.innerHTML = output.join('');
var d1 = document.createElement('div');
d1.appendChild(aTag);
mainView.appendChild(d1)
<div id="mainViewer"></div>
Thanks to #David Thomas for his comment :)

javascript adding click events to buttons in a loop

The code below gets info from xml file.
I succesfully presents the id and name of each planet with a button.
I want to add an onclick event on the button.
Problem now is: it does add the onclick event but only on the last button created in the loop.
What am i doing wrong? Why doesnt it create a onclick event for each button, but only for the last one in loop?
function updatePlaneten() {
var valDiv, planets, valButton, textNode;
// Get xml files
planets = this.responseXML.getElementsByTagName("planeet");
// loop through the <planet> tags
for (var i = 0; i < planets.length; i++) {
valDiv = ''; // clear valDiv each time loop starts
// Get the id and the name from the xml info in current <planet> tag
valDiv += planets[i].getElementsByTagName("id")[0].childNodes[0].nodeValue + "<br>";
valDiv += planets[i].getElementsByTagName("name")[0].childNodes[0].nodeValue + "<br>";
document.getElementById("planetenID").innerHTML += valDiv + "<br>";
// Create button with a value and pass in this object for later reference use (valButton.object=this)
valButton = document.createElement("input");
// valButton.setAttribute("planeetID", planets[i].getElementsByTagName("id")[0].childNodes[0].nodeValue);
valButton.setAttribute("value", 'Meer info');
valButton.setAttribute("type", 'button');
valButton.id = (i + 1);
valButton.object = this;
//
// Here is the problem i cant get fixed
//
//valButton.onclick = function(){ showinfo(); }
valButton.addEventListener('click', showinfo);
// Place the button on screen
document.getElementById("planetenID").appendChild(valButton);
}
}
// simple function to check if it works
function showinfo() {
console.log(this.object);
console.log(this.id);
}
The trouble is this line:
document.getElementById("planetenID").innerHTML += valDiv + "<br>";
When you set innerHTML the content currently in there gets destroyed and replaced with the new html, meaning all your old buttons are now destroyed and new ones are created. The previously attached event listeners do not get attached to the new buttons.
Instead simply create a div/span or whatever container would best help, add your planet text or whatever to it and then use appendChild
valDiv = document.createElement("div");
var id = planets[i].getElementsByTagName("id")[0].childNodes[0].nodeValue;
var name = planets[i].getElementsByTagName("name")[0].childNodes[0].nodeValue;
valDiv.innerHTML = id+"<br>"+name+"<br>";
document.getElementById("planetenID").appendChild(valDiv);
You could also use insertAdjacentHTML
var id = planets[i].getElementsByTagName("id")[0].childNodes[0].nodeValue;
var name = planets[i].getElementsByTagName("name")[0].childNodes[0].nodeValue;
valDiv = id+"<br>"+name+"<br>";
document.getElementById("planetenID").insertAdjacentHTML("beforeend",valDiv);
function updatePlaneten() {
var valDiv, planets, valButton, textNode;
// Get xml files
planets = this.responseXML.getElementsByTagName("planeet");
// loop through the <planet> tags
for (var i = 0; i < planets.length; i++) {
(function(num){
valDiv = document.createElement("div");
// Get the id and the name from the xml info in current <planet> tag
var id = planets[num].getElementsByTagName("id")[0].childNodes[0].nodeValue + "<br>";
var name = planets[num].getElementsByTagName("name")[0].childNodes[0].nodeValue + "<br>";
valDiv.innerHTML = id+"<br>"+name+"<br>";
document.getElementById("planetenID").appendChild(valDiv);
// Create button with a value and pass in this object for later reference use (valButton.object=this)
valButton = document.createElement("input");
// valButton.setAttribute("planeetID", planets[i].getElementsByTagName("id")[0].childNodes[0].nodeValue);
valButton.setAttribute("value", 'Meer info');
valButton.setAttribute("type", 'button');
valButton.id = (num + 1);
valButton.object = this;
// FIX: PASS showinfo TO AN ANONYMOUS FUNCTION CONTAINING THE OBJECT
valButton.addEventListener('click', function(){
showinfo(valButton);
});
// Place the button on screen
document.getElementById("planetenID").appendChild(valButton);
}(i));
}
}
// simple function to check if it works
function showinfo(valButton) {
console.log(valButton.object);
console.log(valButton.id);
}

JavaScript can't delete div from form

I am having an issue deleting or replacing a div with a either an empty div or a new veriosn of the div. I have tried to destroy the div with delete $targetname I've tried to replace the div with $("#divname").replace() and I seem to be missing some. I have the function tied to a button click that also clears a textarea and that part works fine but my form continues to show the divs that are getting appended but never removed. Below is the link to the fiddle for my code, any help is appreciated.
http://jsfiddle.net/fNfK8/
emWindow = window.open("", null, "height=400,width=800,status=yes,toolbar=no,menubar=no,location=no");
emWindow.document.title = "Emote Builder";
emWindow.document.body.style.background = "#00214D";
emWindow.document.body.style.color = "White";
// create a form and set properties
var emForm = document.createElement('form');
emForm.id = 'emForm';
// insert into the body of the new window
emWindow.document.body.appendChild(emForm);
// add text before the input
var emoteBuildL = document.createElement('emoteBuildL');
emForm.appendChild(document.createTextNode('Emote Build Window:'));
//add linebreak
var linebreak = document.createElement('br');
emForm.appendChild(linebreak);
// add a text input
var emoteBuild = document.createElement('textarea');
emoteBuild.type = 'text';
emoteBuild.name = 'emoteBuild';
emoteBuild.id = 'emoteBuild';
emoteBuild.rows = 6;
emoteBuild.cols = 80;
emoteBuild.value = '';
emForm.appendChild(emoteBuild);
var emoteTosend = document.getElementById('emoteBuild');
//add linebreak
var linebreak = document.createElement('br');
emForm.appendChild(linebreak);
var ePreview = document.createElement('button');
ePreview.type = 'button';
ePreview.innerHTML = 'Preview Emote';
ePreview.onclick = emoteFunc;
emForm.appendChild(ePreview);
var eSubmit = document.createElement('button');
eSubmit.type = 'button';
eSubmit.innerHTML = 'Send Emote';
eSubmit.onclick = function () {
client.send_direct("" + emoteBuild.value);
};
emForm.appendChild(eSubmit);
var eClear = document.createElement('button');
eClear.type = 'button';
eClear.innerHTML = 'Clear Emotes';
eClear.onclick = function () {
emoteBuild.value = '';
delete $emPreviews;
};
emForm.appendChild(eClear);
//add linebreak
var linebreak = document.createElement('br');
emForm.appendChild(linebreak);
// add text before the input
var emotePviewL = document.createElement('emotePviewL');
emForm.appendChild(document.createTextNode('Emote Previews:'));
//add linebreak
var linebreak = document.createElement('br');
emForm.appendChild(linebreak);
//add linebreak
var linebreak = document.createElement('br');
emForm.appendChild(linebreak);
function emoteFunc() {
var emPreview = emoteBuild.value;
emPreview = emPreview.replace(/%%(.+?)%%/g, "\<font color=\"red\"\>\"$1\"\</font\>");
emPreview = emPreview.replace(/%%/g, "\"");
emPreview = emPreview.replace(/\^/g, "");
emPreview = emPreview.replace(/(\w+_him)/g, "(him/her)");
emPreview = emPreview.replace(/(\w+_his)/g, "(his/her)");
emPreview = emPreview.replace(/(\w+_he)/g, "(he/she)");
emPreview = emPreview.replace(/#/g, "");
var div = document.createElement("div");
div.class = 'emPreviews';
div.id = 'emPreviews';
div.style.color = "black";
div.style.backgroundColor = "white";
div.innerHTML = emPreview;
emForm.appendChild(div);
emForm.appendChild(linebreak);
}
You will find it very much more efficient to put the HTML into a separate file and use that as the source for the new window. Alternatively, use document.write to add content to the page, e.g. the following replaces about 20 lines at the start of your script:
function openWin() {
var emWindow = window.open("", null, "height=400,width=800,status=yes");
emWindow.document.write(
'<!doctype html><title>Emote Builder<\/title>' +
'<style type="text/css">body{background-color: #00214D;color:White;}<\/style>' +
'<form id="emForm">' +
'Emote Build Window:<br>' +
'<textarea name="emoteBuild" id="emoteBuild" rows="6" cols="80"><\/textarea>'
);
emWindow.document.close();
}
Note that when you do:
var linebreak = document.createElement('br');
it creates an element in the current document, but then:
emForm.appendChild(linebreak);
appends it to an element in a different document. You really should do:
var linebreak = emWindow.document.createElement('br');
emForm.appendChild(linebreak);
Or just put it in the HTML above.
You are also creating a button in the opener window, appending it to the form, then having it call a function in the opener. The new window has a new global context, it doesn't have access to the opener's scope. You can do:
ePreview.onclick = window.opener.emoteFunc;
or similar but you might find that blocked in some browsers.
I'd suggest you re–write the function to firstly generate the HTML you want, then write it to a new window using emWindow.document.write. Don't forget to call emWindow.document.close at the end.
Edit
Remember that you are working across documents. So if you are still running the script in the opener (the original window), you have to preface any reference to methods in the child window with a reference to emWindow, e.g. to get a reference to the form in the child window you have to use:
function emoteFunc() {
// Get a reference to the form in the child window
var emPreview = emWindow.document.getElementById('emoteBuild');
...
// Create a div in the child window to append to it
var div = emWidnow.document.createElement('div');
...
// The form and div are in the same document, so just append
emForm.appendChild(div);
// Create a BR element in the child and append it
emForm.appendChild(emWindow.document.createElement('br'));
...
}
Edit 2
Here is a trivial example of sending data between a child and opener.
<script>
var win;
function newWin(){
win = window.open('','','');
win.document.write(
'<title>new window<\/title>' +
'<script>function getValue() {' +
'document.getElementById("i0").value = opener.document.forms.f0.i0.value;}<\/script>' +
'<input id="i0">' +
'<input type="button" onclick="getValue()" value="Get value from opener">' +
'<input type="button" onclick="opener.getValue()" value="Get value using function in opener">'
);
win.document.close();
};
function getValue() {
console.log('getValue called');
console.log(win.document.getElementById('i0').value);
win.document.getElementById('i0').value = document.f0.i0.value;
}
function sendValue(value) {
win.document.getElementById('i0').value = value;
}
</script>
<button onclick="newWin()">Open child</button>
<form id="f0">
<p>Value to get from child
<input name="i0" value="value in opener">
<input type="button" value="Send value" onclick="sendValue(this.form.i0.value)">
</form>
You will discover that (in IE at least) you can:
call a function in the child window to get a value from the opener
call a function in the opener to send a value to the child
call a function in one window from the other,
but you can't call a function in the other window that updates the current window, that's one too many hops.
So any function you want to call from the child should be in the child, and any function you want to call from the opener should be in the opener.

Manipulating DOM data without affecting the view

<!DOCTYPE html>
<html><body>
<p id="intro">Hello <em id="abcd">intro</em> World!</p>
<script type="text/javascript">
var txt=document.getElementById("intro").innerHTML;
var el = document.createElement("span");
el.innerHTML = txt;
var aa = el.getElementById("abcd").innerHTML;
alert( aa );
</script>
</body></html>
The above is a simple snippet. Actually I have an HTML editor and when the user saves the data I should save only the required content. Here I am getting the content of an element and manipulating it with DOM and pass the details to the server. This way I will not change the page content (user view remains the same) and he/she will continue editing the document.
The above is a simple example but in the real case I have to remove, change and move certain elements. The above code fails el.getElementById("abcd").innerHTML. Appreciate any pointers.
You can create a hidden iframe to manipulate all your changes, thus creating a separate DOM, then simply pull back the results you want.
var iframe;
if (document.createElement && (iframe = document.createElement('iframe'))) {
iframe.name = iframe.id = "externalDocument";
iframe.className = "hidden";
document.body.appendChild(iframe);
var externalDocument;
if (iframe.contentDocument) {
externalDocument = iframe.contentDocument;
} else if (iframe.contentWindow) {
externalDocument = iframe.contentWindow.document;
}
else if (window.frames[iframe.name]) {
externalDocument = window.frames[iframe.name].document;
}
if (externalDocument) {
externalDocument.open();
externalDocument.write('<html><body><\/body><\/html>');
externalDocument.close();
/* Run your manipulations here */
var txt = document.getElementById("intro").innerHTML;
var el = document.createElement("span");
el.innerHTML = txt;
/* Attach your objects to the externalDocument */
externalDocument.body.appendChild(el);
/* Reference the externalDocument to manipulate */
var aa = externalDocument.getElementById("abcd").innerHTML;
alert(aa);
}
/* Completed manipulation - Remove iFrame */
document.removeChild(iframe);
}
I have it working here:
http://jsfiddle.net/ucpvP/
Try using jQuery like given below.
function SaveData() //Your add function
{
var txt=$("#intro").html();
$(document).append("<span id='abcd'>" + txt+ "</span>");
var aa = $("#abcd").hmtl();
alert(aa);
}
You can use a DOM Element that is never appended to the DOM.
I use this 'cleanup' function:
function cleanup(str){
var tester = document.createElement('div'),
invalid, result;
tester.innerHTML = str;
//elements I don't allow
invalid = tester.querySelectorAll('script,object,iframe,style,hr,canvas');
// the cleanup (remove unwanted elements)
for (var i=0;i<invalid.length;(i+=1)){
invalid[i].parentNode.removeChild(invalid[i]);
}
result = tester.innerHTML;
tester = invalid = null;
//diacritics to html-encoded
return result.replace(/[\u0080-\u024F]/g,
function(a) {return '&#'+a.charCodeAt(0)+';';}
)
.replace(/%/g,'%25');
}
//usage:
cleanup(document.getElementById("intro").innerHTML);
You can extend the function with your own code to remove, change and move certain elements.

Categories