Setting attribute of HTML element only affects node, not actual element - javascript

I am having an infuriating problem which is produced by following code:
function setPeriodTimes(day, period, from, to) {
document.getElementsByClassName("day" + day)[0].children[period - 1].setAttribute("from", from)
document.getElementsByClassName("day" + day)[0].children[period - 1].setAttribute("to", to)
}
it doesn't throw an error, however, when using this function, then looking in the console with
document.getElementsByClassName("day" + 1)[0].children[1].getAttribute("to")
I get the expected result, however looking at the elements page in the dev toolbox, I don't see the updated attribute values.
It is rather annoying and is preventing me from continuing... any advice would be most appreciated.
Thankyou Jacob.

Related

jQuery: window.opener makes a difference

I'm working on localhost (so would expect to not have any domain-related probs as here).
On a page I'm using a bit of JS to modify the content of a span in the opening-window. It does not work.
When checking my code to find the control, it works (using FF dev-tools calling my Increment-function or checking the console.log-output): $('#uploads_Count')returns an object of type HTMLSpanElement. However, trying to access the same control from an opened window's console with window.opener.$('#uploads_Count'), this returns an HTML-Document, seemingly the entire page. Why is this not working, what am I missing here?
Here is function that is supposed to increment the counter contained in the span whose id is given as argument:
function Increment(ctrl)
{
var gef = $("#" + ctrl);
if (!gef) // did not find control, maybe on opener?
{
gef = window.opener.$("#" + ctrl);
}
console.log(gef);
cnt = parseInt(gef.text() , 10);
cnt++;
gef.text(cnt);
}
The HTML is trivial:
<span id="uploads_Count">0</span>
If $(selector) returns an element (such as HTMLSpanElement), rather than a collection of elements (would look like [<span id="uploads_Count"></span>] in most dev tools), then you're not calling jQuery.
Dev tools in A-grade browsers tend to introduce $ as a selector function. It is available in the developer console only.
If window.jQuery exists, then it's likely that jQuery.noConflict() was called, in which case you should use window.opener.jQuery.
Found it!
The way I checked if the control was found, was wrong. Instead of if (!gef)I should have used if (!gef.length). Found the explanation here.

Parsing a string returned from ckeditor using javascript string methods

I am trying to get a certain area of data out from ckeditor. In order to do that I use the following code
function get_body_html(){
var email = CKEDITOR.instances['message'].getData();
var before_body = header_to + to + to_subject + subject + subject_body;
var s_index = email.indexOf(before_body)+before_body.length;
var e_index = email.indexOf(body_footer);
return email.substring(s_index,e_index);
}
For some reason that works when I do this on page load
CKEDITOR.instances.message.setData(header_to + to + to_subject+
subject + subject_body + body_text + body_footer);
get_body_html();
it works correctly and gives me the same string that is contained in body_text.
But when I do this
body_text = get_body_html();
CKEDITOR.instances.message.setData(header_to + to + to_subject + subject +
subject_body + body_text + body_footer);
in an onclick function it gets the wrong indexs somehow. Sometimes it can't find the string and returns -1 other times it just gets a weird index that doesn't make sense. These index variations only happen when my code is changed to tackle the problem a different way. So if it is the wrong indices like -5 and 2 then those would continue to be the wrong indices until I made a code change.
There are two facts that you should know about editor.setData.
In some cases it is asynchronous (it depends on the type of editor). That's why it also accepts a callback. Therefore any code that is meant to be executed after setData() should be executed in that callback.
It never is asynchronous before editor is ready. In this period (between editor initialization and instanceReady event) it works in a different mode - it just caches the set value and on getData() it returns exactly that value.
So, as I see on page load you call synchronously setData() and getData() - your function works because you get the value you're expecting to get.
But then, when you try to getData() when editor is already ready you get the HTML parsed, fixed, processed and perhaps differently formatted by CKEditor. I guess that your indexOf() checks are not enough to handle this. You have to rethink your function - e.g. regexp can help.
What also can help is removing htmlwriter plugin, which formats HTML in a way which may make it harder for you to work with it. E.g.:
config.removePlugins = 'htmlwriter';
I was able to get it to work. So the htmlwriter was one of the problems because it must add spaces in between by HTML tags. The other issue I found is that it strips some of the semicolons out in some of the style attributes. Overall CKEditor does a lot of formatting of the source which makes it very hard to index correctly but it's pretty much a trial and error thing. I ended up using the search JavaScript method for strings which can take a regular expression but I used it the same way indexOf would be used so I don't really know if that made a difference or not.

