Javascript error : catChildNotes[y].setAttribute is not an function - javascript

So, I have some JavaScript code which I test with Greasemonkey locally. But I get this persistent error in the Firefox Error Console:
catChildNotes[y].setAttribute is not an function
Code:
var i = prompt("How many videos have you got?", "");
function remove_mp4()
{
titleElems=document.getElementsByName("title");
for(i=0; i<titleElems.length; i++)
{
titleInner=titleElems[i].innerHTML;
titleElems[i].innerHTML=titleInner.replace(titleInner.match(".mp4"), "");
}
}
for (var x = 0; x < i; i++)
{
document.getElementsByName("description")[x].value = "Visit me on my web-site :\
\
http://www.sample.com/";
document.getElementsByName("keywords")[x].value = prompt("Enter keywords : ","");
catChildNodes=document.getElementsByName("category")[x].childNodes;
catChildNodes[x + 1].removeAttribute("selected");
for(y=0; y<catChildNodes.length; y++)
{
if(catChildNodes[y].value="27")
{
catChildNodes[y].setAttribute("selected","");
}
}
}
remove_mp4();
This script should be run on Youtube upload page and do the following :
Remove ".mp4" from the title
Add default description
Add keywords (which are equal to the prompt value)
Change category to "Education"

Typically, when you get the child nodes of an element you will get other element nodes and text nodes. The former have a setAttribute method, the latter don't (simply because text nodes don't have any attributes). If you need only the element children and no the text nodes then you should use children property instead of childNodes.
There is at least one more bug in your code, this is not a comparison:
if (catChildNodes[y].value = "27")
This will assign the value 27 to catChildNodes[y].value. If you actually want to compare then you should use the comparison operator:
if (catChildNodes[y].value == "27")

Related

Am I using indexof correctly?

I'm trying to use indexof to tell me if a string appears on page.
The function below should cycle through all checkboxes (name="comment") in my form checking for each checkbox's value within the rest of the document (only because I can't figure out how to search just one span). If the string value is found to exist elsewhere on the page, that checkbox will change css style.
function loop() {
var comment=document.forms[0].comment;
var ii;
for (ii=0;ii<comment.length;ii++) {
str=comment[ii].value;
id = comment[ii].id;
if(document.body.innerHTML.toString().indexOf(str) !=-1)
{
document.getElementById(id).style.visibility = "hidden";
}
}
}
The result is that all checkboxes turn "hidden". I thought the problem was the checkbox finding its own value in the HTML, but the same happens when I search for nonsense.
Am I using indexof incorrectly? Can anyone point out how and where? I don't want to use window.find.
To elaborate:
Checkbox 1 value is "A IS FOR APPLE". Check the page for the string "A IS FOR APPLE". If found, make checkbox 1 hidden. Go to checkbox 2 and repeat.
If I understood what are you trying to do, I think a better approach should be something like this:
function loop() {
var inputs = document.getElementsByTagName("input");
var span = document.getElementsById("txtHint");
for(var i = 0; i < inputs.length; i++) {
//Let's check only the checkbox with the name comment
if(inputs[i].name == "comment" && inputs[i].value == span.innerText) {
inputs[i].style.visibility = "hidden";
}
}

Javascript Table Iteration

I have an x-y table called "num", and I am trying to iterate through the table and obtain the numerical values in the cells. The alert keep showing me "undefined", and I don't know why. I've tried the innerHTML function, but it doesn't give me the value I need. Any help would be appreciated (I'm new to Javascript).
var x=0;
var y=0;
var table=document.getElementById("num");
for (var r = 1, n = table.rows.length; r < n; r++) {
x= table.rows[r].cells[0].value;
y= table.rows[r].cells[1].value;
alert(x);
}
> x= table.rows[r].cells[0].value
Table cells (td and th elements) don't have a default value property. If you wish to get the text content of a DOM element, you can use the W3C textContent property or the IE innerText property depending on which one is supported in the host. Browsers support one or the other, some both, so you can do:
var cell;
...
cell = table.rows[r].cells[0];
x = cell.textContent || cell.innerText;
You can also write a small function to do the job:
function getText(el) {
if (typeof el.textContent == 'string') {
return el.textContent;
} else if (typeof el.innerText == 'string') {
return el.innerText;
}
}
Then you can just do:
x = getText(table.rows[r].cells[0]);
Note that the value of x will be a string, so be careful if you intend using the + operator for addition.
You'll want .innerText instead of innerHTML
.value is for form elements like inputs.

Replacing text in <body>

