I am having an issue with page loading time. Currently right now I am running UBUNTU in Oracle Vm Virtual Box. I am using mozilla firefox as my browser and I am working on an etchasketch project from "The odin project".
My problem is the page loading time. The code takes a prompt at the start and generates a grid for the etch a sketch based on that prompt. I have not given it the minimum and maximum values (16 and 64) respectively, however any number when prompted at the beginning that is beyond 35 doesn't load or takes ages to load.
How do I speed up the process time? / why is it moving so slow? / how can I avoid this ? / is there a fix that I am over looking that can make this work a lot faster? / feel free to tackle any and all of those questions!
This is my HTML CODE:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8"/>
<title>
</title>
</head>
<body>
<div class="etchhead">
<p> Choose your grid size </p>
<input type = "text"></input>
<button id="startOver"> Clear Grid </button>
<p> Change color </p>
</div>
<div id="grid">
</div>
<script src="eas.js"></script>
</body>
</html>
And this is my CSS code:
p {
color: blue;
display: inline;
}
#grid {
display: grid;
width: 800px;
max-width: 800px;
height: 800px;
max-height: 800px;
line-height: 0;
}
.gridBox {
border: 1px solid black;
background-color: lightgrey
}
And this is my JAVASCRIPT code:
gridStart();
function gridStart(){
var boxes = 0
var selectBody = document.querySelector("#grid");
var addBox = document.createElement("div");
var boxCountStart = prompt("enter a number between 16 and 64");
var boxDimensions = (boxCountStart * boxCountStart);
function rowsAndColumns() {
var selectBody = document.querySelector("#grid");
var gridTemplateColumns = 'repeat('+boxCountStart+', 1fr)';
selectBody.style.gridTemplateColumns= gridTemplateColumns;
selectBody.style.gridTemplateRows= gridTemplateColumns;
};
function hoverColor(){
var divSelector = selectBody.querySelectorAll("div");
divSelector.forEach((div) => {
div.addEventListener("mouseover", (event) => {
event.target.style.backgroundColor = "grey";
});
});
};
rowsAndColumns();
for (boxes = 0; boxes < boxDimensions ; boxes++) {
var selectBody = document.querySelector("#grid");
var addBox = document.createElement("div");
addBox.classList.add("gridBox");
addBox.textContent = (" ");
selectBody.appendChild(addBox);
hoverColor();
};
};
There are two components to your issue. One is that you are repeatedly modifying the DOM in a loop. You can fix it by appending all your boxes to a DocumentFragment and then adding that to the DOM after your loop finishes. You are also calling hoverColor(); inside your loop which results in adding tons of event listeners that all do the same thing (since inside hoverColor you are adding a listener to every single div). You can fix both those issues like this:
var fragment = document.createDocumentFragment( );
for (var i = 0; i < boxDimensions ; i++) {
var addBox = document.createElement("div");
addBox.classList.add("gridBox");
addBox.textContent = (" ");
fragment.appendChild(addBox);
}
document.querySelector("#grid").appendChild( fragment );
hoverColor();
Here is a JSFiddle with your original code, and here is one with the modification.
You could also benefit from only having one event listener total. You don't need to loop and add an event listener to every div. Just add one to #grid and use event.target (like you already do, to find the div that the event originated from). Something like this:
function hoverColor(){
document.querySelector("#grid").addEventListener( 'mouseover', function ( event ) {
event.target.style.backgroundColor = "grey";
} );
}
Related
I want to write a program with one html page, which i can update and fill with different elements via javascript with one button that stays the same in every version, which displays a modalBox. I made a very basic version of this: One page, that is filled with two buttons (next and last) for navigating through the pages and one to display the modal. In addition, i added a number, which is incremented oder decremented accordingly, when you click through the updated versions of the page.
var counter = 1;
function setUp(){
var c = document.getElementById("container");
var d = document.createElement("div");
d.setAttribute("id", "main");
d.innerHTML = counter;
var nxt = document.createElement("button");
var bck = document.createElement("button");
var modalBtn = document.createElement("button");
nxt.innerText = ">";
bck.innerText = "<";
modalBtn.innerText="Show Modal";
nxt.setAttribute("onclick","nextPage()");
bck.setAttribute("onclick","lastPage()");
modalBtn.setAttribute("onclick","showModal()");
d.appendChild(bck);
c.appendChild(d);
d.appendChild(nxt);
d.appendChild(modalBtn);
}
function showModal(){
var m = document.getElementById("modal");
m.style.display = "block";
}
function closeModal(){
var m = document.getElementById("modal");
m.style.display = "none";
}
function nextPage(){
var c = document.getElementById("container");
c.innerHTML="";
counter++;
setUp();
}
function lastPage(){
var c = document.getElementById("container");
c.innerHTML="";
counter--;
setUp();
}
setUp();
*{
padding: 0;
margin: 0;
}
#modal{
position: absolute;
width: 500px;
height: 300px;
background-color: black;
display: none;
}
#main{
background-color: aliceblue;
height: 500px;
width: 800px;
}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="tryout.css">
<script src="tryout.js" defer></script>
<title>Document</title>
</head>
<body>
<div id="container">
<div id="modal"><button id="closeButton" onclick="closeModal()">Close</button></div>
</div>
</body>
</html>
The problem is: on onload, the modal button works fine (on click, the modal is displayed). As soon as i update (not reloading!) the page via the next- or back button, the modal button stops working (Error-message says the type of modalbutton is null). I have no clue why, because to my knowledge, the buttons are reinitiated by clicking on the next or back button (because the setUp()-function is called in the functions triggered by the buttons). As soon as I reload the page via the reload-button, it is working until i use one of the next and back buttons.
I am new to js, it's probable that I'm missing sth. obvious here :) Many Thanks!
On the "nextPage" and "lastPage" function , you're just removing the whole content (including the modal) from the div which associated container class.
That's why when you calling the "showModal" function , there is no element with modal id on the DOM. That's why its saying null.
you can follow what Sakil said on the comment or, in your both(nextPage & lastPage) functions,you can just remove the div with id main and add it later on "setUp" function.
I'm adding some code snippet below, hope it will help;
function nextPage() {
//grab the div with "main" id
let main = document.getElementById("main");
//remove it from document
main.remove();
counter++;
//add it again; what you're doing actually.
setUp();
}
function lastPage() {
//grab the div with "main" id
let main = document.getElementById("main");
//remove it from document
main.remove();
counter--;
//add it again; what you're doing actually.
setUp();
}
of course you should refactor the code base, cause there is lot more copy-pasting staff present, but I'm leaving that up to you.
Hello I'm pretty new to JS and HTML and was trying to make something close to a text editor component like Monaco Editor.
How it works-
function Ventify() takes an element and adds a gutter and a textarea. So I made an if statement in this function that checks the no of lines in the textarea and create gutter lines accordingly. the problem I experienced while doing this was that the function only numbers the lines of the text when it was loaded because the function ended. Is there a way in which I can make the function/if statement never end.
Here is a codepen for the project: https://codepen.io/chrismg/pen/qgxOxg .
JS(with JQuery):
function Ventify(element){
element.style.display="flex";
element.style.flexDirection = "row";
var gutter = document.createElement("div");
var textarea = document.createElement("textarea");
gutter.className = "gutter";
textarea.className = "ventiEditor";
gutter.style.width = "100px";
gutter.style.height = "100%";
gutter.style.backgroundColor = "#1d252c";
textarea.style.width = "calc(100% - 100px)";
textarea.style.overflowY= "scroll";
textarea.style.whiteSpace = "pre";
textarea.style.resize = "none";
textarea.style.height = "100%";
textarea.style.margin = "0px 0px 0px 0px"
textarea.style.border = "0px solid rgb(255,255,255)"
textarea.style.backgroundColor = "#1d252c";
textarea.value = "\n\n\n\n";
element.appendChild(gutter);
element.appendChild(textarea);
if(gutter.childNodes.length != $(textarea).val().split("\n").length){
while(gutter.childNodes.length < $(textarea).val().split("\n").length){
var gutterChild = document.createElement("div");
gutterChild.style.width = "100%";
gutterChild.style.color = "rgba(58,74,88,1)"
gutterChild.style.textAlign = "center";
gutter.appendChild(gutterChild);
gutterChild.innerHTML = `${gutter.childNodes.length}`
}
}
}
Ventify(document.getElementsByClassName("container")[0])
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test Ground</title>
<style>
html,body{
height: 100%;
margin: 0px 0px 0px 0px;
}
.container{
height: 50%;
}
</style>
</head>
<body>
<div class="container"></div>
</body>
</html>
You can use input event attached to <textarea> element to perform a task at user input. See How to continually add typed characters to a variable?.
If you are trying to check if an element childList or attributes have changed you can use MutationObserver. See call function on change of value inside <p> tag.
A better approach will be using a onKeydown ( or similar events ) event on a input or textarea. Like in below example
function handle(){
let element = document.getElementById('inp').value;
console.log(element, 'called on change')
}
<input id='inp' onKeydown=handle() />
In your code can use setInterval. And clearinterval when you want to stop.
setInterval(Ventify(document.getElementsByClassName("container")[0]),200)
Codepen
You can use the onchange event to fire your function every time the user updates the content.
const element = document.getElementsByClassName("container")[0];
element.onchange = (event) => Ventify(event.target, 200);
I've been going through all the related questions but none of the solutions have been working for me. I am extremely new to JavaScript and I'm confused as to how to make a list that I created with JavaScript have clickable items. The most recent attempt included attempting to make an alert pop up on click but instead it just pops up the second the page loads. Please help! Here is my current code:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="css/m-buttons.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
div.links{
margin: auto;
border: 3px solid #003366;
text-align: left;
max-width: 700px;
}
p{
font-size: 40px;
text-align: center;
}
li{
font-size: 1w;
}
body{
font-family: verdana;
}
</style>
</head>
<body>
<div class = "links">
<ul id="blah"></ul>
<script>
var testArray = ["One","Two","Three","Four","Five","Six","Seven"];
function makeLongArray(array){
for(var i = 0; i < 1000; i++){
array.push(i);
}
}
makeLongArray(testArray);
function makeUL(array) {
// Create the list element:
var list = document.createElement("UL");
list.setAttribute("id", "blah");
for(var i = 0; i < array.length; i++) {
// Create the list item:
var item = document.createElement("LI");
// Set its contents:
item.appendChild(document.createTextNode(array[i]));
// Add it to the list:
list.appendChild(item);
list.onclick = alert("Help."); //this is the code causing the issue.
}
// Finally, return the constructed list:
return list;
}
// Add the contents of options[0] to #foo:
document.getElementById("blah").appendChild(makeUL(testArray));
</script>
</div>
</body>
</html>
Your existing code will execute alert('Help.') every time you execute this line of code list.onclick = alert("Help.");
What you need to do is assign a function to onclick. This function then get executed when onclick is executed. As follows:
item.onclick = function() {console.log('hello world');};
Now each list item's onclick event has a function assigned to it that outputs hello world to the console everytime the list item is clicked.
You need to assign a function to the onclick event:
list.onclick = function(){ alert("Help."); }
Here is my solution:
function toggleDone(event) {
var ev = event.target;
ev.classList.toggle("done");
}
ul.addEventListener("click", toggleDone);
I was always under the impression that rather than touching the DOM repeatedly, for performance reasons, we should use a documentFragment to append multiple elements to, and then append the fragment into the document once, rather than just repeatedly appending new elements one-by-one into the DOM.
I've been trying to use Chrome's dev tools to profile these two approaches, using this test page:
<button id="addRows">Add rows</button>
<table id="myTable"></table>
Test 1 uses this code to append 50000 new rows to the table:
let addRows = document.getElementById('addRows');
addRows.addEventListener('click', function () {
for (let x = 0; x < 50000; x += 1) {
let table = document.getElementById('myTable'),
row = document.createElement('tr'),
cell = document.createElement('td'),
cell1 = cell.cloneNode(),
cell2 = cell.cloneNode(),
cell3 = cell.cloneNode();
cell1.textContent = 'A';
cell2.textContent = 'B';
cell3.textContent = 'C';
row.appendChild(cell1);
row.appendChild(cell2);
row.appendChild(cell3);
table.appendChild(row);
}
});
Clicking the button while recording in Chrome's Timeline tool results in the following output:
Test 2 uses this code instead:
let addRows = document.getElementById('addRows');
addRows.addEventListener('click', function () {
let table = document.getElementById('myTable'),
cell = document.createElement('td'),
docFragment = document.createDocumentFragment();
for (let x = 0; x < 50000; x += 1) {
let row = document.createElement('tr'),
cell1 = cell.cloneNode(),
cell2 = cell.cloneNode(),
cell3 = cell.cloneNode();
cell1.textContent = 'A';
cell2.textContent = 'B';
cell3.textContent = 'C';
row.appendChild(cell1);
row.appendChild(cell2);
row.appendChild(cell3);
docFragment.appendChild(row);
}
table.appendChild(docFragment);
});
This has a performance profile like this:
The second, supposedly much faster alternative to the first, actually takes longer to run! I've run these tests numerous times and sometimes the second approach is slightly faster, and sometimes, as these images show, the second approach is slightly slower, but not once has there been any significant difference between the two approaches.
What is happening here? Are browsers optimized so well now that this makes no difference anymore? Am I using the profiling tools incorrectly?
I'm on Windows 10, with Chrome 57.0.2987.133
Well actually your test code just insert nodes and do not alter their content or CSS which would in fact force the rendering engine to a reflow.
I have prepared 3 tests to demonstrate this dramatic difference.
Simple DOM modificiation resulting Layout Trashing
Simple DOM modificiation through window.requestAnimationFrame()
Virtual DOM modification through document.createDocumentFragment()
Notes:
You might like to test on full screen.
In Firefox I have obtained even more dramatic results.
// Resets the divs
function resetLayout() {
divs = document.querySelectorAll('div');
speed.textContent = "Resetting Layout...";
setTimeout(function() {
each.call(divs, function(div) {
div.style.height = '';
div.offsetTop;
});
speed.textContent = "";
}, 16);
}
// print the result
function renderSpeed(ms) {
speed.textContent = ms + 'ms';
}
var divs = document.querySelectorAll('div'),
raf = window.requestAnimationFrame,
each = Array.prototype.forEach,
isAfterVdom = false,
start = 0;
// Reset the Layout
reset.onclick = resetLayout;
// Direct DOM Access
direct.onclick = function() {
isAfterVdom && (divs = document.querySelectorAll('div'), isAfterVdom = false);
start = performance.now();
each.call(divs, function(div) {
var width = div.clientWidth;
div.style.height = ~~(Math.random()*2*width+6) + 'px';
div.style.backgroundColor = '#' + Math.floor(Math.random() * 16777215).toString(16);
});
// Render result
renderSpeed(performance.now() - start);
};
// Access DOM at the next frame by requestAnimationFrame
rAF.onclick = function() {
isAfterVdom && (divs = document.querySelectorAll('div'), isAfterVdom = false);
start = performance.now();
each.call(divs, function(div) {
var width = div.clientWidth;
// Schedule the write operation to be run in the next frame.
raf(function() {
div.style.height = ~~(Math.random()*2*width+6) + 'px';
div.style.backgroundColor = '#' + Math.floor(Math.random() * 16777215).toString(16);
});
});
// Render result
raf(function() {
renderSpeed(performance.now() - start);
});
};
// Update the vDOM and access DOM just once by rAF
vdom.onclick = function() {
var sectCl = divCompartment.cloneNode(true),
divsCl = sectCl.querySelectorAll('div'),
dFrag = document.createDocumentFragment(),
width = divCompartment.querySelector('div').clientWidth;
isAfterVdom = true;
end = 0;
start = performance.now();
each.call(divsCl, function(div) {
div.style.height = ~~(Math.random()*2*width+6) + 'px';
div.style.backgroundColor = '#' + Math.floor(Math.random() * 16777215).toString(16);
});
dFrag.appendChild(sectCl);
divCompartment.parentNode.replaceChild(dFrag, divCompartment);
// Render result
renderSpeed(performance.now() - start);
};
html {
font: 14px Helvetica, sans-serif;
background: black;
color: white;
}
* {
box-sizing: border-box;
margin-bottom: 1rem;
}
h1 {
font-size: 2em;
//word-break: break-word;
-webkit-hyphens: auto;
}
button {
background-color: white;
}
div {
display: inline-block;
width: 5%;
margin: 3px;
background: white;
border: solid 2px white;
border-radius: 10px
}
section {
overflow: hidden;
}
#speed {
font-size: 2.4em;
}
<body>
<h1>Updating 1000 DOM Nodes</h1>
<section>
<button id="reset">Reset Layout</button>
<button id="direct">Update Directly</button>
<button id="rAF">Update by rAF</button>
<button id="vdom">Update by vDOM</button>
<section id="speed"></section>
<section id="divCompartment">
<div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div>
</section>
</body>
Sorry... Due to the 30000 character limit in the answers and the length of the previous one i have to place another one as an extension to my previous answer.
I guess everybody's heard that direct DOM access is no good... Yes that's what i had always been told and so believed in the correctness of the virtual DOM approach. Though i hadn't quite understood the fact that while both the DOM and vDOM are represented on the memory, how come one is faster than the other? Actually streching my test further i have come to believe that the real bottle neck boils down to the JS engine performance if you update the DOM properly.
Now lets imagine the case of having 1000 divs to be updated repeatedly on background-color and height CSS properties.
If you do this directly on DOM elements all you have to do is to keep a nodeList of these elements and simply alter their style.backgroundColor and style.heigth properties.
If you do this by a document fragment you have the apparent benefit of not touching the DOM multiple times, instead first you have to
clone the parent container of the 1000 div elements.
access the nodeList containing the divs like parent.children
perform necessary alterations on each div element,
create a document fragment
re-clone the previously cloned (step 1) parent container element and append it to a document fragment (or alternatively you may chose to clone the divs' container from the DOM if you need the fresh ones but this way or that way for each modification you have to clone them)
append the document fragment to the parent of div container in the DOM and remove the old div container. Basically a Node.replaceChild operation.
In fact for this test we don't need a document fragment since we are not creating new nodes but just updating the already existing ones. So we can skip the step 4 and at step 5 we can directly use the cloned copy of our divs container as a source to our replaceChild operation.
How to update DOM properly..? Definitely asynchronously. So as for the previous example if you move the direct update portion to an asynchronous timeline like setTimeout(_ => renderDirect(),0) it will be the fastest among all. But then repeatedly updating the dome can be a little tricky.
One way to achieve this is to repedeatly feed a setTimeout(_ => renderDirect(),0) with our DOM updates like.
for (var i = 0; i < cnt; i++) setTimeout(_ => renderDirect(divs,width),0);
In the above case the performance of the JS engine is very material on the results. If our code is too light, than multiple cycles will stack up on a single DOM update and we will observe only a few of them. In this particular case, we got to see only like 9 of the 50 updates.
So delaying each turn further might be a good idea. So how about;
for (var i = 0; i < cnt; i++) setTimeout(_ => renderDirect(divs,width),i*17);
Well this is much better, I've got 22 of the 50 updates actually painted on my screen. So it happens to be, if the delay is chosen to be long enough you'll have all the frames painted. But how much long is a problem. Since if it's too long you have idle time for your rendering engine and it resembles slow DOM update. So for this particular test, it turns out to be something like 29-30ms ish... is the optimal value to observe 50 separate DOM updates of all 1000 divs in 1400 ms. Well at least on my desktop with Chrome. You may observe something entirely different depending on the hardware or the browser.
So the setTimeout resolution doesn't look very promising to me. We have to automate this job. Lucky us, we have the right tool for this job. rAF to help again. I have come up with a helper function to abuse the rAF (requestAnimationFrame). We will update the 1000 divs all at once, in one go, by directly accessing the DOM at the next available animation frame. And... while we are still in the asynchronous timeline we will request another animation frame from within the currently executing callback. So another rAF is called from the callback of the rAF recursively. I named this function looper
var looper = n => n && raf(_ => (renderDirect(divs, width),looper(--n)));
well it's a bit of an ES6 code. So let me translate it into classing JS.
function looper(n){
if (n !== 0) {
window.requestAnimationFrame(function(){
renderDirect(divs,width);
looper(n-1);
});
}
}
Now everything should be automated.. It seems pretty cool and done in 1385ms.
So since now we are a little more knowledgeable we may play with the code.
// Resets the divs
function resetLayout() {
divs = document.querySelectorAll('div');
speed.textContent = "Resetting Layout...";
setTimeout(function() {
each.call(divs, function(div) {
div.style.height = '';
div.backcgoundColor = '';
});
speed.textContent = "";
}, 16);
}
// print the result
function renderSpeed(ms) {
speed.textContent = ms + 'ms';
}
function renderDirect(divs,width){
each.call(divs, function(div) {
div.style.height = ~~(Math.random()*2*width+6) + 'px';
div.style.backgroundColor = '#' + Math.random().toString(16).substr(-6);
});
// Render result
renderSpeed(performance.now() - start);
}
function renderByVDOM(sct,prt,wdt){
var //dFrag = document.createDocumentFragment();
divs = sct.children;
each.call(divs, function(div) {
div.style.height = ~~(Math.random()*2*wdt+6) + 'px';
div.style.backgroundColor = '#' + Math.random().toString(16).substr(-6);
});
//dFrag.appendChild(sct);
prt.replaceChild(sct, divCompartment);
// Render result
renderSpeed(performance.now() - start);
}
var divs = document.querySelectorAll('div'),
width = divs[1].clientWidth;
raf = window.requestAnimationFrame,
each = Array.prototype.forEach,
isAfterVdom = false,
start = 0,
cnt = 50;
// Reset the Layout
reset.onclick = resetLayout;
// Direct DOM Access
direct.onclick = function() {
var looper = n => n && raf(_ => (renderDirect(divs, width),looper(--n)));
isAfterVdom && (divs = document.querySelectorAll('div'), isAfterVdom = false);
start = performance.now();
//for (var i = 0; i < cnt; i++) setTimeout(_ => renderDirect(divs,width),i*29);
looper(cnt);
};
// Update the vDOM and access DOM just once by rAF
vdom.onclick = function() {
var sectCl = divCompartment.cloneNode(true),
parent = divCompartment.parentNode,
looper = n => n && raf(_ => (renderByVDOM(sectCl.cloneNode(true), parent, width),looper(--n)));
isAfterVdom = true;
start = performance.now();
looper(cnt);
};
html {
font: 14px Helvetica, sans-serif;
background: black;
color: white;
}
* {
box-sizing: border-box;
margin-bottom: 1rem;
}
h1 {
font-size: 2em;
-webkit-hyphens: auto;
}
button {
background-color: white;
}
div {
display: inline-block;
width: 5%;
margin: 3px;
background: white;
border: solid 2px white;
border-radius: 10px
}
section {
overflow: hidden;
}
#speed {
font-size: 2.4em;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>repl.it</title>
<link href="index.css" rel="stylesheet" type="text/css" />
<script src="index.js" async></script>
</head>
<body>
<h1>Updating 1000 DOM Nodes</h1>
<section>
<button id="reset">Reset Layout</button>
<button id="direct">Update Directly</button>
<button id="vdom">Update by vDOM</button>
<section id="speed"></section>
<section id="divCompartment">
<div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div>
</section>
</section>
</body>
</html>
So the tests look like direct access through rAF is better than cloning and working on the clone and replacing the old one with it. Particularly when a huge DOM chunk is replaced it seems to me that the GC (Garbage Collect) task gets involved in the middle of the job and things get a little sticky. I am not sure how it can be eliminated. Your ideas are most welcome.
A Side Note:
This test also shows that the current (Version 91.0.838.3) of the new chromium based Edge Browser is performing DOM renderings ~15% faster than the current (Version 89.0.4389.114) Chrome.
I'm not sure how exactly to interpret your profiler results but running it through benchmark.js test shows second option is faster in Chrome: https://jsbench.me/awj1gwhk9i/1
ALso faster in FF (although a bit less difference)
I am trying to build my own carousel with pure JavaScript.
I'm struggling with picking up the most efficient way to add an infinite carousel option.
For some reasons, every element (photo, generic object) must have an id
The algorithm I see goes like that:
You check if the carousel is overflown (the are enough objects to fit
the whole container)
If not: append to the back a copy of the first element, then
a copy of the second element and so on. (But there will be an issue with the ids, because this object will have the same id)
- If the user is scrolling to the last object (to right) then append
the first DOM object to the array back
- If the user is scrolling to
the first object (to left) then add the last DOM child to array
front.
Is this going to work? Is there any other efficient way of doing an infinite carousel?
I have also heard that it's better to use translate property rather than changing the left, right properties, so it there would be more work for the GPU than for CPU.
I created a simple slider with css transformations as the animation technique and plain Javascript.
var img = document.getElementsByClassName("img")[0];
img.style.transform = 'translate('+value+'px)';
You can test it in this codepen snippet.
http://codepen.io/TobiObeck/pen/QKpaBr
A press on a button translates all images in the respective direction along the x-axis. An image on the edge, is set transparent outerImg.style.opacity = '0'; and translated to the other side. You can add or remove image elements in HTML and it still works.
In this second codepen snippet you can see how it works. The opacity is set to 0.5 so it is observable which image switches the side. Because overflow: hidden is removed, you can see how the images on the edge enqueue on the other side.
http://codepen.io/TobiObeck/pen/WGpdLE
Moreover it is notworthy that it is checked wether the animation is complete, otherwise the simultaneously added translations would look odd. Therefore a click won't trigger another animation until unless the animation is completed.
img.addEventListener("transitionend", transitionCompleted, true);
var transitionCompleted = function(){
translationComplete = true;
}
leftBtnCLicked(){
if(translationComplete === true){
//doAnimation
}
}
you can use this code to manipulate slides. This basically rotates the array back and front
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body {
width: 100%;
height: 100%;
}
.parentDiv {
height: 30%;
width: 100%;
display: flex;
}
</style>
<title>test</title>
</head>
<body>
<button class="fwd"> Fwd! </button>
<button class="bkwd"> Bkwd! </button>
<script type="text/javascript">
const arr = ['red', 'blue', 'coral', 'green', 'yellow'];
let narr = ['red', 'blue', 'coral'];
const parentDiv = document.createElement('div');
parentDiv.setAttribute('class', 'parentDiv');
document.body.insertAdjacentElement('afterbegin', parentDiv);
window.onload = ()=> {
narr.forEach(color => {
while(parentDiv.children.length < narr.length){
const childDiv = document.createElement('div');
parentDiv.appendChild(childDiv);
};
});
Array.from(parentDiv.children).forEach((child, index) => {
child.style.border = '1px #000 dotted';
child.style.minWidth = '20%';
child.style.minHeight = '20vh';
child.style.backgroundColor = narr[index]
});
};
document.querySelector('.fwd').addEventListener('click', ()=>{
narr.shift();
if(narr[narr.length-1] === arr[arr.length-1]){
narr.push(arr[0])
} else {
narr.push(arr[arr.indexOf(narr[narr.length-1])+1])
}
narr.forEach(color => {
while(parentDiv.children.length < narr.length){
const childDiv = document.createElement('div');
parentDiv.appendChild(childDiv);
};
});
Array.from(parentDiv.children).forEach((child, index) => {
child.style.border = '1px #000 dotted';
child.style.minWidth = '20%';
child.style.minHeight = '20vh';
child.style.backgroundColor = narr[index];
});
})
document.querySelector('.bkwd').addEventListener('click', ()=>{
narr.pop();
if(narr[0] === arr[0]){
narr.unshift(arr[arr.length-1])
} else {
narr.unshift(arr[arr.indexOf(narr[0])-1])
}
narr.forEach(color => {
while(parentDiv.children.length < narr.length){
const childDiv = document.createElement('div');
parentDiv.appendChild(childDiv);
};
});
Array.from(parentDiv.children).forEach((child, index) => {
child.style.border = '1px #000 dotted';
child.style.minWidth = '20%';
child.style.minHeight = '20vh';
child.style.backgroundColor = narr[index]
});
})
</script>
</body>
</html>