Delayed response from jQuery (with some other odd artefacts)

I'm getting a mind-boggling response from jQuery that I'm hoping someone can help me with. I have a simple problem ... I'm setting the "value" of a HTML form's hidden field (aka, <input type='hidden'> when the form loads. I have a form with three hidden fields and two are working just fine. The third ... well here's the problem:
Once the form has completed loading (and yes it has loaded and is visible ... as indirect proof the previous two hidden fields have loaded and successfully been set by jQuery), I run the following code (just showing relevant snippet as this final field is a date set to "today"):
case "today":
SetFieldValue (targetElement , Date.today().toString("yyyy-MM-dd HH:mm:ss") );
console.log ('Setting ' + jQuery(targetElement).attr('id') + ' to "today": ' + Date.today().toString("yyyy-MM-dd HH:mm:ss") );
break;
The SetFieldValue is a little function I wrote to allow setting DOM elements regardless of what type it was. The code is follows:
function SetFieldValue ( domObject, value ) {
// as a safety function, check if a string representation of the domObject was passed in and convert it to a jQuery object if it was
if ( jQuery.type(domObject) === "string") {
domObject = jQuery(domObject);
}
if ( jQuery.inArray (domObject.prop('tagName').toLowerCase(),['input' , 'select' , 'textarea']) >= 0 ) {
console.log ("setting to value attribute: " + value);
domObject.attr('value',value);
console.log ("now set to: " + domObject.attr('value') + "(" + domObject.attr('id') + ")" );
} else {
console.log ("setting to html attribute");
domObject.html( value );
}
return domObject;
}
Please note the console.log messages as this is important in understanding the craziness of the problem. When I run this, here's what I get on the console:
Now I set a breakpoint at the last line of the "today" case statement (aka, on the "break;" line). Based on the console messages all is well. It appears the DOM element #activity-start_time has been set. Well here's where it gets weird.
The first thing I do is test the reference to "targetElement" and the first thing the debugger console give me is an empty array:
What? That's pretty odd. While I'm pondering that a few seconds pass and all of sudden it resolves itself to what I'd expect (I don't retype it into the console it simply changes from the empty set to what you see below):
Now that the targetElement is exactly as I'd expect I do a simple check with jQuery that the DOM element of #activity-start_time is reporting the same value. You can see the result above. It's precisely the same as targetElement except it HAS NOT got a value. What?!?
I'm at a complete loss. Any help would be greatly appreciated.
Ken
p.s. I will note that other people have suggested using .prop instead of .attr both seem to behave precisely the same.
I know I didn't provide enough context for people to really dig into this problem but I have in the end solved it. What was the issue? It was silly really ... isn't always? Anyway it was just a case of the DOM element in questions 'id' not beging unique. Grrr. It's always the obvious things that you then go onto overlook that get you in trouble.
Anyway, thanks for your patience and help.

removeChild in javascript/ DOM is not working correctly when more than one is present in the function