I m new to javascript and would be great if you could help me with this issue I m facing.
In the HTML code below, I m trying to highlight a particular word(say "message")by replacing the word by appending to it to make it bold.
<body>
<img src="http://some.web.site/image.jpg" title="abcd" />
This is a test message.
</body>
The String "This is a test message" is found directly in the body element(so there is no id, and hence no getElementById can be used to extract the text).
So I got the entire body element and extracted the text by using textContent.(this gave me the text and ignored the image that is above the text in the html.)
Then after highlighting the word I set the 'body's textContent back to the new String.
The problem is that now I m not able to preserve the image that was above the text, and the new body has only the value of the textContent in it, and the image is lost. This is the JS I used(but now I m replacing message with the word phone).
<script>
function myFunction(){
var x = document.getElementsByTagName('body')[0].textContent;
var v = x.replace("message","phone");
document.getElementsByTagName('body')[0].textContent = v;
}
</script>
Is there any other way to replace text that is placed directly under the body, which has other elements too?
In order to do something like this, you would need to loop through all text nodes in the document, search for the word, and wrap it.
Something like this:
function highlight(str,node) {
if( !node) node = document.body;
var c = node.children, l = c.length, i, p;
for( i=0; i<l; i++) {
if( c[i].nodeType == 1) highlight(str,c[i]);
if( c[i].nodeType == 3) {
if( (p=c[i].data.indexOf(str)) > -1) {
c[i].splitText(p);
c[i].nextSibling.deleteData(0,str.length);
c[i].parentNode.insertBefore(
document.createElement('b'),c[i].nextSibling
).appendChild(document.createTextNode(str));
}
}
}
}
That should do it. To use, just call highlight("message"), with whatever text you want. Note that this will be case-sensitive - if you need caseless matching, let me know and I'll edit to take that into account (although for the most part you could probably get away with highlight("message"); highlight("Message");)
Also, you can limit the search to a particular element. Let's say you have <div id="replace-me">, you can limit the search to that element like so:
highlight("message",document.getElementById('replace-me'));
(You can use any way to get the node, this is just the easiest)
The textContent property sets or returns the textual content of the specified node, and all its descendants.
If you set the textContent proerty, any child nodes are removed and replaced by a single Text node containing the specified string.
refer to here
Try
var children = document.body.childNodes;
for(var len = children.length, child=0; child<len; child++){
if (children[child].nodeType === 3){ // textnode
var contents = children[child].nodeValue;
children[child].nodeValue = contents.replace(/message/gi, 'phone');
}
}

How to remove eval() from dynamic property calls and Uncaught ReferanceError

I'm trying to call object properties dynamically. I have solved the problem with eval() but i know that eval is evil and i want to do this on a better and safer way. My eval code:
// onfocus
var classes = this.getAttribute('class').split(' ');
for(var i = 0; i < classes.length; ++i) {
if(classes[i].match(/val\- */) !== null) {
var rule = classes[i].substr(4);
var instruction = eval('validate.instructionTexts.'+ rule +'()');
tooltip.appendChild( document.createTextNode(instruction) );
}
}
And I also have this code:
// onblur
var classes = this.getAttribute('class').split(' ');
for( var i = 0; i < classes.length; ++i ){
if(classes[i].match(/val\- */) !== null) {
var rule = classes[ i ].substr( 4 );
var tooltip = document.getElementsByClassName( 'tooltip' );
for( i = 0; i < tooltip.length; ++i){
tooltip[ i ].style.display = 'none';
}
eval('validate.rules.'+ rule +'(' + (this.value) + ')');
}
the problem with the second code is that I want to send a string to my property. this.value = the text i type in my textbox so i get correct string from this.value but i got this error.
if i type foo.
Uncaught ReferenceError: foo is not defined. Javascript thinks I trying to send a variabel but i want it to send a string. How can i solve this problems?
An HTML element's CSS class can be accessed directly from JS thru the className property.
JS object properties can be accessed via the dot-notation object.property or via the square-bracket-notation object['property'].
The regex /val\- */ matches the characters v, a, l, a '-' hyphen, and zero or more spaces, anywhere in the string.
The spaces are completely irrelevant since you're testing the result of a string that was split on spaces (and so it won't contain any spaces anymore).
Also, you're not anchoring the regex so a class of 'eval-test' will also be matched. I doubt that's what you're looking for.
If you were just testing for the classes starting with val-, then the indexOf method is much easier to read, and probably also a lot more efficient.
I've adjusted your bits of code accordingly. I'm assuming that the class names for your validation rules all start with val-, and that the rest of the class name is the name for the rule:
// onfocus
var classes = this.className.split(' ');
for(var i = 0; i < classes.length; ++i) {
if(classes[i].indexOf('val-') === 0) { // the class name starts with 'val-'
var rule = classes[i].substr(4);
var instruction = validate.instructionTexts[rule]();
tooltip.appendChild(document.createTextNode(instruction));
}
}
// onblur
var classes = this.className.split(' ');
for (var i = 0; i < classes.length; ++i ){
if(classes[i].indexOf('val-') === 0) { // the class name starts with 'val-'
var rule = classes[i].substr(4);
var tooltip = document.getElementsByClassName('tooltip');
for (i = 0; i < tooltip.length; ++i){
tooltip[i].style.display = 'none';
}
validate.rules[rule](this.value);
}
}
You do not need to use eval, you can access it as:
validate.rules[rule](this.value);
Which will solve your other problem too, which is that you are passing in the value of this.value which when eval()'d is not quoted as a string (which 'foo' is) so is being interpreted as a variable.
to get a property foo from object obj, you could use either
obj.foo
or
obj["foo"]
The first one won't allow reserved words or if the property contains spaces.
So your first example could change to
validate.instructionTexts[rule]()

