Is there any way to optimise this code? [closed] - javascript

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
I have a piece of code which will invert all the checkboxes on my form. I have multiple elements (not just checkboxes but also <input type='text'>'s) in it. The reason I need it to be optimised is because it takes about two to three seconds to select all the checkboxes (275 right now).
Here's my code:
function FormInverse() {
var iCheckbox = 1; // Because there are multiple input elements, we need to distinquish the input element ID and the row id
var FormLength = document.FormFacturen.elements.length;
for (i=0; i < FormLength; i++) {
var FormElementType = document.FormFacturen.elements[i].type;
if (FormElementType == "checkbox") {
var Elements = document.getElementsByClassName('row' + iCheckbox); // Alle elementen in de array zetten
var iNumElements = Elements.length;
for (iElement=0; iElement < iNumElements; iElement++) {
if (document.FormFacturen[i].checked == true) {
Elements[iElement].className = "invoice-tr-standard row" + iCheckbox;
} else {
Elements[iElement].className = "invoice-tr-clicked row" + iCheckbox;
}
}
iCheckbox++;
document.FormFacturen[i].checked = !document.FormFacturen[i].checked;
}
}
}
And here is the document.getElementsByClassName function:
document.getElementsByClassName = function(cl) {
var retnode = [];
var myclass = new RegExp('\\b'+cl+'\\b');
var elem = document.getElementsByTagName('*');
for (var i = 0; i < elem.length; i++) {
var classes = elem[i].className;
if (myclass.test(classes)) retnode.push(elem[i]);
}
return retnode;
};

I would suggest using jQuery as well.
Try this:
Add a reference to jQuery:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js" type="text/javascript" language="JavaScript"></script>
Use this code:
$(':checkbox').each( function() {
$(this).attr('checked', !$(this).attr('checked'));
});
Edited:
Or use this to change the classes as well:
$(':checkbox').each(function() {
var checked = $(this).attr('checked');
if (checked) {
$(this).attr('checked', false);
$(this).addClass('invoice-tr-clicked');
$(this).removeClass('invoice-tr-standard');
}
else {
$(this).attr('checked', true);
$(this).addClass('invoice-tr-standard');
$(this).removeClass('invoice-tr-clicked');
}
});
Final version:
$('#FormFacturen :checkbox').each(function() {
var checked = $(this).attr('checked');
if (checked) {
$(this).attr('checked', false);
$(this).parents('tr').addClass('invoice-tr-clicked');
$(this).parents('tr').removeClass('invoice-tr-standard');
}
else {
$(this).attr('checked', true);
$(this).parents('tr').addClass('invoice-tr-standard');
$(this).parents('tr').removeClass('invoice-tr-clicked');
}
});

Each call to getElementsByClassName is expensive, and it is being called on each pass of your for loop.
In addition to #Geoff's suggestion, you could call document.getElementsByTagName('input'); just once, instead of each time getElementsByClassName is called and cache the result for use within your loop.
That would require making a small modification to your getElementsByClassName function whereby it accepts an array of elements to search through.
document.getElementsByClassName = function(cl, eles) {
var retnode = [];
var myclass = new RegExp('\\b'+cl+'\\b');
var len = eles.length;
for (var i = 0; i < len; i++) {
var classes = eles[i].className;
if (myclass.test(classes)) retnode.push(eles[i]);
}
return retnode;
};
function FormInverse() {
// cache all inputs
var inputs = document.getElementsByTagName("input");
...
// later
var Elements = document.getElementsByClassName('row' + iCheckbox, inputs);

You should look into a library like JQuery. It will handle this kind of thing well.
There are a lot of little things you can do to improve your code though. First thing I notice is that your getElementsByClassName function is looping through ALL elements on your page every time you call it. You could change this line:
var elem = document.getElementsByTagName('*');
to just get the input elements:
var elem = document.getElementsByTagName('input');

I am pretty much sure the bottleneck here is the getElementsByClassName function, the browser needs to re-scan the whole html each time to find your element. I suggest you give your elements a unique id id="row1", id="row2", ... instead of a unique class name and use getElementById which is much faster.
var Elements = document.getElementsById('row' + iCheckbox);

Related

How to improve my add class to element functions [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 1 year ago.
Improve this question
I want to write a vanilla js function that works like jQuery's addClass() function. I have written the code below.
function checkType(value){ //get type of target
var name = "";
var type = value.split("");
for(var i=1; i<type.length; i++){
name = name + type[i];
}
if(type[0] == "#"){ //if id
return document.getElementById(name); //return id
}else if(type[0] == "."){ //if class
return document.getElementsByClassName(name); //return class
}else{
return null;
}
}
function classesToArray(value){
if(value != null){
value = value.split(" ");
return value;
}
return [];
}
function addClass(target, value) {
var element = checkType(target);
var classes = classesToArray(value);
if(element != null){
classes.forEach(function(classItem){
if(element.length){
for(var i = 0; i < element.length; i++){
element[i].className += " " + classItem;
}
}else{
element.className += " " + classItem;
}
});
}
}
So the way to use it is as below
addClass('.elementIdName','nameOfClassToAdd'); //target Id if begin with .
addClass('#elementClassName','nameOfClassToAdd'); //target className if begin with #
addClass('.elementIdName', 'nameOfClass1 nameOfClass2'); //you can also add multiple class names
The code works. But I know there's a lot of room to improve it. I'd really appreciate if someone could show me the ropes on what should I look for to improve this. Thanks
There is no need to write your own addClass function.
You can use this:
let el = document.querySelector("#your_element_id");
if(el) {
el.classList.add("your class name");
}
source: https://developer.mozilla.org/en-US/docs/Web/API/Element/classList

jQuery.each() function in plain (vanilla) javascript [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
$.fn.slideUpTransition = function() {
return this.each(function() {
var $el = $(this);
$el.css("max-height", "0");
$el.addClass("height-transition-hidden");
});
};
In jQuery, this.each will iterate over all the matching elements and then apply the function. Here this as a global object holds the class selectors of a div.
jQuery.fn.init [div.custom-dropdown-body.height-transition,
prevObject: jQuery.fn.init(1)]
How can we achieve the same with pure javascript?
I tried doing following but couldn't get as expected:
var obj = this;
for(var i=0; i< obj.length; i++){
// codes
};
return this
Here's a NodeList based example for how $.each works:
NodeList.prototype.each = function(fn) {
for (var i = 0; i < this.length; i++) {
fn.apply(this[i], [i, this[i]]);
}
return this;
};
document.querySelectorAll("p").each(console.log).each(function(i) {
this.textContent = i;
});
<p></p>
<p></p>
<p></p>
(Using the same order of index, element that jQuery uses, as opposed to the JS convention of element, index!)
Edit: added binding of correct this for callback

Getting index from Javascript array [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
How would I get the index of the chosen element or of 'elems[i]' within the javascript? I know this is a very simple question so any suggestions on a book to buy or course to take would be greatly appreciated. Thanks!
code:
<html>
<body>
<table>
<tr id="subject"><?php
$subjects = array("IT","Programming","Networks");
foreach($subjects as $key => $value) {
echo "<td>"."<a href='index.php' class='column'>".$value."</a>"."</td>";
}
?></tr>
</table>
</body>
<script type="text/javascript">
var elems = document.getElementsByClassName('column');
for (var i = 0; i < elems.length; i++) {
elems[i].onclick = function () {
console.log(this.innerHTML);
};
}
</script>
var elems = document.getElementsByClassName('column');
for (var i = 0; i < elems.length; i++) {
let a=i;//the magic happens
elems[i].onclick = function () {
console.log(a);
};
}
See Closures, let, ES6.
Option two (for older browsers):
var elems = document.getElementsByClassName('column');
for (var i = 0; i < elems.length; i++) {
(function(){
var a=i;
elems[i].onclick = function () {
console.log(a);
};
})();
}
You need to put the index into the handlers scope, either trough using block scoping (let, first answer) or by creating a new scope trough an IIFE...
(You need to create an own scope for each handler)
solution 3:
set the dom objects id:
elems[i].id=i;
elems[i].onclick=function(){
alert(this.id);
};
Solution 4:
Get the index dynamically:
alert(elems.indexOf(this));

Event for multiple elements with the same class name [duplicate]

This question already has answers here:
getElementsByClassName onclick issue [duplicate]
(5 answers)
Closed 9 years ago.
I have the following code:
var abbrs = document.getElementsByClassName("hover");
abbrs.onmouseover=function() {
console.log(this);
};
It should trigger when I hover over an element with the class "hover", but it is not working.
What am i doing wrong?
As its name suggests document.getElementsByClassName returns a list of elements, with the hover as their className, so you can do it like:
var i=0,
len = abbrs.length,
abbrs = document.getElementsByClassName("hover");
for( ; i < len ; i++){
abbrs[i].addEventListener("mouseover", function(event){
//...
});
}
Although it answers the question but in terms of a better coding practice we better avoid from creating functions in loops. So the better practice could be something like this:
var i=0,
len = abbrs.length,
abbrs = document.getElementsByClassName("hover");
fnction addEvent(abbr){
abbr.addEventListener("mouseover", function(event){
//...
});
}
for( ; i < len ; i++){
addEvent(abbrs[i]);
}
document.getElementsByClassName returns either a NodeList or HTMLCollection depending on your current browser and version.
To add event listeners to all of the items in the "abbrs" collection/list you would need to do:
for(i=0; i< abbrs.length; i++) {
abbrs[i].onmouseover=function() {...};
}
Alternately, using jQuery:
$(".hover").on("mouseover", function() {...});
See the code below (I assume you are not using jquery)
var abbrs = document.getElementsByClassName("hover");
var index,l=abbrs.length;
for (index = 0; index < l; ++index) {
console.log(abbrs[index]);
abbrs[index].onmouseover = function() {
console.log(this);
}
}

JQuery for loop stuck at last index [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
jQuery Looping and Attaching Click Events
(4 answers)
Closed 9 years ago.
I have function process_row that appends tags to html, and those tags are chained to a function upon clicked. (in this case, simply alert(i), its position in the result array).
But however, upon being clicked, the newly generated alerts the length of the entire result array. I have tried many, many changes to try and make it work, but it doesn't.
Strange thou, fab_div.attr("id", result_data[0]); works fine !! In Chrome inspect element the id tags are displayed as they are, but the click function points everything to the last element in the array.
for example, if I do, fab_div.click(function () { alert(result_data[0]) });, I get the name of the LAST element in the array, doesn't matter which element was clicked.
can anyone please explain to me... WHY??
I think it may have something to do with $("<div>") where JQuery thinks it's the same div that it's assigning to. Is there any way around this? The 's are generated dynamically and I would not want to let PHP do the echoing. Plus the content may be updated realtime.
Example dataset :
Smith_Jones#Smith#Jones#janet_Moore#Janet#Moore#Andrew_Wilson#Andrew#Wilson
After many, many changes, still not working:
function process_row(data){
result_array = data.split("#");
if(result_array.length > 0){
result_data =result_array[0].split("#");
for(i = 0; i < result_array.length; i++){
result_data =result_array[i].split("#");
var fab_text = result_data[1] + " " + result_data[2]
var fab_div = $("<div>");
fab_div.addClass('scroll_tap');
fab_div.attr("id", result_data[0]);
fab_div.append(fab_text)
// fab_div.click(function () { alert(i) });
// ^ not working, try appending list of id's to id_list
id_list.push(result_data[0])
$('#ls_admin').append(fab_div)
}
for(j = 0; j < id_list.length; j++){
$('#' + id_list[j]).click(function () { alert(j) })
}
}
}
Original Attempt:
function process_row(data){
result_array = data.split("#");
if(result_array.length > 0){
result_data =result_array[0].split("#");
for(i = 0; i < result_array.length; i++){
result_data =result_array[i].split("#");
var fab_text = result_data[1] + " " + result_data[2]
var fab_div = $("<div>").append(fab_text).click(function () { alert(i) });
fab_div.addClass('scroll_tap');
fab_div.attr("id", result_data[0]);
$('#ls_admin').append(fab_div)
}
}
}
If you must use an alert, then you can encapsulate the click handler in a self executing function and pass the index to it. Like,
(function (index) {
fab_div.click(function () {
alert(index);
});
})(i);
Although, this is not a clean way to do it. Otherwise, if you are looking to just manipulate the div element is any way, then adding any method directly will also work. Like,
fab_div.click(function () {
alert($(this).attr('id'));
});
You can refer a jsFiddle here
Wonky Solution, but it worked! Haha! Big thanks to Kevin B.
function process_row(data){
result_array = data.split("#");
if(result_array.length > 0){
result_data =result_array[0].split("#");
for(i = 0; i < result_array.length; i++){
result_data =result_array[i].split("#");
var fab_text = result_data[1] + " " + result_data[2]
var fab_div = $("<div>").append(fab_text);
fab_div.addClass('scroll_tap');
fab_div.attr("id", result_data[0]);
$('#ls_admin').append(fab_div)
}
$("#ls_admin").children(this).each(function( index ) {
$(this).append($(this).click(function () { alert($(this).text()) }));
});
}
}

Categories

Resources