After adding querySelector inside function page crash - javascript

With this function, every div element that has children needs to wrap the children in a wrap div. Everything works fine if
<button id="run" onclick="wrapChildren(document.querySelector('#element1'))">Run</button>
but at the moment when I insert in the function:
var element = document.querySelector('#element1');
the page is crashing "devtools was disconnected from the page". Why is this happening and how to fix?
function wrapChildren() {
var element = document.querySelector('#element1');
const childElements = Array.from(element.children);
if (childElements.length === 0) {
return;
}
const wrapDiv = document.createElement('div');
wrapDiv.id = element.id+"wrap";
childElements.forEach((childElement) => {
wrapDiv.appendChild(childElement);
});
element.appendChild(wrapDiv);
childElements.forEach((childElement) => {
wrapChildren(childElement);
});
}
<div id="element1">
<div id="child1">
<div id="grandchild1"></div>
<div id="grandchild2">
<div id="granddrandchild1"></div>
<div id="granddrandchild2"></div>
<div id="granddrandchild3">
<div id="granddrandgrandchild1"></div>
</div>
</div>
</div>
<div id="child2"></div>
</div>
<button id="run" onclick="wrapChildren()">Run</button>

The way you have written it, your wrapChildren function doesn't actually take any arguments:
function wrapChildren()
so it was always running exactly the same code, even when you attempt to call it recursively with a different argument (in the forEach at the end of your function). As a result, your code leads to infinite recursion and hence a page crash.
To fix this, just give it an element as the argument, and use this element in the function body rather than hardcoding element to be the one with id element1.
I have made this change below and there is no crash any more. The function doesn't actually appear to do anything very much, but I'll leave that to you to sort out, or perhaps ask a new question about. (I don't actually know what this is trying to do.)
function wrapChildren(element) {
const childElements = Array.from(element.children);
if (childElements.length === 0) {
return;
}
const wrapDiv = document.createElement('div');
wrapDiv.id = element.id+"wrap";
childElements.forEach((childElement) => {
wrapDiv.appendChild(childElement);
});
element.appendChild(wrapDiv);
childElements.forEach((childElement) => {
wrapChildren(childElement);
});
}
<div id="element1">
<div id="child1">
<div id="grandchild1"></div>
<div id="grandchild2">
<div id="granddrandchild1"></div>
<div id="granddrandchild2"></div>
<div id="granddrandchild3">
<div id="granddrandgrandchild1"></div>
</div>
</div>
</div>
<div id="child2"></div>
</div>
<button id="run" onclick="wrapChildren(document.querySelector('#element1'))">Run</button>

Related

When clicking on one of the tags with the same class as javascript, how to access the clicked one? [duplicate]

This question already has answers here:
How to get the element clicked (for the whole document)?
(10 answers)
Closed 12 months ago.
When clicking on one of the tags with the same class as javascript, how to access the clicked one? I tried by clicking, I looked at the problems, but unfortunately I could not solve the problem, it is very important for me, thanks in advance. Kind regards
HTML:
<div class="money-select">
<div class="money-check">
<div class="money-check-left">
<img src="https://cdn.dsmcdn.com/ty100/product/media/images/20210408/17/78753423/161972772/0/0_org_zoom.jpg">
<h2>BTC</h2>
</div>
<div class="money-check-right">
<i class="fi-xwsdxl-chevron-wide"></i>
</div>
</div>
<div class="dropdown">
<div class="money-check">
<div class="money-check-left">
<img src="https://cdn.iconscout.com/icon/free/png-256/ethereum-2296075-1912034.png">
<h2>ETH</h2>
</div>
</div>
<div class="money-check">
<div class="money-check-left">
<img src="https://cryptologos.cc/logos/thumbs/bnb.png?v=022">
<h2>BNB</h2>
</div>
</div>
<div class="money-check">
<div class="money-check-left">
<img src="https://cryptologos.cc/logos/thumbs/shiba-inu.png?v=022">
<h2>SHIBA</h2>
</div>
</div>
<div class="money-check">
<div class="money-check-left">
<img src="https://cryptologos.cc/logos/thumbs/decentraland.png?v=022">
<h2>DTC</h2>
</div>
</div>
</div>
</div>
JS:
let moneySelect = document.querySelector(".money-select");
let moneyList = moneySelect.querySelector(".money-check");
let moneyValue = moneyList.querySelector("h1");
let input = document.getElementById("htmlinamk");
for (var i = 0; i < moneyList.length; i++) {
moneyList[i].addEventListener("click", function () {
a = moneyValue.innerText;
input.value = a;
});
}
Sorry for my bad english
The event listener function receives the event object, which has a target property that would point to the div element with class money-check. You could then traverse the DOM tree and get down to child of child and extract the inner text from the header tag. Something like this:
moneyList[i].addEventListener("click", (event) => {
const innerText = event.target.child<traverse here>.innerText;
input.value = innerText;
});
There are 2 ways to get the element that was clicked. this, or event.target. Either of the techniques will work for this example:
moneyList[i].addEventListener("click", function (e) {
let a = this.querySelector('H2').innerText;
// let a = e.target.querySelector('H2').innerText;
input.value = a;
});

How to add function in every single element in javascript nodelist

so here's an example of the working code
const test = document.getElementById('test')
const time = 1000
var timer;
test.addEventListener('touchstart', hlt)
test.addEventListener('touchend', endt)
function onlongtouch(){
console.log('it has been 600 milisecond')
}
function hlt(){
console.log('test');
timer = setTimeout(onlongtouch, time)
console.log(timer)
}
function endt(){
if(timer){
clearTimeout(timer)
}
}
And the HTML
<div class="data" id="test">
<div class="num"><p>1</p></div>
<div class="title"><p>Crush It</p></div>
<div class="author"><p>Gary Vaynerchunk</p></div>
<div class="ISBN"><p>132132312</p></div>
</div>
<div class="data">
<div class="num"><p>2</p></div>
<div class="title"><p>Crush It</p></div>
<div class="author"><p>Gary Vaynerchunk</p></div>
<div class="ISBN"><p>132132312</p></div>
</div>
<div class="data">
<div class="num"><p>3</p></div>
<div class="title"><p>Crush It</p></div>
<div class="author"><p>Gary Vaynerchunk</p></div>
<div class="ISBN"><p>132132312</p></div>
</div>
so i was taking a single element with an id of "test" and add the touch functionality to it, but what if i replace const test = document.getElementById('test') with document.querySelectorAll('data') im grabbing the same element but instead of a single element it's a whole nodelist full of element. How do i add functionality to all of them?
Thank you in advance!
let elements = [...document.querySelectorAll('.data')];
for(let el of elements) {
el.addEventListener('touchstart', hlt)
el.addEventListener('touchend', endt)
}

Removing images using event handlers

I really can't figure out how to solve this problem. Here is the question and the original code.
Question: Implement the setup function that registers a click event
handler and implements the following logic: When the button of class
remove is clicked, its parent element should be removed from the
gallery.
function setup() {
**//IM SUPPOSED TO PUT MY CODE ONLY IN THIS PART//**
}
// Example case.
document.body.innerHTML = `
<div class="image">
<img src="firstimage.jpg" alt="First">
<button class="remove">X</button>
</div>
<div class="image">
<img src="secondimage.jpg" alt="Second">
<button class="remove">X</button>
</div>`;
setup();
document.getElementsByClassName("remove")[0].click();
console.log(document.body.innerHTML);
This is what I have. As soon as I run the program, it removes the first image without the user clicking on it. And I have no idea how to fix it.
function setup() {
var myImage = document.getElementsByClassName("image");
document.getElementsByClassName("remove")[0].
addEventListener("click", function(){
myImage[0].parentNode.removeChild(myImage[0]);});
}
// Example case.
document.body.innerHTML = `
<div class="image">
<img src="firstimage.jpg" alt="First">
<button class="remove">X</button>
</div>
<div class="image">
<img src="secondimage.jpg" alt="Second">
<button class="remove">X</button>
</div>`;
setup();
document.getElementsByClassName("remove")[0].click();
console.log(document.body.innerHTML);
The getElementsBy* methods return HTMLCollections, which can be difficult to work with. Consider using querySelectorAll instead, which returns a static NodeList - unlike an HTMLCollection, it can be iterated over directly, it won't change while it's being iterated over, and it's much more flexible.
You want to iterate over each element, which is a lot more elegant than assigning to each element in the collection individually, so try something like this instead:
document.querySelectorAll('.remove')
.forEach(button =>
button.addEventListener('click', () => button.parentElement.remove())
)
.remove removes an element from the DOM.
The reason why the first image is removed automatically before you even click on it is because of the document.getElementsByClassName("remove")[0].click(); which is directly under the setup() function call.
Which means as soon as the function is called to perform the task, document.getElementsByClassName("remove")[0].click(); is immediately performed and removes the first image using index 0 and click().
So to solve this, try removing that [0] index or remove document.getElementsByClassName("remove")[0].click(); which is not useful in your case, and see how it goes.
function setup() {
let myImage = document.querySelectorAll(".remove").forEach(function (button){
button.addEventListener('click', function(){
button.parentElement.remove()
})
});
}
// Example case.
document.body.innerHTML = `
<div class="image">
<img src="firstimage.jpg" alt="First">
<button class="remove">X</button>
</div>
<div class="image">
<img src="secondimage.jpg" alt="Second">
<button class="remove">X</button>
</div>`;
setup();
document.getElementsByClassName("remove").click();
console.log(document.body.innerHTML);
import React from 'react';
import {useState} from 'react';
const ImageGallery = ({links}) =>{
const [data,setData] = useState(links)
const removeElement = (removeElement) => {
const newsetdata = data.filter((index) => index !== removeElement);
setData(newsetdata);
console.log(newsetdata)
// setData(data.splice(index,1));
};
return (
<>
<div>
{
data.map((abc,i,data)=> {
return(
<div key={i}>
<img src={abc} alt="images"/>
<button onClick={() => removeElement(data[i])}>X
</button>
</div>
)
})
}
</div>
</>
)
}
export default ImageGallery;

Slider animation handle with JQuery, hover element issue using promises

I have problem with a slider. It works correctly without one, strange for me, situation. When I mouseover fast from one dot to another, it will not wait until previous animation ends and two texts overlap. Can somebody older and wiser help me?
HTML structure of project:
<section class="product-section">
<div class="vertical-text vertical-text-custom-5">
Pluginy
</div>
<div class="carrousel-image-container-1 product-release-management">
<i class="image-carrousel-1"></i>
</div>
<div class="carrousel-image-container-2 product-SLA">
<i class="image-carrousel-2"></i>
</div>
<div class="carrousel-image-container-3 product-test-management">
<i class="image-carrousel-3"></i>
</div>
<div class="col-custom-5">
<div class="col-custom-7 text-size-xl">
<div class="text-container-17">
<div class="product product-release-management">
<span class="text-color-6 text-weight-thin">Rivet</span> <br>
<span class="text-color-5 text-weight-bold">Release Management</span> <br>
<span class="text-color-3 text-weight-bold">plugin</span>
</div>
<div class="product product-SLA">
<span class="text-color-6 text-weight-thin">Rivet</span> <br>
<span class="text-color-5 text-weight-bold">SLA</span> <br>
<span class="text-color-3 text-weight-bold">plugin</span>
</div>
<div class="product product-test-management">
<span class="text-color-6 text-weight-thin">Rivet</span> <br>
<span class="text-color-5 text-weight-bold">Test Management</span> <br>
<span class="text-color-3 text-weight-bold">plugin</span>
</div>
</div>
<div id="carrousel-dots-contener" class="carrousel-dots text-color-5">
<div class="dot-container" data-carrousel-dot='dot-1'>
<div class="dot-border">
<div class="dot dot-custom-2">●</div>
</div>
</div>
<!--
-->
<div class="dot-container" data-carrousel-dot='dot-2'>
<div class="dot-border">
<div class="dot dot-custom-2">●</div>
</div>
</div>
<!--
-->
<div class="dot-container" data-carrousel-dot='dot-3'>
<div class="dot-border">
<div class="dot dot-custom-2">●</div>
</div>
</div>
</div>
</div>
</div>
REST OF CODE HERE
These are the main issues:
The promise() call works fine when you are sure you don't need to interrupt the animation, but as soon as you have mouse events that need immediate action (like hideAll), this promise will become a problem: it will still resolve, but at an inconvenient moment. In fact, as soon as you do another animation like hideAll, you want to cancel the execution of the code that follows the resolved promise. So... add a condition before proceeding with fadeIn() to see that the product selection is still relevant.
runInterval calls cyclicChange immediately, which is great when the page loads, but which is a bit annoying when moving the mouse over one dot to the next: as the mouse may exit the area, runInterval gets called and makes the selection jump to potentially another dot, which makes it kinda jumpy. It is better to remove this immediate call to cyclicChange and then to add some code to show the first product when start runs.
To avoid unwanted queuing of animations, you could call stop(true) before doing fadeOut().
I applied these changes to your JavaScript code, where I also made some other improvements, unrelated to the problem:
var carrousel = (function() {
var interval = null,
products,
current = -1;
products = [
'.product-release-management',
'.product-SLA',
'.product-test-management'
];
function showProduct(id) {
// Avoid unnecessary work
if (id == current) return; // nothing to do;
// In other cases: hide first
hideAll();
current = id;
$('.product').promise().done(function() {
// Make sure selection is still like at the start of showProduct execution
if (current === id) $(products[current]).fadeIn(500);
});
$("div[data-carrousel-dot='dot-" + (current + 1) + "']").addClass('dot-active');
}
function hideAll() {
// 1. easier selector for selecting all product classes
// 2. stop any ongoing animation
$(products.join(",")).stop(true, true).fadeOut(500);
$("div[data-carrousel-dot").removeClass('dot-active');
}
function cyclicChange() {
if ( isNaN(interval) ) return; // timer is not active
showProduct((current + 1) % products.length); // easier way to roundtrip
}
function runInterval(){
interval = setInterval(cyclicChange, 3000);
}
function mouseOverDotHandler() {
$('.dot-container').hover(
function() {
// Easier way to get number
showProduct($(this).index());
}
);
$('#carrousel-dots-contener').hover(
function(){
clearInterval(interval);
interval = null; // use variable for indicating the pause
},
runInterval
);
}
return {
start: function() {
showProduct(0);
runInterval();
mouseOverDotHandler();
}
}
})();
$(document).ready(function(){
carrousel.start();
});
See it run on jsbin.com.

"Cut and Paste" - moving nodes in the DOM with Javascript

I have html code that looks roughly like this:
<div id="id1">
<div id="id2">
<p>some html</p>
<span>maybe some more</span>
</div>
<div id="id3">
<p>different text here</p>
<input type="text">
<span>maybe even a form item</span>
</div>
</div>
Obviously there's more to it than that, but that's the basic idea. What I need to do is switch the location of #id2 and #id3, so the result is:
<div id="id1">
<div id="id3">...</div>
<div id="id2">...</div>
</div>
Does anyone know of a function (I'm sure I'm not the first person to require this functionality) that can read and write the two nodes (and all their children) so as to swap their location in the DOM?
In this case, document.getElementById('id1').appendChild(document.getElementById('id2')); should do the trick.
More generally you can use insertBefore().
This function takes any node that is passed into it and wraps it with the tag given. In the example code snippet I wrapped a span tag with a section tag.
function wrap(node, tag) {
node.parentNode.insertBefore(document.createElement(tag), node);
node.previousElementSibling.appendChild(node);
}
function wrap(node, tag) {
node.parentNode.insertBefore(document.createElement(tag), node);
node.previousElementSibling.appendChild(node);
}
let toWrap = document.querySelector("#hi");
wrap(toWrap, "section");
console.log(document.querySelector("section > #hi"), " section wrapped element");
<span id="hi">hello there!</span>
You can use
insertAdjacentElement instead of appendChild to have more control about the position of element with respect to a target element.
Syntax: targetElement.insertAdjacentElement(position, element).
It has four position codes as:
'beforebegin': Before the targetElement itself.
'afterbegin': Just inside the targetElement, before its first child.
'beforeend': Just inside the targetElement, after its last child.
'afterend': After the targetElement itself.
it appears as:
//beforebegin
<p>
//afterbegin
foo
//beforeend
</p>
//afterend
In your case, you can write the code as:
document.getElementById('id2').insertAdjacentElement('beforebegin', document.getElementById('id3'));
Note that this way, you don't need reference the parent (container) element!
Also consider You have more elements than id2, id3, eg: id4, id5, id6. Now, if you want to reposition for example id5 after id2, its as simple as:
function changePosition() {
document.getElementById('id2').insertAdjacentElement('afterend', document.getElementById('id5'));
}
<div id='container'>
<div id='id1'>id1</div>
<div id='id2'><u>id2</u></div>
<div id='id3'>id3</div>
<div id='id4'>id4</div>
<div id='id5'><b>id5</b></div>
<div id='id6'>id6</div>
</div>
<p><input type='button' onclick="changePosition()" value="change position"></p>
In my opinion is worth adding that if you need just a visual change (the DOM will stay the same but I will change in the UI) you can use the CSS order property.
It is probably more efficient that working on the DOM like the other answers, althought again doesn't really change the DOM structure so of course is not a real answer to this question.
Example:
document.addEventListener("DOMContentLoaded", function () {
const btnEl = document.getElementById('btn-swap');
const elToSwap = document.getElementById('id2');
btnEl.addEventListener('click', e => {
elToSwap.classList.toggle("first");
});
});
.container {
display: flex;
flex-direction: column;
}
.first {
order: -1;
}
<div class="container">
<div id="id1">first DIV</div>
<div id="id2">second DIV</div>
</div>
<button id="btn-swap">swap divs</button>
Short
I just add button (at the bottom) and js to your html
id3.after(id2);
function swap() {
id3.after(id2);
}
<div id="id1">
<div id="id2">
<p>some html</p>
<span>maybe some more</span>
</div>
<div id="id3">
<p>different text here</p>
<input type="text">
<span>maybe even a form item</span>
</div>
</div>
<button onclick="swap()">swap</button>

Categories

Resources