How to get text from all descendents of an element, disregarding scripts?

My current project involves gathering text content from an element and all of its descendants, based on a provided selector.
For example, when supplied the selector #content and run against this HTML:
<div id="content">
<p>This is some text.</p>
<script type="text/javascript">
var test = true;
</script>
<p>This is some more text.</p>
</div>
my script would return (after a little whitespace cleanup):
This is some text. var test = true; This is some more text.
However, I need to disregard text nodes that occur within <script> elements.
This is an excerpt of my current code (technically, it matches based on one or more provided selectors):
// get text content of all matching elements
for (x = 0; x < selectors.length; x++) { // 'selectors' is an array of CSS selectors from which to gather text content
matches = Sizzle(selectors[x], document);
for (y = 0; y < matches.length; y++) {
match = matches[y];
if (match.innerText) { // IE
content += match.innerText + ' ';
} else if (match.textContent) { // other browsers
content += match.textContent + ' ';
}
}
}
It's a bit simplistic in that it just returns all text nodes within the element (and its descendants) that matches the provided selector. The solution I'm looking for would return all text nodes except for those that fall within <script> elements. It doesn't need to be especially high-performance, but I do need it to ultimately be cross-browser compatible.
I'm assuming that I'll need to somehow loop through all children of the element that matches the selector and accumulate all text nodes other than ones within <script> elements; it doesn't look like there's any way to identify JavaScript once it's already rolled into the string accumulated from all of the text nodes.
I can't use jQuery (for performance/bandwidth reasons), although you may have noticed that I do use its Sizzle selector engine, so jQuery's selector logic is available.
function getTextContentExceptScript(element) {
var text= [];
for (var i= 0, n= element.childNodes.length; i<n; i++) {
var child= element.childNodes[i];
if (child.nodeType===1 && child.tagName.toLowerCase()!=='script')
text.push(getTextContentExceptScript(child));
else if (child.nodeType===3)
text.push(child.data);
}
return text.join('');
}
Or, if you are allowed to change the DOM to remove the <script> elements (which wouldn't usually have noticeable side effects), quicker:
var scripts= element.getElementsByTagName('script');
while (scripts.length!==0)
scripts[0].parentNode.removeChild(scripts[0]);
return 'textContent' in element? element.textContent : element.innerText;
EDIT:
Well first let me say im not too familar with Sizzle on its lonesome, jsut within libraries that use it... That said..
if i had to do this i would do something like:
var selectors = new Array('#main-content', '#side-bar');
function findText(selectors) {
var rText = '';
sNodes = typeof selectors = 'array' ? $(selectors.join(',')) : $(selectors);
for(var i = 0; i < sNodes.length; i++) {
var nodes = $(':not(script)', sNodes[i]);
for(var j=0; j < nodes.length; j++) {
if(nodes[j].nodeType != 1 && node[j].childNodes.length) {
/* recursion - this would work in jQ not sure if
* Sizzle takes a node as a selector you may need
* to tweak.
*/
rText += findText(node[j]);
}
}
}
return rText;
}
I didnt test any of that but it should give you an idea. Hopefully someone else will pipe up with more direction :-)
Cant you just grab the parent node and check the nodeName in your loop... like:
if(match.parentNode.nodeName.toLowerCase() != 'script' && match.nodeName.toLowerCase() != 'script' ) {
match = matches[y];
if (match.innerText) { // IE
content += match.innerText + ' ';
} else if (match.textContent) { // other browsers
content += match.textContent + ' ';
}
}
ofcourse jquery supports the not() syntax in selectors so could you just do $(':not(script)')?

Categories

Resources