How is HTML DOM's getElementsByClassName() so efficient? - javascript

So I had to make a recursive version of getElementsByClassName for a class I'm taking and after I finished I compared it to the native HTMLElement.getElementsByClassName().
I found that on average my code is 30% ± 2% less efficient, yet when accounting for non-instant runs I find that I am only 1.5%± 0.5% less efficient. I can't find the native code online anywhere but I am curious, what does it implement to have so many instant results? Does it use closure scope to memorize past inputs or is it something more complicated? My only thought is that I might be losing efficiency from using JQuery but while writing this I realized I indeed didn't need to use JQuery and changed:
var myClassArray = $(me.classList);
var myChildren = $(me.childNodes);
to:
var myClassArray = me.classList;
var myChildren = me.childNodes;
With my new implementation I that on average My code is 11% ± 1% less efficient, yet when accounting for non-instant runs I find that I am only 1.01%± 0.005% less efficient. I am now even more intrigued to know how it achieves so many instant results, as my version is virtually just as efficient when not counting instant runs.
/**
* Recursive version of .getElementsByClassName.
*
* Only elements with ALL of the classNames specified are selected.
*
* #param {String} className the target class name
* #param {HTMLElement} lastMeChild the child of the last recursive element
* #returns {Array} Return array of elements with the targeted className
*/
var getElementsByClassName = function(className, lastMeChild) {
var me;
if (lastMeChild) {
me = lastMeChild;
} else {
me = document.body;
}
var myClassArray = me.classList;
var myChildren = me.childNodes;
var matchingElements = [];
// Detect classes with multiple Strings as class name ex: class="red apple"
var classNames = '';
if (myClassArray !== undefined) {
for (var myClassName = 0; myClassName < myClassArray.length; myClassName++) {
classNames += ' ' + myClassArray[myClassName];
}
}
classNames += ' ';
// In order to support multiStr classNames add space at beginning
// and end of className to filter strings conatining same chars
// ex: 'targetClassN ameButNotQuite' or 'ButNotQuitetargetClassN ame'
var uniqClassName = ' ' + className + ' ';
if (classNames.includes(uniqClassName)) {
// I have the target in my class
matchingElements.push(me);
}
// Check if I have children
if (myChildren !== undefined) {
for (var child = 0; child < myChildren.length; child++) {
matchingElements.push(getElementsByClassName(className, myChildren[child]));
}
}
//Flatten the array so we hae a neat array of depth 1
return matchingElements.flat(Infinity);
};
// Benchmarking:
var avg1 = [];
var avg2 = [];
for (var x = 0; x < 50000; x++) {
var startTime1 = performance.now();
var result = getElementsByClassName('targetC lassName');
var endTime1 = performance.now();
avg1.push(endTime1 - startTime1);
var startTime2 = performance.now();
var expectedNodeList = document.getElementsByClassName('targetC lassName');
var expectedArray = Array.prototype.slice.apply(expectedNodeList);
var endTime2 = performance.now();
avg2.push(endTime2 - startTime2);
}
const average = array => array.reduce((a, b) => a + b) / array.length;
var av1 = average(avg1);
var av2 = average(avg2);
console.log('Mine: ' + av1 + 'ms OG: ' + av2 + 'ms');
console.log((av1 < av2) ? ('Mine is ' + (Math.round(((av2 / av1) + Number.EPSILON) * 1000) / 1000) + '% more efficient') : ('OG is ' + (Math.round(((av1 / av2) + Number.EPSILON) * 1000) / 1000) + '% more efficient'));
var av1 = average(avg1.filter(val => val !== 0));
var av2 = average(avg2.filter(val => val !== 0));
console.log('\nNon-Instant: \n\nMine: ' + av1 + 'ms OG: ' + av2 + 'ms');
console.log((av1 < av2) ? ('Mine is ' + (Math.round(((av2 / av1) + Number.EPSILON) * 1000) / 1000) + '% more efficient') : ('OG is ' + (Math.round(((av1 / av2) + Number.EPSILON) * 1000) / 1000) + '% more efficient'));
<div class="targetC lassName"></div>
<div class="otherClassName targetC lassName"></div>
<div>
<div class="targetC lassName"></div>
</div>
<div>
<div class="targetC lassName">
<div class="targetC lassName"></div>
</div>
</div>
<div>
<div></div>
<div>
<div class="targetC lassName"></div>
</div>
</div>
<div>
<div class="targetC lassName"></div>
<div class="targetC lassName"></div>
</div>
<div>
<div class="someOtherDiv">
<div class="targetC lassNameButNotQuite innerDiv">
<span class="targetC lassName">Some text for this span.</span>
</div>
</div>
</div>
<div class="container">
<div class="targetC lassName randomClass"></div>
</div>
<div class="paragraph text targetC lassName">
<p class="intro targetC lassName">
Text for the paragraph tag.
</p>
</div>
<div>
<div class="someOtherDiv">
<div class="ButNotQuitetargetC lassName innerDiv">
<span class="targetC lassName">Some text for this span.</span>
</div>
</div>
</div>

