I've made code to query a document for matching strings and make a URL from the strings obtained. It looks through the tag elements looking for matches, makes the URL string, then it appends the link to the designated parentNode object. This code works fine in plain javascript, but it breaks when I stick it in Greasemonkey. I can't figure out why.
Here is a fully working version when I stick it in the chrome console:
//loop through elements by classname and find string matches
regexQueryEmail = "(AccountEmailAddress\\s)(.+?)(\\n)"
regexQueryContact = "(Contact with ID: )(.+?)(\\D)"
var Tags = document.getElementsByClassName('msg-body-div')
for (i = 0; i < Tags.length; i++) {
matchEmail = Tags[i].innerText.match(regexQueryEmail)
matchContact = Tags[i].innerText.match(regexQueryContact)
if (matchEmail != null) {
var emailString = matchEmail[2]
var placeHolder = Tags[i]
}
if (matchContact != null) {
var idString = matchContact[2]
}
}
var urlFirst = "https://cscentral.foo.com/gp/stores/www.foo.com/gp/communications/manager/main/191- 4559276-8054240?ie=UTF8&customerEmailAddress="
var urlSecond = "%3E&initialCommId="
var cscURL = urlFirst + emailString + urlSecond + idString
var cscLink = document.createElement('a')
cscLink.innerText = 'Communication History'
cscLink.href = cscURL
placeHolder.parentNode.appendChild(cscLink)
When I stick it in Greasemonkey, it gives me this error from the Greasemonkey "Edit" screen:
/*
Exception: Tags[i].innerText is undefined
#Scratchpad:18
*/
It has also told me that "placeHolder" is undefined, but I am unable to replicate this right now. I have a feeling that it has something to do with how the variables are scoped. I've added "var Tags;" and "var placeHolder;" to the top of the script and it didn't help.
Firefox uses the element.textContent property.
https://developer.mozilla.org/en-US/docs/Web/API/Node.textContent?redirectlocale=en-US&redirectslug=DOM%2FNode.textContent
The variable placeholder in never declared in the scope you try to use it in. Instead it's declared somewhere in your for loop. Make sure you declare it within the same scope.
E.g.
var Tags = document.getElementsByClassName('msg-body-div')
var placeholder; // declare in same scope
for (var i = 0; i < Tags.length; i++) {
// lookup the tag once
var tag = Tags[i];
// get the text only once
var text = tag.textContent;
matchEmail = text.match(regexQueryEmail)
matchContact = text.match(regexQueryContact)
if (matchEmail != null) {
var emailString = matchEmail[2]
placeHolder = tag // deleted var statement
}
if (matchContact != null) {
var idString = matchContact[2]
}
}
...
// now you can use it.
if (placeHolder) {
placeHolder.parentNode.appendChild(cscLink);
}
Related
Hello I'm trying to add a class to all of my elements on a webpage. The overall goal is to grab all the elements on a webpage and add in a class. The class containing a font size will be changed to hide a message.
I'm getting this error
Uncaught TypeError: Cannot set property 'innerHTML' of null
I've tried moving my script outside the body tag of my index.html but its still not working.
Another problem is I can't add a class to all of the IDs I'm selecting. I can add classes manually like
$("#iconLog").addClass("style"); //this works
but when I try to add a class like this
empTwo = "#" + temp; //where empTwo is a string that equals "#iconLog"
$("empTwo").addClass("style") //this does not work
I'll post my entire script below for reference
$(function() {
var hideMsg = "f";
var n = hideMsg.length;
var i;
var j;
var holder;
var hideHolder;
// on button click - hide msg
$('#btnHide').on('click', function() {
//grab all IDS ON WEBPAGE
var allElements = document.getElementsByTagName("*");
var allIds = [];
for (var i = 0, n = allElements.length; i < n; ++i) {
var el = allElements[i];
if (el.id) {
allIds.push(el.id);
}
}
//ERRORS HAPPENING IN THIS LOOP
for(var i = 0; i < allElements.length; ++i)
{
console.log(allIds[i]);
try{
var temp = document.getElementById(allIds[i]).id;
}
catch(err){
document.getElementById("*").innerHTML = err.message;
}
tempTwo = "#" + temp;
console.log(tempTwo);
//$("#iconLog").addClass("style") //this works
$("tempTwo").addClass("style"); //this does not work
}
for(i = 0; i < n; i++) {
//set var holder to first value of the message to hide
holder = hideMsg.charCodeAt(i);
for(j = 7; -1 < j; j--) {
//set hideHolder to holders value
hideHolder = holder;
//mask hideHolder to grab the first bit to hide
hideHolder = hideHolder & (1<<j);
//grab the first element ID
if(hideHolder === 0) {
// embed the bit
// bitwise &=
} else {
//embed the bit
// bitwise ^=
}
}
}
});
});
To add a class to all elements you don't need a for loop. Try this:
$("*").addClass("style");
Same for setting the inner html of all elements. Try this:
$("*").html("Html here");
Remove the double quotes from empTwo .You don't need quotes when you are passing a varible as a selector. The variable itself contains a string so you don't need the quotes.
empTwo = "#" + temp;
$(empTwo).addClass("style") //this will work
Try this:
$(empTwo).addClass("style")
Note: You used string instead of variable:
well,
try this...
You were passing the varibale in the quotos because of that instead of getting value to empTwo it was searching directly for "empTwo".
$(empTwo).addClass("style");
to get all element try this-
var allElements = = document.body.getElementsByTagName("*");
Hoping this will help you :)
empTwo = "#" + temp; //where empTwo is a string that equals "#iconLog"
$("empTwo").addClass("style") //this does not work
You made mistake in the second Line.
The variable empTwo already is in string format.
So all you need to do is
$(empTwo).addClass("style") //this works because empTwo returns "#iconLog"
I am trying to output only articles if authorsId = authorId.
Beside that the whole function works exactly as I want, here it is:
The general idea is to limit access to only own articles.
So, my question is: how do I limit the results to show only articles written by the owner of the page we are on (authorsId = authorId).
function ArticlesListReturn(returned) {
xml = returned.documentElement;
var rel = document.getElementById('related_category_articles');
rel.options.length = 0;
var status = getXMLData('status');
var title = '';
var id = '';
var authorid = '';
if (status == 0) {
alert("%%LNG_jsArticleListError%%" + errormsg);
} else {
var authorid = document.getElementById("authorid").value; // Serge
// authorsid = getNextXMLData('authors',x);
for (var x = 0; x < xml.getElementsByTagName('titles').length; x++) {
title = getNextXMLData('titles', x);
id = getNextXMLData('ids', x);
authorsid = getNextXMLData('authors', x);
alert(authorsid) // authors of each article - it returns the proper values
alert(authorid) // author of the page we are on - it returns the proper value
var count = 0;
rel.options[x] = new Option(title, id, authorid); // lign that returns results
title = '';
id = '';
authorid = '';
}
}
I suspect the problem is when you try performing a conditional statement (if/then/else) that you are comparing a number to a string (or a string to a number). This is like comparing if (1 == "1" ) for example (note the double quotes is only on one side because the left would be numeric, the right side of the equation would be a string).
I added a test which should force both values to be strings, then compares them. If it still gives you problems, make sure there are no spaces/tabs added to one variable, but missing in the other variable.
Also, I changed your "alert" to output to the console (CTRL+SHIFT+J if you are using firefox). The problem using alert is sometimes remote data is not available when needed but your alert button creates a pause while the data is being read. So... if you use alert, your code works, then you remove alert, your code could reveal new errors (since remote data was not served on time). It may not be an issue now, but could be an issue for you going forward.
Best of luck!
function ArticlesListReturn(returned) {
xml = returned.documentElement;
var rel = document.getElementById('related_category_articles');
rel.options.length = 0;
var status = getXMLData('status');
var title = '';
var id = '';
var authorid = '';
if (status == 0) {
alert("%%LNG_jsArticleListError%%" + errormsg);
} else {
var authorid = document.getElementById("authorid").value; // Serge
// authorsid = getNextXMLData('authors',x);
for (var x = 0; x < xml.getElementsByTagName('titles').length; x++) {
title = getNextXMLData('titles', x);
id = getNextXMLData('ids', x);
authorsid = getNextXMLData('authors', x);
console.log("authorsid = "+authorsid); // authors of each article - it returns the proper values
console.log("authorid = "+authorid); // author of the page we are on - it returns the proper value
if( authorsid.toString() == authorid.toString() )
{
rel.options
var count = 0;
console.log( authorsid.toString()+" equals "+authorid.toString() );
rel.options[rel.options.length] = new Option(title, id, authorid); // lign that returns results
}
else
{
console.log( authorsid.toString()+" NOT equals "+authorid.toString() );
}
title = '';
id = '';
authorid = '';
}
Did you check the console for messages? Did it correctly show authorid and authorsid?
I have edited the script and made a couple of additions...
The console will tell you if the conditional check worked or not (meaning you will get a message for each record). See the "if/else" and the extra "console.log" parts I added?
rel.options[x] changed to equal rel.options[rel.options.length]. I am curious on why you set rel.options.length=0 when I would instead have done rel.options=new Array();
I'm using Greasemonkey to insert a value into a text box, but I cannot for the life of me get it to work. I right click the text box in Firefox and inspect the element. It shows "input#ExtensionExtnum.field_for_edit"
document.getElementsByName("ExtensionExtnum").item(0).value = "test";
document.forms[0].submit();
and I get back:
/*
Exception: document.getElementsByName("ExtensionExtnum").item(0) is null
#Scratchpad:8
*/
I've gotten this sort of code to work on other websites, for inputting usernames and passwords automatically. I don't understand why it won't work here.
var items = document.getElementsByName("ExtensionExtnum");
if(items.length > 0)
items[0].value = "test";
Check if there is such an element first
if(document.getElementById("ExtensionExtnum") != undefined)
{
document.getElementById("ExtensionExtnum").value = "test";
}
In input#ExtensionExtnum.field_for_edit, ExtensionExtnum is the element ID, not its name. Thus you must use:
var elem = document.getElementById("ExtensionExtnum");
if ( elem !== null) elem.value = "test";
If you want to set the value of all the elements of class field_for_edit, use:
var elem = document.getElementsByClassName("field_for_edit");
for (var i = 0; i < elem.length; i++)
{
elem[i].value = "test";
}
I want to make a word bold in given paragraph. Here is a javascript code.
var hlWord = "idm";
var nregex = new RegExp(hlWord,"gi");
var div = document.getElementById("SR").innerHTML;
var rword = div.replace(nregex,"<b>"+hlWord+"</b>");
document.getElementById("SR").innerHTML = rword;
Here is a HTML code.
<div id="SR">
Download here free idm.
click here to download
</div>
This is work well and make all idm bold but here is a problem that it also change
url to like this
click here to download
This is not a valid url.This is the problem that this code make the url damaged.
Please tell me how can I avoid this.
Thanks...
You can iterate through all the text nodes with the methods in this thread, change them and replace them with new bold ones.
var hlWord = "idm";
var nregex = new RegExp(hlWord,"gi");
var sr = document.getElementById('SR');
function escape_html(html) {
return html.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
}
(function findTextNodes(current) {
// make a shadow copy of the child nodes.
var current_children = Array.prototype.slice.call(current.childNodes);
for(var i = 0; i < current_children.length; i++) {
var child = current.childNodes[i];
// text node
if(child.nodeType == 3) {
var value = escape_html(child.nodeValue);
var html = value.replace(nregex, '<b>' + hlWord + '</b>');
if (html != value) {
var node = document.createElement('div');
node.innerHTML = html;
// make a shadow copy of the child nodes.
var childNodes = Array.prototype.slice.call(node.childNodes);
// replace the plain text node with the bold segments
for (var j = 0; j < childNodes.length; j++) {
var c = childNodes[j];
current.insertBefore(c, child);
}
current.removeChild(child);
}
}
else {
findTextNodes(child);
}
}
})(sr);
Check the code example at jsFiddle.
UPDATE:
Passerby pointed out that innerHTML should be used carefully. Escape text nodeValue before processing.
After some try-and-fail, I made a working demo that may be more complicated than you might have think:
http://jsfiddle.net/4VKNk/
var cache=[];
var reg=/idm/gi;
var id=function(ID){return document.getElementById(ID);}
function walkElement(ele){
if(ele.childNodes.length>0){
for(var i=0;i<ele.childNodes.length;i++){
walkElement(ele.childNodes[i]);
}
}else if(ele.nodeType==3){//text node
if(reg.test(ele.nodeValue)){
cache.push(ele);
}
}
}
id("test").onclick=function(){
cache=[];
walkElement(id("SR"));
while(cache.length>0){
var ele=cache.shift();
var val=ele.nodeValue;
var pnt=ele.parentNode;
var nextSibling=ele.nextSibling;
var i=0;
var r,tmp;
pnt.removeChild(ele);
while(r=reg.exec(val)){
tmp=document.createTextNode(val.substring(i,r.index));
if(nextSibling){
pnt.insertBefore(tmp,nextSibling);
tmp=document.createElement("strong");
tmp.appendChild(document.createTextNode("idm"));
pnt.insertBefore(tmp,nextSibling);
}else{
pnt.appendChild(tmp);
tmp=document.createElement("strong");
tmp.appendChild(document.createTextNode("idm"));
pnt.appendChild(tmp);
}
i=reg.lastIndex;
}
if(i<val.length-1){
tmp=document.createTextNode(val.substring(i,val.length));
if(nextSibling){
pnt.insertBefore(tmp,nextSibling);
}else{
pnt.appendChild(tmp);
}
}
}
};
I took the approach of DOM manipulation.
Explanation:
Walk through the whole DOM tree under target element, and cache all TEXT_NODE (nodeType==3);
Use RegExp.exec() method to get the index of each match;
While you find a match, add back the text that come before it, and then add a highlight element (<strong>) that contains the match; continue this step;
If we still have text left, add it back.
The reason I need to cache the TEXT_NODEs first, is that if we directly modify it in walkElement, it will change childNodes.length of its parent, and break the process.
Trying to replace the contents of a td using regular expressions in my javascript function. I'm using this...
var re = /<td id="idreplaceme">.+?<\/td>/gi;
oldDivContent = oldDivContent.replace(re,'<td id="idreplaceme"></td>');
This works in FF and Chrome but not IE8. How do I make this work in IE8?
EDIT:
OldDivContent is a string
Why not document.getElementById('idreplaceme').innerHTML = ''
Try a slightly different regex:
var re = /<td id="idreplaceme">[^<]+<\/td>/gi;
oldDivContent = oldDivContent.replace(re,'<td id="idreplaceme"></td>');
or assuming the lawyers don't want you posting any part of oldDivContent, here's a JavaScript function that should work in lieu of a regex:
function removeContentOfIdentifiedTDs(content, id) {
var d = document.createElement('div'),
tr = {},
td = {},
i = 0;
d.innerHTML = content; //add content to div
td = d.getElementsByTagName('td');
for (i = 0; i < td.length; i += 1) {
if (td[i].id === id) { //if id matches
td[i].innerHTML = ''; //remove content
}
}
return d.innerHTML; //return original html minus removed content
}
removeContentOfIdentifiedTDs(oldDivContent, 'idreplaceme');