UPDATE:
Here's the JsFiddle link:
http://jsfiddle.net/zAVFv/
It seems to be not working in JsFiddle, but guess it serves to show the whole code - html, css and Js.
I am facing a very weird situation in DOM editing using Javascript. My sample code is below. Basically, the swapCells would take in two objects, which have 2 children each - one is an img element, and the other is a textNode. What I want to see is the how removeChild works.
I have marked the 2 lines where all the confusion comes from.
issue#1 is on the line where sourceTD has its child removed, issue#2 is when the lastChild is removed from the destinationTD.
Let me explain how the code below is running:
a) when only issue line#1 is present in the code, the sourceTD child gets removed, the output says source has 1 child of img type; destination has 2 children - img and text ---- works as expected
b) when only issue line #2 is present in the code, the destinationTD child gets removed, again works similar to above, AS EXPECTED
c) NOW THE PROBLEM - when both lines are present in the code, it only removes the sourceTD lastChild. output is received only for the sourceTD part of alerts. The destinationTD alerts are not coming, so I am unable to evaluate whether the destination child got removed
CODE:
function swapCells(sourceTD, destinationTD){
//line below is issue line#1
sourceTD.removeChild(sourceTD.lastChild);
//line below is issue line#2
destinationTD.removeChild(destinationTD.lastChild);
if(sourceTD.hasChildNodes()){
alert("Source has: " + sourceTD.childNodes.length);
alert(sourceTD.childNodes[0].alt);
alert(sourceTD.childNodes[1].nodeName);
}
destinationTD.removeChild(destinationTD.lastChild);
if(destinationTD.hasChildNodes()){
alert("Destination has: " + destinationTD.childNodes.length);
alert(destinationTD.childNodes[0].alt);
alert(destinationTD.childNodes[1].nodeName);
}
}
Please let me know why the code is behaving abnormally when both the issue lines are present. Also, is there something like a function can remove only a single node, or that only a single removeChild will work...????? I am confused.
Thanks!
Sorted -> http://jsfiddle.net/zAVFv/3/
The first childNode of TD is a newline not the img element .... incremented the numbers by 1 and its working fine ... seems a little flimsy to use in a prod environment - unless you can control the HTML - perhaps look at using getelementbytagname('img') instead ?
When you have both lines in there, you end up calling destinationTD.removeChild(destinationTD.lastChild); twice, so both children get removed. Your if statement then evaluates to false because destinationTD has no child nodes, so you don't get anything output - in this case, lack of output is proof that the code IS working, rather than proof that it ISN'T.
Ahhh... figured it out.
the issue was because of an error created due to referencing a childnode that was no more present, after deletion.
For example, if the sourceTD has 3 children, and were referenced as childNodes[1], and childNodes[2], then upon deletion of the lastChild of the sourceTD, there is no longer childNodes[2], as there are only 2 left and no third node anymore. As a result of this error in JS, the remaining code is somehow suppressed and not run. so this is the reason why the destinationTD alerts also are getting suppressed.
Stupid referencing..!
Thanks everyone, especially #ManseUK for all the help

Whats happening? One day its OK, the next day its 'undefined'?

