At Pc it works well.
but mobile it is too late.
is any other faster method or the others?
function country_change(country,countries)
{
var cls = document.getElementsByClassName("country_events");
if(document.readyState == "loading")
{
alert('not loading.');
} else
{
for (n=0; n < cls.length; n++)
{
var elem = cls[n];
var div_elem = elem.getElementsByTagName('div').length;
for (m=1; m < div_elem; m++)
{
if (elem.getAttribute('name') == country)
{
if (elem.getElementsByTagName('div')[m].style.display == "none")
{
elem.getElementsByTagName('div')[m].style.display="block";
increaseHeight()
}
}
else
{
elem.getElementsByTagName('div')[m].style.display="none";
increaseHeight()
}
}
}
}
}
at pc it works about 1~3 seconds, but mobile it takes almost 10~20 sec.
i think display is not good method, but there is no other way isn't it?
The collection returned by:
var cls = document.getElementsByClassName("country_events");
is live, so any modification to the DOM may require the browser to refresh the collection. That may happen a lot more often that you think (and much more in some browsers than others), so you may want to convert that to an array or use querySelectorAll (which returns a static collection) instead:
var cls = document.querySelectorAll(".country_events");
Then you have:
var elem = cls[n];
var div_elem = elem.getElementsByTagName('div').length;
so it is good to cache elem, but getElementsByTagName also returns a live collection so use querySelectorAll again:
var div_elem = elem.querySelectorAll('div').length;
div_elem seems an inappropriate name, perhaps divCount would be better?
Then:
if (elem.getAttribute('name') == country)
you can save a method call by accessing the property directly:
if (elem.name == country)
And then the awfully wastefull:
if (elem.getElementsByTagName('div')[m].style.display == "none")
{
elem.getElementsByTagName('div')[m].style.display="block";
increaseHeight()
You got the list of divs earlier, so cache it there
var divs = elem.querySelectorAll('div');
var divCount = divs.length;
Now reuse it:
if (divs[m].style.display == 'none') {
divs[m].style.display = '';
} else {
divs[m].style.display = 'none';
}
increaseHeight();
which can be shortened to:
divs[m].style.display == divs[m].style.display == 'none'? '' : 'none';
increaseHeight();
However the conditional operator is usually slower than the equivalent if..else.
So the function becomes:
function country_change(country,countries) {
var cls = document.querySelectorAll('.country_events');
if(document.readyState == "loading") {
alert('not loading.');
} else {
for (var n=0, nLen=cls.length; n<nLen; n++) {
var elem = cls[n];
var divs = elem.querySelectorAll('div');
var divCount = divs.length;
for (var m = 1; m < divCount; m++) {
if (elem.name == country) {
if (divs[m].style.display == 'none') {
divs[m].style.display = '';
} else {
divs[m].style.display = 'none';
}
increaseHeight()
}
}
}
}
}
Note that querySelectorAll is not supported by IE 7 and lower.
Setting element.style.display to '' (empty string) allows it to return to it's default or computed style and means you don't have to know what that is for each different type of element or what is included in CSS rules.
Related
When parsing documents using the excellent libxmljs library in Node.js, I stumbled across a case where a lot of nested elements were found, and the only thing I had to do was create a JS object from it.
Here is what the code looks like :
if (node.type() == 'element') {
switch(node.name()) {
case 'element1': {
myObject.element1 = {}
for (var i = 0; i < node.childNodes().length; i++) {
if(node.type() == 'element') {
switch(node.name()) {
case 'element2': {
myObject.element1.element2 = node.text()
...
}}}}}}}}
/* didn't count the number of closing brackets, but you get the idea ^_^ */
Is there a faster or built-in way to do such things, create an object from an XML string (or part of it) using libxmlJS ?
Note that, if it helps, the parsed XML must be validated against a XTD schema (which can really easily be done using this library)
Thanks
Here is some non-working code that can be found on this article :
function XML2jsobj(node) {
var data = {};
// append a value
function Add(name, value) {
if (data[name]) {
if (data[name].constructor != Array) {
data[name] = [data[name]];
}
data[name][data[name].length] = value;
}
else {
data[name] = value;
}
};
// element attributes
var c, cn;
for (c = 0; cn = node.attributes[c]; c++) {
Add(cn.name, cn.value);
}
// child elements
for (c = 0; cn = node.childNodes[c]; c++) {
if (cn.nodeType == 1) {
if (cn.childNodes.length == 1 && cn.firstChild.nodeType == 3) {
// text value
Add(cn.nodeName, cn.firstChild.nodeValue);
}
else {
// sub-object
Add(cn.nodeName, XML2jsobj(cn));
}
}
}
return data;
}
From that code, I could build something that seems to work with the latest libxmljs release, here it is :
function XML2jsobj(node) {
var data = {};
// append a value
function Add(name, value) {
if (data[name]) {
if (data[name].constructor != Array) {
data[name] = [data[name]];
}
data[name][data[name].length] = value;
}
else {
data[name] = value;
}
};
for (var c = 0; c < node.attrs().length; c++) {
var cn = node.attrs()[c];
Add(cn.name, cn.value);
}
// child elements
for (var c = 0; c < node.childNodes().length; c++) {
var cn = node.childNodes()[c];
if (cn.type() == 'element') {
if (cn.childNodes().length == 1 && cn.childNodes()[0].type() == 'text') {
// text value
Add(cn.name(), cn.childNodes()[0].text());
}
else {
// sub-object
Add(cn.name(), XML2jsobj(cn));
}
}
}
return data;
}
I hope this will have helped someone.
I have a select element with values adding up to it using a javascript function which also has a value selected by default.
Everything is fine when i use the google chrome debug mode to see what has gone wrong. If i come out of the debug mode, i cant see any values, even though they are loaded.
They seem to turn back when i scroll using the select elements.
Everything seems fine in Internet Explorer and Firefox though.
Anyone faced this issue before?
Adding the code here: function call has only first 3 variables set. childSelect1 is the select element i am talking about and childOptions1 are the available options for it. The options are added based on the value in parentSelect.
function toggleChildOptions(parentSelect, childSelect1, childOptions1, childSelect2, childOptions2, addBlankEntry)
{
if(childSelect1 == null && childSelect2 == null)
{
return;
}
if(childSelect1 != null)
{
childSelect1.options.length = 0;
}
if(childSelect2 != null)
{
childSelect2.options.length = 0;
}
for (var i = 0; i < parentSelect.length; i++)
{
if(parentSelect.options[i].selected)
{
var parentID = parentSelect.options[i].value;
if(childSelect1 != null)
{
var currentChildOption1Length = childSelect1.options.length;
if (addBlankEntry == true)
childSelect1.options[currentChildOption1Length++] = new Option('',-1);
for(var j = 0; j<childOptions1.length;j++)
{
if(childOptions1[j][1] == parentID)
{
childSelect1.options[currentChildOption1Length++] = new Option(childOptions1[j][2],childOptions1[j][0]);
if (childOptions1[j][2] == '')
child1BlankFound = true;
}
}
}
if(childSelect2 != null)
{
var currentChildOption2Length = childSelect2.options.length;
if (addBlankEntry == true)
childSelect2.options[currentChildOption2Length++] = new Option('',-1);
for(var v = 0; v<childOptions2.length; v++)
{
if(childOptions2[v][1] == parentID)
{
childSelect2.options[currentChildOption2Length++] = new Option(childOptions2[v][2],childOptions2[v][0]);
}
}
}
}
}
}
I have the following function for changing the CSS display on a given DIV
function(){
if(document.getElementById('kitstory').style.display === "none")
{
document.getElementById('kitstory').style.display = "block";
} else{
document.getElementById('kitstory').style.display = "none";
}
}
I'm using onclick in a link to call the function as and when required.
This works fine on my DIV with the ID "kitstory", but is there a way I can use this function for more than one DIV (I'll have several articles on 1 page with different DIV's I want the function to effect)? I've tried leaving the id blank but it doesn't run.
There are a number of ways of achieving this. The easiest ways I can think of are using getElementsByTagName or getElementsByClassName and iterate over the list, or using a framework like jQuery
But of course! For example:
function toggleSingle(elementId) {
var el = document.getElementById(elementId);
if (el.style.display === 'none') {
el.style.display = 'block';
}
else {
el.style.display = 'none';
}
}
This function may be used by another function, which works with collections:
function toggleMultiple(elementIds) {
if (! (elementIds && elementIds[0]) ) {
// elementIds is not an array, should exit (and warn the developer as well perhaps)
return;
}
for (var i = 0, l = elementIds.length; i < l; ++i) {
toggleSingle(elementIds[i]);
}
}
Why did I write this as two functions and not one? Ok, that probably IS an overkill in this case, but in general I often separate the 'individual' item processing into a separate function: it makes my code more readable, and my tests more simple. Still, one can write an omnipotent function with something like this:
function toggleSmart() {
for (var i = 0, l = arguments.length; i < l; ++i) {
var el = document.getElementById(arguments[i]);
if (el.style.display === 'none') {
el.style.display = 'block';
}
else {
el.style.display = 'none';
}
// and another way to do it: more concise, but less readable for some:
// var display = el.style.display;
// display = display === 'none'
// ? 'block'
// : 'none';
}
}
Make a named function and send the id as a parameter to the function:
function Toggle(id){
var element = document.getElementById(id);
if (element.style.display === "none") {
element.style.display = "block";
} else{
element.style.display = "none";
}
}
Usage:
Toggle('kitstory');
You could use one of the following:
document.getElementsByClassName('some-class');
document.getElementsByTagName('div');
For instance:
var elements = document.getElementsByClassName('kitstory');
for(var i = 0, l = elements.length; i < l; i++) {
elements[i].style.display = (elements[i].style.display == 'none') ? 'block' : 'none';
}
If class name or tag name do not immediately help you target the desired elements, you could also change document to something else:
var elements = document.getElementById('my-container').getElementsByTagName('div');
Try this:
function yourAction() {
if(this.style.display === "none") {
...
}
}
var i, len;
for(i = 0, len = document.getElementsByTagName("div"); i < len; i++) {
document.getElementsByTagName("div")[i].onclick = yourAction;
}
try this
function change(id){
if(document.getElementById(id).style.display === "none")
{
document.getElementById(id).style.display = "block";
} else{
document.getElementById(id).style.display = "none";
}
}
then call function with change(divID);
I'm dealing with a ball-of-mudd project that uses frames & iframes to create a "customizable" interface (like people did in 2002).
The application runs from within a hta and kind of emulates a real WPF style app. I need to capture keys so I can selectively change/refresh some of the subframes.
What I'm trying to do is, if there was a sub-sub frame called desktop and that had some frames in it how would I capture an event, safely, across all frames; and refresh a frames subframes?
Any help appreciated; I accept no responsibility for nausia caused by repeating the last paragraph too many times. :)
Answering to get the formatting
arrFrames[i].document.onkeypress = function(){
var evtobj = window.event ? event : e;
evtobj.cancelBubble = true;
if (evtobj.stopPropagation){ evtobj.stopPropagation();}
top.console.log(evtobj.type+' - '+(evtobj.which?evtobj.which:evtobj.keyCode));
};
I don't know anything about HTA, but the question is marked as javascript / jquery / iframe, so i'll guess it isn't a problem...
You can use an object in window.top to manage your events in a centralized place.
In your main window, you use something like:
var getTopObject = function() {
return window.top.topObject;
}
var TopClass = function () {
this.processClick = function (frame) {
//do something...
alert('click in ' + frame.document.location.toString());
var msj = frame.document.getElementById("msj");
msj.innerHTML = "change!";
};
}
window.top.topObject = new TopClass();
And then, on every iframe, you put:
window.onclick = function () { getTopObject().processClick(window); };
That way you get notified of the click event.
Also note that inside the 'processClick' function in the example you can access the iframe document.
Of course, you can do this a lot more complex, but that's the basic idea. You will have to deal with different events in your case.
Hope this helps, and sorry for my english!
Working; digs through the frames in a loop using a function calling itself; I limited it to 8 rather as I know thats the deepest it will get. You can always change that yourself.
var XXX_util_keyboard = function()
{
//"private" variables:
var objTopWindow = top.window.frames,
arrFrames = [],
MaxDepth = 8;
//"private" methods:
var AddToArray = function(obj){
if(typeof obj.document != "undefined") {
arrFrames.push(obj);
return true;
}
return false;
};
var DeleteFromArray = function(obj){
if(typeof obj != "undefined") {
arrFrames.splice(arrFrames.indexOf(obj), 1);
return true;
}
return false;
};
var FrameLoop = function(objFrames){
if(MaxDepth > 0){
if(objFrames !== null)
{
for(var k = 0; k < objFrames.frames.length; k++)
{
var tmp = objFrames.frames[k];
AddToArray( tmp );
FrameLoop( tmp );
}
this.MaxDepth--;
}
}
};
var AttachEvent = function(key, fn) {
for(var i = 0; i < arrFrames.length; i++){
arrFrames[i].document.onkeypress = function(e) {
var evt = e || window.event,
charCode;
if(evt === null){ evt = this.parentWindow.event; /*IE doesnt capture scope correctly*/ }
charCode = evt.keyCode || evt.which;
alert(charCode);
evt.cancelBubble = true;
if (evt.stopPropagation){ evt.stopPropagation();}
};
}
};
return {
init: function()
{
AddToArray(this.getTopWindow()[0]);
FrameLoop(this.getTopWindow()[0]);
},
getFrames: function()
{
if(arrFrames.length < 1){ FrameLoop(objTopWindow[0]); }
return arrFrames;
},
getTopWindow: function()
{
return objTopWindow === undefined ? window.frames : objTopWindow.window.frames;
},
attachEvent: function()
{
if(arrFrames.length < 1){ FrameLoop(objTopWindow[0]); }
AttachEvent();
}
};
}();
Is there a way to remove the id attribute of every node in a range or fragment?
Update: I finally found out that the bug I'm struggling with is based on a <[script]> being included in a range, and therefore unexpectedly cloned, when a chrome user does a ctrl+a. My goal would be to remove any instance of <[script]> from the range (or doc fragment), such that it is not replicated when cloned.
You may be able to use a TreeWalker, which works in pretty much all the browers that Range works in.
function actOnElementsInRange(range, func) {
function isContainedInRange(el, range) {
var elRange = range.cloneRange();
elRange.selectNode(el);
return range.compareBoundaryPoints(Range.START_TO_START, elRange) <= 0
&& range.compareBoundaryPoints(Range.END_TO_END, elRange) >= 0;
}
var rangeStartElement = range.startContainer;
if (rangeStartElement.nodeType == 3) {
rangeStartElement = rangeStartElement.parentNode;
}
var rangeEndElement = range.endContainer;
if (rangeEndElement.nodeType == 3) {
rangeEndElement = rangeEndElement.parentNode;
}
var isInRange = function(el) {
return (el === rangeStartElement || el === rangeEndElement ||
isContainedInRange(el, range))
? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
};
var container = range.commonAncestorContainer;
if (container.nodeType != 1) {
container = container.parentNode;
}
var walker = document.createTreeWalker(document,
NodeFilter.SHOW_ELEMENT, isInRange, false);
while (walker.nextNode()) {
func(walker.currentNode);
}
}
actOnElementsInRange(range, function(el) {
el.removeAttribute("id");
});
yes: http://api.jquery.com/removeAttr/