The getElementsByClassName() method provided by modern web browsers is likely implemented with native code, which is optimized for performance. This means that it is written in a low-level programming language such as C++, which is compiled to machine code and runs directly on the computer's hardware. This is in contrast to JavaScript, which is a higher-level language that runs on top of the web browser's JavaScript engine.
In terms of implementation, it is likely that the browser's getElementsByClassName() method uses a combination of techniques to optimize its performance. One of these techniques may be a pre-built index of elements that have a given class name, which allows for fast lookups based on class name. Another technique that may be used is caching, where the method remembers the results of previous calls with the same input, so that it does not need to traverse the DOM tree again for the same input.
It's important to note that different Browsers might implement the native getElementsByClassName differently. This is why it's generally faster than your own implementation.
Also, using jQuery generally has a performance penalty over using native methods. It's good that you removed it from your implementation.
It's also worth mentioning that, if you are doing a performance test, you should make sure that you test with a large dataset and run the test multiple times to get an accurate representation of the performance difference.

Related

How to create ascending order numbers for html elements via JS?

I have ten divs to which I want to assign order numbers into text via JS on the page load.
HTML:
<div class="generate-order-number"></div>
<div class="generate-order-number"></div>
<div class="generate-order-number"></div>
<div class="generate-order-number"></div>
...
<div class="generate-order-number"></div>
I have this JS code, but it's not working:
var currentNumber = 0;
window.onload = function generateOrderNumbers(){
for (var i = 0; i < 10; i++) {
var currentNumber = currentNumber + 1;
$('.generate-order-number:eq(' + currentNumber + ')').innerHTML = currentNumber + '.'};
};
so the result on the page would be like:
1.
2.
...
9.
10.
The three issues I'm seeing are:
First, you're re-declaring currentNumber, which means you're never incrementing the global value:
var currentNumber = currentNumber + 1;
Don't create a new local variable, just update the global variable:
currentNumber = currentNumber + 1;
Second, you're trying to set .innerHTML on a jQuery object:
$('.generate-order-number:eq(' + currentNumber + ')').innerHTML = currentNumber + '.'
With jQuery you call the .html() function instead:
$('.generate-order-number:eq(' + currentNumber + ')').html(currentNumber + '.')
Third, the use of :eq() in your selector is one-based:
$('.generate-order-number:eq(' + currentNumber + ')')
But should be zero-based:
$('.generate-order-number:eq(' + (currentNumber - 1) + ')')
Otherwise the first element will be empty and the remaining ones will be numbered 1-9.
Aside from those, just to make your own code more readable and supportable I recommend cleaning up the semicolons and curly braces to be more consistent.
A better way to achieve your code can be: (doesn't use jQuery)
window.onload = ()=>{
document.querySelectorAll(".generate-order-number").forEach(
(el, i) => el.innerHTML = (i + 1) + '.'
)
}

QuerySelector of javascript VS Find() of Jquery

On performance basics QuerySelector() of javascript or Find() of Jquery which is better to use in code on factors like speed and efficent access to Dom Elements
element = document.querySelector(selectors);
or
element= $(document).find(selectors);
querySelector is far more performant. It doesn't require a library nor the construction of a jQuery object.
Warning, the following will block your browser for a little bit, depending on your computer's specs:
const t0 = performance.now();
for (let i = 0; i < 1e6; i++) {
const div = document.querySelector('div');
}
const t1 = performance.now();
for (let i = 0; i < 1e6; i++) {
const div = $(document).find('div');
}
const t2 = performance.now();
console.log('querySelector: ' + (t1 - t0));
console.log('jQuery: ' + (t2 - t1));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>some div</div>
That said, performance for selecting a single element will rarely matter - I think it would only be something to consider if it's done in a nested loop, for example, and is done thousands of times in under a second.

javascript replace string/html from array

I try to create a system replacement for ToolTip.
I already create a version but its not quite optimal (search a better way to do it)
here's a fiddle : http://jsfiddle.net/forX/Lwgrug24/
I create a dictionary (array[key]->value). the array is order by length of the key.
each key is a word or an expression, the value is the definition of the expression.
So, I replace the expression by a span (could/should be a div). The span is used for the tooltip (I use the data-title as tooltip text).
because some word is reused in expression, I need to remove expression already with tooltip (in real life think of father/grandfather, you dont want the definition of father inside grandfather). For replacement I use a ramdom value. That's the worst of this code.
You could make comment or post a new way to do it. maybe someone already did it.
Clarification :
I think my way to do it is wrong by using a string for replacement. Or it could be more secure. How should I do it?
html :
<div class="container">
one two three four five six seven eight nine ten
</div>
javascript :
$(function() {
var list = [
{'k':'one two three four five','v':'First five number.'},
{'k':'four five six seven','v':'middle number.'},
{'k':'six seven eight','v':'second middle number.'},
{'k':'two','v':'number two.'},
{'k':'six','v':'number six.'},
{'k':'ten','v':'number ten.'}
];
$(".container").each(function(){
var replacement = new Array();
for (i = 0; i < list.length; i++) {
var val = list[i];
var rString = randomString(32, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
replacement[rString + "_k"] = htmlEncode(val["k"]);
replacement[rString + "_v"] = htmlEncode(val["v"]);
var re = new RegExp("(" + val["k"] + ")","g");
$(":contains('" + val["k"] + "')",$(this).parent()).html(function(_, html) {
var newItem = '<span class="itemWithDescription" '
+ 'data-title="' + rString + "_v" + '">'
+ rString + "_k"
+ '</span>';
return html.replace(re, newItem);
});
}
for (var k in replacement){
$(this).html($(this).html().replace(k,replacement[k]));
console.log("Key is " + k + ", value is : " + replacement[k]);
}
});
$(document).tooltip({
items:'.itemWithDescription',
tooltipClass:'Tip',
content: function(){
var title = $(this).attr("data-title");
if (title == ""){
title = $(this).attr("title"); //custom tooltips
}
return title;
}
});
});
function randomString(length, chars) {
var result = '';
for (var i = length; i > 0; --i) result += chars[Math.round(Math.random() * (chars.length - 1))];
return result;
}
function htmlEncode(value){
//create a in-memory div, set it's inner text(which jQuery automatically encodes)
//then grab the encoded contents back out. The div never exists on the page.
return $('<div/>').text(value).html();
}
I added a little thing. on the random function, I put a | and } for every char, its bigger but there's not much chance to have a conflic with an expression.
for (var i = length; i > 0; --i) result += '|' + ( chars[Math.round(Math.random() * (chars.length - 1))] ) + '}' ;
http://jsfiddle.net/forX/Lwgrug24/3/

Whats the difference between creating a div as a string versus using createElement()?

I know there is some functions made available to creating a node div with createElement(), but is there any other differences? performance? usability? etc...
String div
$('body').append("<div>foo</div>");
versus
Creating a node div
var newDiv = document.createElement("div");
var divContent = document.createTextNode("foo");
newDiv.appendChild(divContent);
$('body').append(newDiv);
Using the string (example 1) is actually better performing than creating nodes (second example). This is because jQuery already does what is in your node example for you, so you're sort of doing double work in the second example.
Because people didn't seem to believe me, here's a simple benchmark I just made and ran, doing 10,000 operations of each method:
var start = performance.now();
for (var i = 0; i < 10000; i++) {
$('body').append("<div>foo</div>");
}
console.log('Took ' + (performance.now() - start) + ' ms');
Took 70 ms averaged over 10 tests.
var start = performance.now();
for (var i = 0; i < 10000; i++) {
var newDiv = document.createElement("div");
var divContent = document.createTextNode("foo");
newDiv.appendChild(divContent);
$('body').append(newDiv);
}
console.log('Took ' + (performance.now() - start) + ' ms');
Took 110 ms averaged over 10 tests.
Keep in mind that this is all dependent on browser, jquery version, other javascript on the page, etc. That said, if you're appending a number of elements to the dom where this performance actually matters I'd be very surprised.

How can I create unique IDs with JavaScript?

I have a form where a user can add multiple select boxes for multiple cities. The problem is that each newly generated select box needs to have a unique id. Can this be done is JavaScript?
Here is the part of the form for selecting cities. Also note that I'm using some PHP to fill in the cities when a specific state is selected.
<form id="form" name="form" method="post" action="citySelect.php">
<select id="state" name="state" onchange="getCity()">
<option></option>
<option value="1">cali</option>
<option value="2">arizona</option>
<option value="3">texas</option>
</select>
<select id="city" name="city" style="width:100px">
</select>
<br/>
</form>
Here is the JavaScript:
$("#bt").click(function() {
$("#form").append(
"<select id='state' name='state' onchange='getCity()'>
<option></option>
<option value='1'>cali</option>
<option value='2'>arizona</option>
<option value='3'>texas</option>
</select>
<select id='city' name='city' style='width:100px'></select><br/>"
);
});
var id = "id" + Math.random().toString(16).slice(2)
another way it to use the millisecond timer:
var uniq = 'id' + (new Date()).getTime();
const uid = function(){
return Date.now().toString(36) + Math.random().toString(36).substr(2);
}
This Function generates very unique IDs that are sorted by its generated Date.
Also useable for IDs in Databases.
could you not just keep a running index?
var _selectIndex = 0;
...code...
var newSelectBox = document.createElement("select");
newSelectBox.setAttribute("id","select-"+_selectIndex++);
EDIT
Upon further consideration, you may actually prefer to use array-style names for your selects...
e.g.
<select name="city[]"><option ..../></select>
<select name="city[]"><option ..../></select>
<select name="city[]"><option ..../></select>
then, on the server side in php for example:
$cities = $_POST['city']; //array of option values from selects
EDIT 2 In response to OP comment
Dynamically creating options using DOM methods can be done as follows:
var newSelectBox = document.createElement("select");
newSelectBox.setAttribute("id","select-"+_selectIndex++);
var city = null,city_opt=null;
for (var i=0, len=cities.length; i< len; i++) {
city = cities[i];
var city_opt = document.createElement("option");
city_opt.setAttribute("value",city);
city_opt.appendChild(document.createTextNode(city));
newSelectBox.appendChild(city_opt);
}
document.getElementById("example_element").appendChild(newSelectBox);
assuming that the cities array already exists
Alternatively you could use the innerHTML method.....
var newSelectBox = document.createElement("select");
newSelectBox.setAttribute("id","select-"+_selectIndex++);
document.getElementById("example_element").appendChild(newSelectBox);
var city = null,htmlStr="";
for (var i=0, len=cities.length; i< len; i++) {
city = cities[i];
htmlStr += "<option value='" + city + "'>" + city + "</option>";
}
newSelectBox.innerHTML = htmlStr;
function uniqueid(){
// always start with a letter (for DOM friendlyness)
var idstr=String.fromCharCode(Math.floor((Math.random()*25)+65));
do {
// between numbers and characters (48 is 0 and 90 is Z (42-48 = 90)
var ascicode=Math.floor((Math.random()*42)+48);
if (ascicode<58 || ascicode>64){
// exclude all chars between : (58) and # (64)
idstr+=String.fromCharCode(ascicode);
}
} while (idstr.length<32);
return (idstr);
}
No external libraries needed. Uniqueness proved.
You could do something like this.
// Function to generate unique id
const uniqueId = (length=16) => {
return parseInt(Math.ceil(Math.random() * Date.now()).toPrecision(length).toString().replace(".", ""))
}
// ----------------------------
document.querySelector("#butt01").onclick = () => {
document.querySelector("#span01").innerHTML = uniqueId()
}
ids = []
count = 0
document.querySelector("#butt02").onclick = () => {
for (let i = 0; i< 1000; i++){
ids.push(uniqueId())
}
for (el of ids){
for (ell of ids){
if(el == ell && ids.indexOf(el) != ids.indexOf(ell)){
count += 1
}
}
}
document.querySelector("#span02").innerHTML = `Found ${count} duplicated random values.`
}
<button id="butt01">Generate</button>
<br>
<span id="span01"></span>
<br>
<hr>
<br>
<button id="butt02">Check collision potentiality in 1000 cases</button>
<br>
<span id="span02"></span>
Multiply time in milliseconds since epoch with a random value to fixed size.
Run this to see possible collisions.
You would see there are no collisions whether it is 1000, 10000 or 1000000000.
It would have a very small chance if two users generate ids at the same time and gets the rame random number.
To increase the uniqueness you could multiply date more Math.random()s.
The shortest and without libraries, also works in nodejs
crypto.randomUUID();
// 'a63ae209-ec69-4867-af8a-6f4d1efe15c6'
https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID
btn.onclick = () => myID.textContent = crypto.randomUUID()
<button id="btn">Generate ID</button>
<myID id="myID"></myID>
Very short function will give you unique ID:
var uid = (function(){var id=0;return function(){if(arguments[0]===0)id=0;return id++;}})();
alert ( uid() );
In reply to #scott :
Sometime JS go very fast... so...
var uniqueId = null,
getUniqueName = function(prefix) {
if (!uniqueId) uniqueId = (new Date()).getTime();
return (prefix || 'id') + (uniqueId++);
};
I'm working on a similar problem to the OP, and found that elements of the solutions from #Guy and #Scott can be combined to create a solution that's more solid IMO. The resulting unique id here has three sections separated by underscores:
A leading letter;
A timestamp displayed in base 36;
And a final, random section.
This solution should work really well, even for very large sets:
function uniqueId () {
// desired length of Id
var idStrLen = 32;
// always start with a letter -- base 36 makes for a nice shortcut
var idStr = (Math.floor((Math.random() * 25)) + 10).toString(36) + "_";
// add a timestamp in milliseconds (base 36 again) as the base
idStr += (new Date()).getTime().toString(36) + "_";
// similar to above, complete the Id using random, alphanumeric characters
do {
idStr += (Math.floor((Math.random() * 35))).toString(36);
} while (idStr.length < idStrLen);
return (idStr);
}
You could generate an ID using a timer and avoiding duplicates using performance.now():
id = 'id' + performance.now()
dup = 'id' + performance.now()
console.log(id)
console.log(id.replace('.','')) // sexy id
console.log(id === dup) // false!
.as-console-wrapper{border-top: none !important;overflow-y: auto !important;top: 0;}
Note that the High resolution time API is available in all recent browsers.
EDIT(per Ciprian's comment): That is unfortunately not enough, performance.now() is only precise to the millisecond. Consider using it in conjuction with Math.random():
const generateId = () => `${performance.now()}${Math.random().toString().slice(5)}`.replace('.','')
let id = generateId()
let dup = generateId()
console.log(id)
console.log(id === dup) // false!
let ary = [...Array(1000)].map(_ => generateId())
console.log((new Set(ary)).size === 1000) // no dups!
.as-console-wrapper{border-top: none !important;overflow-y: auto !important;top: 0;}
put in your namespace an instance similar to the following one
var myns = {/*.....*/};
myns.uid = new function () {
var u = 0;
this.toString = function () {
return 'myID_' + u++;
};
};
console.dir([myns.uid, myns.uid, myns.uid]);
There are two packages available for this.
For short unique id generation nanoid link
import { nanoid } from 'nanoid'
const id = nanoid() // "Uakgb_J5m9g-0JDMbcJqLJ"
const id = nanoid(10) // "jcNqc0UAWK"
For universally unique id generation uuid link
import { v4 as uuidv4 } from 'uuid';
const id= uuidv4(); // quite big id
You can use the Generator function, was introduced in ES6 (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*)
const idCreator = function* () {
let i = 0;
while (true) yield i++;
};
const idsGenerator = idCreator();
const generateId = () => idsGenerator.next().value;
console.log(generateId()) // 0
console.log(generateId()) // 1
console.log(generateId()) // 2
...
To avoid creating any counters and be sure that the id is unique even if there are some other components that create elements with ids on the page, you can use a random number and than correct it if it's not good enough (but you also have to set the id immediately to avoid conflicts):
var id = "item"+(new Date()).getMilliseconds()+Math.floor(Math.random()*1000);
// or use any random number generator
// whatever prefix can be used instead of "item"
while(document.getElementById(id))
id += 1;
//# set id right here so that no element can get that id between the check and setting it
Random is not unique. Times values are not unique. The concepts are quite different and the difference rears its ugly head when your application scales and is distributed. Many of the answers above are potentially dangerous.
A safer approach to the poster's question is UUIDs: Create GUID / UUID in JavaScript?
Like others said you can use a running index, or if you don't like the idea of using a variable just pull the id of the last city in the list and add 1 to its id.
Here is a function (function genID() below) that recursively checks the DOM for uniqueness based on whatever id prefex/ID you want.
In your case you'd might use it as such
var seedNum = 1;
newSelectBox.setAttribute("id",genID('state-',seedNum));
function genID(myKey, seedNum){
var key = myKey + seedNum;
if (document.getElementById(key) != null){
return genID(myKey, ++seedNum);
}
else{
return key;
}
}
Warning: This answer may not be good for the general intent of this question, but I post it here nevertheless, because it solves a partial version of this issue.
You can use lodash's uniqueId (documentation here). This is not a good uniqueId generator for say, db records, or things that will persist a session in a browser or something like that. But the reason I came here looking for this was solved by using it. If you need a unique id for something transient enough, this will do.
I needed it because I was creating a reusable react component that features a label and a form control. The label needs to have a for="controlId" attribute, corresponding to the id="controlId" that the actual form control has (the input or select element). This id is not necessary out of this context, but I need to generate one id for both attributes to share, and make sure this id is unique in the context of the page being rendered. So lodash's function worked just fine. Just in case is useful for someone else.
Simple Solution :)
const ID = (_length=13) => {
// Math.random to base 36 (numbers, letters),
// grab the first 9 characters
// after the decimal.
return '_' + Math.random().toString(36).substr(2, _length); // max _length should be less then 13
};
console.log("Example ID()::", ID())
function generateId() {
return Math.random().toString(36).substring(2) +
(new Date()).getTime().toString(36);
}
console.log(generateId())
Look at this functions, it will get ur job done.
If u want uppercase and lowercase chars in ur string:
function (length) {
var id = '';
while (id.length < length) {
var ch = Math.random()
.toString(36)
.substr(2, 1);
if (Math.random() < 0.5) {
ch = ch.toUpperCase();
}
id += ch;
}
return id;
}
Only lowercase chars:
function (length) {
var id = '';
while (id.length < length) {
id += Math.random()
.toString(36)
.substr(2, 1);
}
return id;
}
Just two cents
function makeId(tokenLen) {
if (tokenLen == null) {
tokenLen = 16;
}
var text = "";
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (var i = 0; i < tokenLen; ++i)
text += possible.charAt(Math.floor(Math.random() * possible.length));
return text;
}
Here's my own take at it based on the xpath of the element created :
/** Returns the XPATH of an element **/
var getPathTo = function(element) {
if (element===document.body)
return element.tagName;
var ix= 0;
var siblings= element.parentNode.childNodes;
for (var i= 0; i<siblings.length; i++) {
var sibling= siblings[i];
if (sibling===element)
// stripped xpath (parent xpath + tagname + index)
return getPathTo(element.parentNode)+ element.tagName + ix+1;
if (sibling.nodeType===1 && sibling.tagName===element.tagName)
ix++;
}
}
/** hashcode function (credit http://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript-jquery **/
var hashCode = function(str) {
var hash = 0, i, chr, len;
if (str.length === 0) return hash;
for (i = 0, len = str.length; i < len; i++) {
chr = str.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0; // Convert to 32bit integer
}
return hash;
};
/** Genaretes according to xpath + timestamp **/
var generateUID = function(ele)
{
return hashCode(getPathTo(ele)) + new Date().getTime();
}
First the xpath of the element is fetched.
The hashcode of the xpath is then computed. We therefore have a unique id per xpath.
The problem here is that xpath are not necesseraly unique if unique elements are generated on the fly. Thus we add the timestamp at the end.
Maybe we could also garantee more unique elements by adding a final Math.Random().
You could take advantage of closure.
var i = 0;
function generateId() {
return i++;
}
If you want to enclose it:
function generator() {
var i = 0;
return function() {
return i++;
};
}
var generateId = generator();
generateId(); //1
generateId(); //2
generator could accept a default prefix; generateId coud accept an optional suffix:
function generator(prefix) {
var i = 0;
return function(suffix) {
return prefix + (i++) + (suffix || '');
};
}
var generateId = generator('_');
generateId('_'); //_1_
generateId('#'); //_2#
This comes in handy if you want your id to indicate a sequence, very much like new Date().getTime(), but easier to read.
Combining random & date in ms should do the trick with almost no change of collision :
function uniqid(){
return Math.random().toString(16).slice(2)+(new Date()).getTime()+Math.random().toString(16).slice(2);
}
alert(uniqid()+"\r"+uniqid());
const generateUniqueId = () => 'id_' + Date.now() + String(Math.random()).substr(2);
// if u want to check for collision
const arr = [];
const checkForCollision = () => {
for (let i = 0; i < 10000; i++) {
const el = generateUniqueId();
if (arr.indexOf(el) > -1) {
alert('COLLISION FOUND');
}
arr.push(el);
}
};
I think if you really want to have a unique ID then the best approach is to use a library like: uuid or uniqueid
Note: Unique ID is not the same as Random ID
To use only date time milliseconds approach is wrong.
Nowadays computers are fast enough and able to run more than one iteration of a loop in a single millisecond.
npm install uuid
Importing the library:
If you are using ES modules
import { v4 as uuidv4 } from 'uuid';
And for CommonJS:
const { v4: uuidv4 } = require('uuid');
Usage:
uuidv4();
// This will output something like: 9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d
For generate unique id's:
const uid = () =>
String(
Date.now().toString(32) +
Math.random().toString(32) +
Math.random().toString(32)
).replace(/\./g, '')
For check that is works:
var size = 500000
var arr = new Array(size)
.fill(0)
.map(() => uid())
var b = new Set(arr)
console.log(
size === b.size ? 'all ids are unique' : `not unique records ${size - b.size}`
)
I managed to have a non digit unique id with this function, so i'll like to share :)
const id = btoa((Math.random(0,(new Date()).getTime())).toString()).slice(0, -10);
I sliced it for DB varchar limit reasons, but you're free to do without it.

Categories