Find and change element's textContent without breaking DOM events - javascript

I need to find an element and replace the .textContent of it within a constantly changing table. I'm fairly new to coding, but I've come across an issue when trying to replace certain elements' .textContent.
I have a table on my website that shuffles the values upon every new session. I would like to have certain values within that table to be replaced.
<table class = "tb">
<tr>
<td>Element 1</td>
<td>Element 2</td>
<td>Element 3</td>
</tr>
</table>
And upon each new session, the order of each element is changed.
I want to first find "Element 2" and change it to "Element 2.1", but I cannot do it with my current script because it just changes the 2nd row of the table rather than what I am looking for. So upon reshuffling, it might change Element 1 to the replacement value instead.
I have a very simple script to swap out elements.
const Tableswap = () => {
const findele = document.querySelectorAll(".tb")[1];
if (findele === null){
return;
};
findele.textContent = "Element 2.1";
};
I've tried the following to find the particular element that I am looking for, but I am not sure where to go from here:
//find the required element
function contains(selector, text) {
var elements = document.querySelectorAll(selector);
return Array.prototype.filter.call(elements, function(element){
return RegExp(text).test(element.textContent);
});
}
const finder = contains('.tb', "Element 2");
if (finder === null){
return;
};
finder.tb.textContent = "Element 2.1";
I am honestly not sure if I even went in the right direction with all of this, and I am a complete newbie when it comes to all of this, I've basically been scavenging bits of code from everywhere and trying to understand what I'm doing along the way. I would prefer everything to be in JS, as I have absolutely no experience with JQuery and have no clue what it even is.

Here's what you were trying to achieve through the second script.
I tried to keep it close to your orginal code to make it easier for you to comprehend.
The script should be deferred if loaded externally to prevent it from executing before the DOM has finished loading.
<!DOCTYPE html>
<html>
<body>
<table class="tb">
<tr>
<td>Element 1</td>
<td>Element 2</td>
<td>Element 3</td>
</tr>
</table>
<script> //defer if loading via src, keep it at the end otherwise
function findAndReplace(selector, find, replace) {
let elements = document.querySelectorAll(selector);
return Array.prototype.filter.call(elements, function (element) {
if (RegExp(find).test(element.textContent))
element.textContent = replace;
});
}
findAndReplace(".tb td", "Element 2", "Element 2.1");
</script>
</body>
</html>

In your HTML, change it to <td id="myID">, and then you can use document.getElementById(myID).innerHTML = "Element 2.1".

Related

How can I link a JavaScript variable to a HTML value?

I need to link an item in a table to a Javascript variable, but it won't link. I think there will be further problems changing the value.
I am running on Atom Editor, with the script package installed, although I could copy the script into Notepad++ or Brackets if needed.
</tr>
<td>Abena</td>
<td>Natale</td>
<td id = "Abena_Natale">11782</td>
<td>No.1</td>
<td>Monarch of Boars</td>
</tr>
<script>
var Abena_Natale = ""
</script>
I expected to get a variable that would change the value in the Abena_Natale element when the Javascript stated. Instead, it just stays the same.
There was an error in your HTML.
From there we need to write the selector to get the element.
And then grab the content inside the node.
Update to support editing.
var Abena_Natale = "";
var abenaElement;
document.addEventListener('DOMContentLoaded', function() {
abenaElement = document.querySelector('#Abena_Natale');
Abena_Natale = abenaElement.innerHTML;
console.log(Abena_Natale);
// Update the value
abenaElement.innerHTML = "Testing";
});
<table>
<tr>
<td>Abena</td>
<td>Natale</td>
<td id="Abena_Natale">11782</td>
<td>No.1</td>
<td>Monarch of Boars</td>
</tr>
</table>
That's not how you access the Document Object Model (DOM) with JS. You need to grab the element using one from a number of methods, and then amend the data.
1) Make sure your table row elements are opened and closed correctly.
2) Use either document.getElementById('Abena_Natale'), or document.querySelector('#Abena_Natale') to grab the element.
3) Set the textContent of the element. value is generally used for input elements.
const aN = document.getElementById('Abena_Natale');
aN.textContent = 'This has been changed';
<table>
<tr>
<td>Abena</td>
<td>Natale</td>
<td id="Abena_Natale">11782</td>
<td>No.1</td>
<td>Monarch of Boars</td>
</tr>
</table>