I am writing a greasemonkey script. Recently i had this same problem twice and i have no idea why is this happening.
function colli(){
.....
var oPriorityMass = bynID('massadderPriority');//my own document.getElementById() function
var aPriorities = [];
if (oPriorityMass) {
for (var cEntry=0; cEntry < oPriorityMass.childNodes.length; cEntry++) {
var sCollNumber = oPriorityMass.childNodes[cEntry].getAttribute('coll');
if (bynID('adder' + sCollNumber + '_check').checked)
aPriorities.push(parseInt(sCollNumber));
}
}
.....
}
So the mystery of this is, one day i had oPriorityMass named as oPririoty. It was working fine, but the whole function was not yet complete and i started working on another functions for my script. These functions have no connection with each other.
Few days later i decided to go back to my function in the above example and finish it. I ran a test on it without modifying anything and got an error in the firefox's (4) javascript error console saying that oPriority.chilNodes[cEntry] is undefined. NOTE, few days back i have tested it exactly the same way and there was no such problem at all.
Ok, so, i decided to rename oPriority to oPriorityMass. Magically, problem got solved.
At first i thought, maybe there was some conflict of 2 objects, with the same name being used in different functions, which somehow continued to live even outside of function scope. My script is currently over 6000 lines big, but i did a search and found out that oPriority was not mentioned anywhere else but in this exact function.
Can somebody tell me, how and why is this happening? I mentioned same thing happened twice now and they happened in different functions, but the same problem node.childNodes[c] is undefined yet node is not null and node.childNodes.length show correct child count.
What is going on? How do i avoid such problems?
Thank you
EDIT: The error given by error console is
Error: uncaught exception: TypeError: oPriorityMass.childNodes[cEntry] is undefined
In response to Brocks comment:
GM_log(oPriorityMass.childNodes[cEntry]) returns undefined as a message. So node.childNodes[c] is the thing that is undefined in general.
My script creates a div window. Later, the above function uses elements in this div. Elements do have unique IDs and i am 100% sure the original site don't know about them.
My script has a start/stop button to run one or the other function when i need to.
I have been refreshing the page and running my script function now. I have noticed that sometimes (but not always) script will fail with the described error on the first run, however, if i run it again (without refreshing the page) it starts working.
The page has a javascript that modifies it. It changes some of it's element widths so it changes when the browser is resized. But i know it has no effect on my div as it is left unchanged when i resize browser.
EDIT2:
function bynID(sID) {
return top.document.getElementById(ns(sID));
}
function ns(sText) {
return g_sScriptName + '_' + sText;
}
ns function just adds the script name in front of the ID. I use it when creating HTML element so my elements never have the same id as the web page. So bynID() is simple function that saves some typing time when i need to get element by ID.
I have modified my colli() function to include check
if (oPriorityMass) {
if (!oPriorityMass.childNodes[0]) {
GM_log('Retrying');
setTimeout(loadPage,2000);
return;
}
for (var cEntry=0; cEntry < oPriorityMass.childNodes.length; cEntry++) {
var sCollNumber = oPriorityMass.childNodes[cEntry].getAttribute('coll');
if (bynID('adder' + sCollNumber + '_check').checked)
aPriorities.push(parseInt(sCollNumber));
}
}
The loadPage function does 1 AJAX call, then i run few XPATH queries on it, but the actual contents are never appended/shown on the page, just kept inside document.createElement('div'), then this function calls colli(). So now, as i have modified my function, i checked the error console and saw that it may take up to 5 tries for it to start working correctly. 5 x 2seconds, thats 10 seconds. It is never 5 retries always, may vary There's got to be something else going on?
In Firefox, childNodes can include #text nodes. You should check to make sure that childNodes[cEntry] has nodeType == 1 or has a getAttribute method before trying to call it. e.g.
<div id="d0">
</div>
<div id="d1"></div>
In the above in Firefox and similar browsers (i.e. based on Gecko and WebKit based browsers like Safari), d0 has one child node, a text node, and d1 has no child nodes.
So I would do something like:
var sCollNumber, el0, el1;
if (oPriorityMass) {
for (var cEntry=0; cEntry < oPriorityMass.childNodes.length; cEntry++) {
el0 = oPriorityMass.childNodes[cEntry];
// Make sure have an HTMLElement that will
// have a getAttribute method
if (el0.nodeType == 1) {
sCollNumber = el0.getAttribute('coll');
el1 = bynID('adder' + sCollNumber + '_check');
// Make sure el1 is not falsey before attempting to
// access properties
if (el1 && el1.checked)
// Never call parseInt on strings without a radix
// Or use some other method to convert to Number
aPriorities.push(parseInt(sCollNumber, 10));
}
}
Given that sCollNumber seems like it is a string integer (just guessing but it seems likely), you can also use:
Number(sCollNumber)
or
+sCollNumber
whichever suits and is more maintainable.
So, according to your last edit, it now works, with the delay, right?
But when I suggested the delay it was not meant to do (even more?) ajax calls while waiting!!
NOT:
if (!oPriorityMass.childNodes[0]) {
GM_log('Retrying');
setTimeout(loadPage,2000);
return;
More like:
setTimeout (colli, 2000);
So the ajax and the other stuff that loadPage does could explain the excessive delay.
The random behavior could be caused by:
return top.document.getElementById(ns(sID));
This will cause erratic behavior if any frames or iframes are present, and you do not block operation on frames. (If you do block such operation then top is redundant and unnecessary.)
GM does not operate correctly in such cases -- depending on what the script does -- often seeming to "switch" from top scope to frame scope or vice versa.
So, it's probably best to change that to:
return document.getElementById (ns (sID) );
And make sure you have:
if (window.top != window.self) //-- Don't run on frames or iframes
return;
as the top lines of code.
Beyond that, it's near impossible to see the problem, because of insufficient information.
Either boil the problem into a Complete, Self Contained, Recipe for duplicating the failure.
OR, post or link to the Complete, Unedited, Script.

Categories