Changing the title of an element using a child nodes innerHTML

I've got a webpage which uses the JQuery ToolTips plugin for popup boxes with extra information. That's all working fine with no issues.
However in order for the popup boxes to work perfectly I need to give the elements in question titles detailing out what I want the popups to say. This would be simple if I could change the HTML directly but I can only do it through JS. More to bother me, the tooltip changes depending on the content of what's in the elements innerHTML, but the element itself doesn't have an ID. It's parent however does have an ID.
so I've been trying to access the child node through the parent, reads it's innerHTML, compare it to a few if statements and then apply a title to the parent based on this innerHTML.
Here is my code:
for(var i = 1; i<5; i++){
var q = document.getElementById("cell.0."+i);
console.log(i+" "+q);
if(q.children[0].innerHtml === "some text1"){
q.setAttribute('title', 'Other text1');
}
else if(q.children[0].innerHtml === "some text2"){
q.setAttribute('title', 'Other text2');
}
else if(q.children[0].innerHtml === "some text3"){
q.setAttribute('title', 'Other text3');
}
else if(q.children[0].innerHtml === "some text4"){
q.setAttribute('title', 'Other text4');
}
}
And an accompanying JSFiddle to make it a bit clearer: http://jsfiddle.net/qxb58/7/
Note: the JSFiddle uses a button, but the actual function I'm using will be on page load.
I've been testing each line in the console. From what I can tell all of the statements which are immediately testable in the console work (e.g. q.children[0].innerHtml === "some text4" and q.setAttribute('title', 'Other text4');). But when put into a for loop it doesn't seem to work.
Note it's unlikely to be a HTML error as my HTML is autogenerated and works. If there is an error with the HTML in the fiddle, it's me being crap at HTML. Thanks for pointing it out though!
EDIT: Solution: innerHTML not innerHtml. Sorry for the stupid mistake. And thanks for the help!
You were executing javascript onload which meant that when the DOM was created with the function changer(), it was undfined at that time. Also you were looking for property innerHtml instead of innerHTML. Properties are case senstive. With these two changes, it works fine:
JSFiddle
Aside from your case problem, you also have the problem of your HTML being invalid. Enclose your <tr>s in a and it works:
http://jsfiddle.net/qxb58/7/
<table>
<tr>
<td id="cell.0.1"> <span>some text1</span>
</td>
</tr>doc
<br/>
<tr>
<td id="cell.0.2"> <span>some text2</span>
</td>
</tr>
<br/>
<tr>
<td id="cell.0.3"> <span>some text3</span>
</td>
</tr>
<br/>
<tr>
<td id="cell.0.4"> <span>some text4</span>
</td>
</tr>
</table>
<br/>
<br/>
<button onClick="changer()">Changing</button>
Still not 100% correct, but working.
Oh, also, you need to set your fiddle to put your script in the head. You had it in the onload which won't work because the handler won't be defined.
Edit: Sorry, missed the part of actually setting the title. Your problem there is that you need innerText not innerHTML. See here: http://jsfiddle.net/qxb58/15/
Finally: if you are using a jQuery plugin for your tooltips, why not use jQuery throughout instead of getElementById
In HTML ids are case sensitive.
You set them in lower case and query them in JS with an upper case C.
Correct it this way:
q = document.getElementById("cell.0."+i);
^ lower case now
This will not solve the problem but is just one of a multi-step debug. Below is a further answer solving the problem.
EDIT: this works now. I corrected the upper case C and replaced innerHtml with innerText.
And BTW, innerHtml is written this way:
innerHTML
^^^^ all upper case
Copy & paste solution:
function changer()
{
for (var i = 1; i < 5; i++)
{
var q = document.getElementById("cell.0." + i);
if (q.children[0].innerText === "some text1") {
q.setAttribute('title', 'Other text1');
} else if (q.children[0].innerText === "some text2") {
q.setAttribute('title', 'Other text2');
} else if (q.children[0].innerText === "some text3") {
q.setAttribute('title', 'Other text3');
} else if (q.children[0].innerText === "some text4") {
q.setAttribute('title', 'Other text4');
}
}
}

Function named "open" loads a new page - trying to change iframe src with inline JS

I wrote a simple function for change the src attribute of an iframe, and this is the code:
function open(dat) {
document.getElementById('frame1').src = dat;
}
I call the function in this way:
<table id="maintab1" align="center" border="0" style="width:95%">
<tr>
<td width="50%" onclick="open('/timetrials/european/mushroom.php');"> text </td>
<td width="50%" onclick="open('/timetrials/european/flower.php');" > text </td>
</tr>
</table>
Of course the iframe has the id 'frame1' as you can see: <iframe id="frame1" src="/timetrials/european/mushroom.php" >You browser does not support iframes.</iframe>. The page is correctly loaded on the iframe at first.
When I click on a <td>, instead of changing the src of my iframe, I see a new blank page loading forever without getting a result.
Do you know why?
Once again, the evils of inline js are clear! This is because there's a property of window called open and when you use onlick="open()" you're calling the wrong open()! Don't use inline js!
Read some of these results: Why is inline JS bad?
A simple solution to your whole problem is just to use links targeting the iframe like this:
Change iframe path!
<iframe name="myiframe"></iframe>
Improving your current code:
Live demo here (click).
Markup:
<table id="maintab1" >
<tr>
<td width="50%" data-open="/timetrials/european/mushroom.php');">Mushroom</td>
<td width="50%" data-open="/timetrials/european/flower.php');" >Flower</td>
</tr>
</table>
JavaScript:
//get element references
var tds = document.querySelectorAll('#maintab1 td');
//loop each element
for (var i=0; i<tds.length; ++i) {
//register click function
tds[i].addEventListener('click', myClickFunc);
}
function myClickFunc(event) {
var link = this.getAttribute('data-open');
openIframe(link);
}
//also note that camelCase naming is standard
function openIframe(link) {
console.log(link);
}
There are a lot of ways that this could be done, but the key is just getting your element references with javascript and attaching the click function there. Select them however you want to - id, class, selector, or you could even create the element itself with javascript! Rather than storing the data on the element as in my example, you could store them in an array in javascript and then pull them out for each element in the loop (first element will get first link from the array, etc).
I have solved my question by changing the name of my function. The name function open(dat) causes a conflict with the window.open
function open_iframe(dat) {
document.getElementById('frame1').src = dat;
}
This is the new code I wrote and it is fine now.

Javascript works in a href but not in JS file

Hope everyone is fine. Well I have a weird question, something that I'm missing or failing to understand. Hope anyone here can help me out. Well here goes,
Well I have an html page where I've defined a table with a few hard-coded values as below,
<table id='data-table' class='someClass'>
<thead>
<tr>
<th id="name-title">NAME</th>
</tr>
</thead>
<tbody>
<tr class="odd"><th class='c1'>Zachary Quinto</th></tr>
<tr class="even"><th class='c1'>Penny</th></tr>
<tr class="odd"><th class='c1'>Glen McGrath</th></tr>
</tbody>
</table>
Now I have a javascript file in which, somewhere down the code (Using jQuery), I do this,
$('#data-table').click(function() {
var value = $(this).find("th.c1").text();
if(value == "Zachary Quinto")
someFunc.showData('data-table', 1);
});
And for some reason this doesn't work, it goes over this function and I don't see any change/effect. However, to my amazement, If, when encapsulating my data into tags, it seem to work. (By encapsulating, I mean something like below)
<tr class="odd"><th class='c1'>Zachary Quinto</th></tr>
<tr class="even"><th class='c1'>Penny</th></tr>
<tr class="odd"><th class='c1'>Glen McGrath</th></tr>
Can anyone please help me with this, I'm not really sure what I'm doing wrong in Javascript file which doesn't let me do this. It's kind of weird as I thought both mean the same thing one way or another.
Thanks a lot for your time.
Because something is wrong with your script.
You should write it as:
"use strict";
$('#data-table').on("click", "th", function(e) {
e.preventDefault();
var value = $(this).text();
if(value == "Zachary Quinto")
someFunc.showData('data-table', 1);
});
jQuery's text function returns the combined text of all matching elements. In your case, there are three th.c1 matches, so the text() function returns Zachary QuintoPennyGlen McGrath.
You can see this by throwing a breakpoint on your event handler in a debugger.

JS. How to replace html element with another element/text, represented in string?

I have a problem with replacing html elements.
For example, here is a table:
<table>
<tr>
<td id="idTABLE">0</td>
<td>END</td>
</tr>
</table>
(it can be div, span, anything)
And string in JavaScript:
var str = '<td>1</td><td>2</td>';
(It can be anything, 123 text, <span>123 element</span> 456 or <tr><td>123</td> or anything)
How can I replace element idTABLE with str?
So:
<table>
<tr>
<td id="idTABLE">0</td>
<td>END</td>
</tr>
</table>
Becomes:
<table>
<tr>
<td>1</td>
<td>2</td>
<td>END</td>
</tr>
</table>
<!-- str = '<td>1</td><td>2</td>'; -->
<table>
<tr>
123 text
<td>END</td>
</tr>
</table>
<!-- str = '123 text' -->
<table>
<tr>
<td>123</td>
<td>END</td>
</tr>
</table>
<!-- str = '<td>123</td>' -->
I tried createElement, replaceChild, cloneNode, but with no result at all =(
As the Jquery replaceWith() code was too bulky, tricky and complicated, here's my own solution. =)
The best way is to use outerHTML property, but it is not crossbrowsered yet, so I did some trick, weird enough, but simple.
Here is the code
var str = 'item to replace'; //it can be anything
var Obj = document.getElementById('TargetObject'); //any element to be fully replaced
if(Obj.outerHTML) { //if outerHTML is supported
Obj.outerHTML=str; ///it's simple replacement of whole element with contents of str var
}
else { //if outerHTML is not supported, there is a weird but crossbrowsered trick
var tmpObj=document.createElement("div");
tmpObj.innerHTML='<!--THIS DATA SHOULD BE REPLACED-->';
ObjParent=Obj.parentNode; //Okey, element should be parented
ObjParent.replaceChild(tmpObj,Obj); //here we placing our temporary data instead of our target, so we can find it then and replace it into whatever we want to replace to
ObjParent.innerHTML=ObjParent.innerHTML.replace('<div><!--THIS DATA SHOULD BE REPLACED--></div>',str);
}
That's all
Because you are talking about your replacement being anything, and also replacing in the middle of an element's children, it becomes more tricky than just inserting a singular element, or directly removing and appending:
function replaceTargetWith( targetID, html ){
/// find our target
var i, tmp, elm, last, target = document.getElementById(targetID);
/// create a temporary div or tr (to support tds)
tmp = document.createElement(html.indexOf('<td')!=-1?'tr':'div'));
/// fill that div with our html, this generates our children
tmp.innerHTML = html;
/// step through the temporary div's children and insertBefore our target
i = tmp.childNodes.length;
/// the insertBefore method was more complicated than I first thought so I
/// have improved it. Have to be careful when dealing with child lists as
/// they are counted as live lists and so will update as and when you make
/// changes. This is why it is best to work backwards when moving children
/// around, and why I'm assigning the elements I'm working with to `elm`
/// and `last`
last = target;
while(i--){
target.parentNode.insertBefore((elm = tmp.childNodes[i]), last);
last = elm;
}
/// remove the target.
target.parentNode.removeChild(target);
}
example usage:
replaceTargetWith( 'idTABLE', 'I <b>can</b> be <div>anything</div>' );
demo:
http://jsfiddle.net/97H5Y/1/
By using the .innerHTML of our temporary div this will generate the TextNodes and Elements we need to insert without any hard work. But rather than insert the temporary div itself -- this would give us mark up that we don't want -- we can just scan and insert it's children.
...either that or look to using jQuery and it's replaceWith method.
jQuery('#idTABLE').replaceWith('<blink>Why this tag??</blink>');
update 2012/11/15
As a response to EL 2002's comment above:
It not always possible. For example, when createElement('div') and set its innerHTML as <td>123</td>, this div becomes <div>123</div> (js throws away inappropriate td tag)
The above problem obviously negates my solution as well - I have updated my code above accordingly (at least for the td issue). However for certain HTML this will occur no matter what you do. All user agents interpret HTML via their own parsing rules, but nearly all of them will attempt to auto-correct bad HTML. The only way to achieve exactly what you are talking about (in some of your examples) is to take the HTML out of the DOM entirely, and manipulate it as a string. This will be the only way to achieve a markup string with the following (jQuery will not get around this issue either):
<table><tr>123 text<td>END</td></tr></table>
If you then take this string an inject it into the DOM, depending on the browser you will get the following:
123 text<table><tr><td>END</td></tr></table>
<table><tr><td>END</td></tr></table>
The only question that remains is why you would want to achieve broken HTML in the first place? :)
Using jQuery you can do this:
var str = '<td>1</td><td>2</td>';
$('#__TABLE__').replaceWith(str);
http://jsfiddle.net/hZBeW/4/
Or in pure javascript:
var str = '<td>1</td><td>2</td>';
var tdElement = document.getElementById('__TABLE__');
var trElement = tdElement.parentNode;
trElement.removeChild(tdElement);
trElement.innerHTML = str + trElement.innerHTML;
http://jsfiddle.net/hZBeW/1/
You would first remove the table, then add the new replacement to the table's parent object.
Look up removeChild and appendChild
http://javascript.about.com/library/bldom09.htm
https://developer.mozilla.org/en-US/docs/DOM/Node.appendChild
Edit:
jQuery .append allows sting-html without removing tags: http://api.jquery.com/append/
Your input in this case is too ambiguous. Your code will have to know if it should just insert the text as-is or parse out some HTML tags (or otherwise wind up with bad HTML). This is unneeded complexity that you can avoid by adjusting the input you provide.
If the garbled input is unavoidable, then without some sophisticated parsing (preferably in a separate function), you could end up with some bad HTML (like you do in your second example... which is Bad, right?).
I'm guessing you want a function to insert columns into a 1-row table. In this case, your contents should be passed in as an array (without table, tr, td tags). Each array element will be one column.
HTML
<table id="__TABLE__"><tr><td></td></tr></table>
JS
using jQuery for brevity...
function insert_columns (columns)
{
var $row = $('<tr></tr>');
for (var i = 0; i < columns.length; i++)
$row.append('<td>'+columns[i]+'</td>');
$('#__TABLE__').empty(); // remove everything inside
$('#__TABLE__').append($row);
}
So then...
insert_columns(['hello', 'there', 'world']);
Result
<table id="__TABLE__"><tr><td>hello</td><td>there</td><td>world</td></tr></table>
If you need to actually replace the td you are selecting from the DOM, then you need to first go to the parentNode, then replace the contents replace the innerHTML with a new html string representing what you want. The trick is converting the first-table-cell to a string so you can then use it in a string replace method.
I added a fiddle example: http://jsfiddle.net/vzUF4/
<table><tr><td id="first-table-cell">0</td><td>END</td></tr></table>
<script>
var firstTableCell = document.getElementById('first-table-cell');
var tableRow = firstTableCell.parentNode;
// Create a separate node used to convert node into string.
var renderingNode = document.createElement('tr');
renderingNode.appendChild(firstTableCell.cloneNode(true));
// Do a simple string replace on the html
var stringVersionOfFirstTableCell = renderingNode.innerHTML;
tableRow.innerHTML = tableRow.innerHTML.replace(stringVersionOfFirstTableCell,
'<td>0</td><td>1</td>');
</script>
A lot of the complexity here is that you are mixing DOM methods with string methods.
If DOM methods work for your application, it would be much bette to use those.
You can also do this with pure DOM methods (document.createElement, removeChild, appendChild), but it takes more lines of code and your question explicitly said you wanted to use a string.
use the attribute "innerHTML"
somehow select the table:
var a = document.getElementById('table, div, whatever node, id')
a.innerHTML = your_text

Categories