I'm working on making a checkbox appear when a user hovers over a table's first data cell.
I used "firstDataCell.onmouseover" to add the checkbox and "firstDataCell.onmouseout" to remove it which works as the checkbox appears but you are unable to click on the checkbox as the events propagate when you've hovered on it. I think the solution would be in some way to stop the other event propagation while one event is active but can't seem to work it out with ".stopPropagation()".
Code bellow:
let taskTable=document.querySelector('#table_1')
let tableBody=taskTable.getElementsByTagName('tbody')[0].getElementsByTagName('tr')
let tableRowArr=[]
for(let i=0;i<tableBody.length;i++){
tableRowArr.push(tableBody[i])
}
tableRowArr.forEach((row,index) => {
let tableRowFirstDataCell=row.getElementsByTagName("td")[0]
let cellData=tableRowFirstDataCell.innerHTML
tableCellsData.push(cellData)
tableRowFirstDataCell.onmouseover=addCheckboxes
})
function addCheckboxes(event){
event.currentTarget.onmouseover=()=>{}
event.currentTarget.innerHTML=""
let checkBox=document.createElement("input")
checkBox.setAttribute("type","checkbox")
checkBox.setAttribute("class","check")
// checkBox.setAttribute("") //<----- Add styling to checkboxes
// checkBox.addEventListener("onclick",deleteTask(id))
event.currentTarget.appendChild(checkBox)
event.currentTarget.onmouseout=removeCheckboxes
}
function removeCheckboxes(event) {
event.currentTarget.onmouseout=()=>{}
event.currentTarget.innerHTML=""
event.currentTarget.innerHTML=tableCellsData[1]
event.currentTarget.onmouseover=addCheckboxes
}
Related
I have this code the target is to put it in a hidden menu (contains div in the code)
When after a click the date picker will show up, then I add the date to it and after that on blur, some elements will be created. The problem is that I want the date picker to be hidden and shown after a click on a div but this will cause the blur to run 3 times instead of one if I put the blue alone right in your face it acts normal, any idea how to prevent this triple-action from happening? and create the needed element once
let link = document.querySelector('.date-input')
let fatherClosest = document.querySelector('.contain')
fatherClosest.addEventListener("click", function (e) {
link.addEventListener("blur", (e) => {
console.log(e)
// a function that will created element on blur out form the date picker
fatherClosest.insertAdjacentHTML('afterbegin', '<div class="cla">hi</div>')
})
})
.cla {
height: 20px;
width: 120px;
background-color:gray;
}
<div class='contain'>
<input class="date-input" type="date" value="">
</div>
You are nesting your event listener attachment code. On every onclick you are adding a new blur event listener.
The below code adds the event listener for both elements only once.
let link = document.querySelector('.date-input')
let fatherClosest = document.querySelector('.contain')
fatherClosest.addEventListener("click", function (e) {
});
link.addEventListener("blur", (e) => {
console.log(e)
// a function that will created element on blur out form the date picker
fatherClosest.insertAdjacentHTML('afterbegin', '<div class="cla">hi</div>')
})
})
One more thing you could do is in your existing code before adding the 'blur' listener, remove all listeners of that type. Stack overflow link
I'm making a pizza ordering application using pure javascript. In the CART section, I have two click operations - increase /decrease the item quantity and remove the item from cart. I have two event listeners for these two operations. I want to make use of event delegation here.
The DOM structure looks like this. Here the class cart-items is the parent class of all the items that are dynamically added into the card. Every item present in the cart gets a div and the ID associated with the pizza item chosen is assigned as ID for that item. For example, I add the pizza item with ID 4 in my cart, so a <div> with id 4 is added in the DOM. :-
Here are the event listeners for the increase/decrease in quantity and remove item from cart.
//event listener attached when remove button is clicked on
let removeCartItemButtons = document.getElementsByClassName("btn-danger");
for (let i = 0; i < removeCartItemButtons.length; i++) {
let button = removeCartItemButtons[i];
button.addEventListener("click", removeCartItem);
}
//event listener attatched when quantity is increased or decreased
let quantityInputs = document.getElementsByClassName("cart-quantity-input");
for (let i = 0; i < quantityInputs.length; i++) {
let input = quantityInputs[i];
input.addEventListener("change", quantityChanged);
}
MY TAKE ON:
document
.getElementsByClassName("cart-items")
.addEventListener()
);
I want to make use of event delegation and don't want to create multiple event listeners. So, I'm fetching the cart-items which is basically the parent element here for the CART section.
Now, how do I add the two event listeners inside the above code.
Please help!
for(const el of document.querySelectorAll(".cart-items")){
el.addEventListener('click', function(e){
if(e.target.matches('.btn-danger')){
removeCartItem(e.target);
}
});
el.addEventListener('change', function(e){
if(e.target.matches('.cart-quantity-input')){
quantityChanged(e.target);
}
});
}
My goal is put javascript functions in loop:
select option from drop down
click on "Filter" button
download selected file
When i have it in two functions it works perfectly: - no loop, running manually (index from 0 until x)
function chooseOpt(x){
document.getElementsByClassName('span4 m-wrap dropdown combobox')[1].selectedIndex = x;
const links = Array.from(document.getElementsByClassName('btn green'));
links.forEach((link) => {
if (link.textContent === 'Filter') {
link.click();
}
})
}
function Dowload(){
var button = document.getElementsByClassName('btn icn-only black tooltips download_link')[0];
button.click();
}
But how can i put into loop?
i ve created one function from this two, but doesnt work:
function chooseOpt(){
for (x = 0; x<3;x++){
document.getElementsByClassName('span4 m-wrap dropdown combobox')[1].selectedIndex = x;
const links = Array.from(document.getElementsByClassName('btn green'));
var button = document.getElementsByClassName('btn icn-only black tooltips download_link')[0];
links.forEach((link) => {
if (link.textContent === 'Filter') {
link.click();
}
}
)
button.click();
}
}
chooseOpt();
Can you give me hint, link ? Thank you
Not seeing your HTML made it a bit challenging, but I contrived some markup that works with your script structure, then modified the script to successfully:
Loop through all options,
Simulate an associated link click for each option, and
Simulate a button click for each option to download an associated file
I encountered the same problem you did, and I was able to make it go away by adding a custom-event listener on the dropdown element. At least part of the issue for me was that programmatically changing the selectedIndex property of a select element does not trigger event listeners like calling the .click method on certain elements does. To make sure that the download function is called for each option, the code below employs a custom event (called programmatic-selection), which is triggered manually in the chooseOpt function.
Other things to note:
Each link has a data-file attribute that stores the path to the associated file. This value is copied to a global filepath variable when needed so the button can see which file to download.
Each link will be clicked if it has a class matching the current option's text content. (Your code selects links based on link.textContent === 'Filter', and this behavior is preserved: any link that matches the current option has its textContent property set to 'Filter' in order to meet your condition and thereby trigger a simulated click.)
// Declares global variables
const
dropdown = document.getElementsByClassName('dropdown')[1],
links = Array.from(document.getElementsByClassName('btn')),
button = document.getElementsByClassName('download-link')[0];
let filepath = '';
// Adds event listeners
dropdown.addEventListener('programmatic-selection', handleProgrammaticSelection);
document.addEventListener('click', handleLinkClick);
button.addEventListener('click', handleButtonClick);
// Main
chooseAll();
// Loops through options, selecting each one
function chooseAll(){
let i = -1;
while(++i < dropdown.length){
chooseOpt(i);
}
}
// Triggers custom event listener and the appropriate link
function chooseOpt(x){
dropdown.selectedIndex = x;
// The `change` event won't fire if we select an option
// programmatically, so we fire a custom event instead
dropdown.dispatchEvent(new CustomEvent('programmatic-selection'));
links.forEach((link) => {
if(link.textContent === 'Filter'){
// Simulates click on certain links, triggering listener
link.click();
}
});
}
// Listener for custom event -- sets link text
function handleProgrammaticSelection(event){
const dropdown = event.target;
links.forEach((link) => {
// `chooseOpt` relies on `textContent` property of `link` elements,
// so we set this before deciding which link to click
link.textContent = link.classList.contains(dropdown.value)
? 'Filter'
: 'Nope';
});
}
// (Listeners can automatically access their triggering events)
function handleLinkClick(event){
// An event has a `target` property
const clickedThing = event.target;
// Ignores irrelevant clicks
if(!clickedThing.classList.contains('btn')){ return; }
// Sets global `filepath` to match 'file' data-attribute of target
filepath = clickedThing.dataset.file;
// Calls `download`, which simulates button click
download();
}
// Called by above listener
function download(){
// Simulates click on `button`, triggering listener
button.click();
}
// Called by listener on `button`
function handleButtonClick(event){
// Accesses global `filepath` to pick file
console.log(`downloading ${filepath}...`);
}
<select class="dropdown">
<option>A</option>
<option>B</option>
</select><br/>
<select class="dropdown">
<option>AA</option>
<option>BB</option>
<option>CC</option>
</select><br/><br/>
<button data-file='some-file' class='btn AA'>Nope</button><br/>
<button data-file='some-other-file' class='btn BB'>Nope</button><br/>
<button data-file='another-file' class='btn CC'>Nope</button><br/><br/>
<button class='download-link'>Download</button>
Your question kind of doesn't make sense but, I think this is what you might want...
function chooseOpt(x){
document.getElementsByClassName('span4 m-wrap dropdown combobox')[1].selectedIndex = x;
const links = Array.from(document.getElementsByClassName('btn green'));
var button = document.getElementsByClassName('btn icn-only black tooltips download_link')[0];
links.forEach((link) => {
if (link.textContent === 'Filter') {
link.click();
}
});
button.click();
}
for(let x = 0; i < 3; x++){
chooseOpt(x);
}
I am quite new to manipulating elements in the DOM in JS so I am creating a simple to do list to get more comfortable and where I can add items using the input and remove items by clicking on the list item.
ALthough this may not be best practice and limitting I am just wanting to use create and remove elements rather than using objects or classes until I get more familar, also using plain/vanilla js so please keep this in mind when answering.
I am trying to add a click event which removes the <li> when the <li> is clicked.
My logic is...
When the page is loaded I can't just run a for loop over all of the <li>s and add event handlers as all of the <li>'s do not exist yet.
So my attempted solution is when the addTaskButton event is triggered, we get all of the <li> that are on the page at the time of the event, we loop through all of them and add an eventlistener to <li>'s that are waiting to be removed when clicked.
This doesn't seem to work and may be overly complicated.
Can someone please explan to me very simply like I'm 5 why this doesn't work or what a better way to do this would be?
Thank you in advance
HTML
<ul id="taskList">
<li>example</li>
</ul>
<input type="text" id="addTaskInput">
<button id="addTaskButton">Add Task</button>
JavaScript
const taskList = document.querySelector("#taskList");
const addTaskInput = document.querySelector("#addTaskInput");
const addTaskButton = document.querySelector("#addTaskButton");
let taskItem = document.querySelectorAll("li");
addTaskButton.addEventListener("click", () => {
let taskItem = document.createElement("li");
taskItem.textContent = addTaskInput.value;
for (let i = 0; i < taskItem.length; i++) {
taskItem[i].addEventListener("click", () => {
let taskItem = document.querySelectorAll("li");
taskList.removeChild(taskItem[i]);
});
}
taskList.appendChild(taskItem);
addTaskInput.value = " ";
});
Here is code i created for your requirement, this implement jQuery $(document).on mechanism in vanilla javascript, now where ever you create an li inside the document, on clicking that li it will be removed.
Explaination
What it does is on clicking the document it checks on which element is clicked (e.target is the clicked element, e is is the click event on document), then checks if the clicked item is an li tag (e.target.tagName will tell us the tag name if the item clicked), so if it is an li just remove it;
const taskList = document.querySelector("#taskList");
const addTaskInput = document.querySelector("#addTaskInput");
const addTaskButton = document.querySelector("#addTaskButton");
addTaskButton.addEventListener("click", () => {
let taskItem = document.createElement("li");
taskItem.textContent = addTaskInput.value;
taskList.appendChild(taskItem);
addTaskInput.value = " ";
});
document.onclick = function(e)
{
if(e.target.tagName == 'LI'){
e.target.remove();
}
}
<ul id="taskList">
<li>example</li>
</ul>
<input type="text" id="addTaskInput">
<button id="addTaskButton">Add Task</button>
Update your for loop like so:
for (let i = 0; i < taskItems.length; i++) {
taskItems[i].addEventListener("click", () =>
taskList.removeChild(taskItems[i]);
});
}
Also your initial taskItem variable should be taskItems and is reflected in the for loop above.
taskList.addEventListener("click", (event) => {
event.target.remove();
});
When the specified event occurs the event object is returned.
The event object has several properties, one of them being target which is the element which is the element which the event occured on. event.target is returned to us and we are applying the remove() method to event.target
because of event "bubbling" or "Event Propagation", we can attach the event handler to an ancestor. It's best to attach the event listener to the closest ancestor element that is always going to be in the DOM (won't be removed).
When an event is triggered-in this case the "click" event. All decending elements will be removed - which in our case as there are only <li>'s this would be fine. But we should be more specific as in a different case we could be attaching this event handler to a div which has several different elements.
To do this we add an if condition to check that the tagName is an <li>
if (event.target.tagName == "LI")
note that the element must be calpitalised
Solution is as follows
taskList.addEventListener("click", (event) => {
if(event.target.tagName == "LI"){
event.target.remove();
}});
Further reading:
Event object and its properties:
https://developer.mozilla.org/en-US/docs/Web/API/Event
Event Bubbling:
https://developer.mozilla.org/en-US/docs/Web/API/Event/bubbles
tagName:
https://developer.mozilla.org/en-US/docs/Web/API/Element/tagName
So I have EDIT and REMOVE buttons that are dynamically added for each data node (a "poll") in a Firebase database. I have a function which assigns onclick listeners to these with jQuery, but oddly, the event only fires when there just happens to be a single node, and hence a single pair of EDIT/REMOVE buttons. When there are multiple nodes and multiple pairs of buttons, none will fire. Here's the javascript where the events are added to the buttons...
function displayCurrentPollsForEditing(pollsRef)
{
var tbl = createTable();
var th = ('<th>Polls</th>');
$(th).attr('colspan', '3');
$(th).appendTo($(tbl).children('thead'));
pollsRef.once('value', function(pollsSnapshot) {
pollsSnapshot.forEach(function(pollsChild) {
var type = pollsChild.name();
// If this is true if means we have a poll node
if ($.trim(type) !== "NumPolls")
{
// Create variables
var pollRef = pollsRef.child(type);
var pollName = pollsChild.val().Name;
var btnEditPoll = $('<button>EDIT</button>');
var btnRemovePoll = $('<button>REMOVE</button>');
var tr = $('<tr></tr>');
var voterColumn = $('<td></td>');
var editColumn = $('<td></td>');
var rmvColumn = $('<td></td>');
// Append text and set attributes and listeners
$(voterColumn).text(pollName);
$(voterColumn).attr('width', '300px');
$(btnEditPoll).attr({
'class': 'formee-table-button',
'font-size': '1.0em'
});
$(btnRemovePoll).attr({
'class': 'formee-table-remove-button',
'font-size': '1.0em'
});
$(btnEditPoll).appendTo($(editColumn));
$(btnRemovePoll).appendTo($(rmvColumn));
// Append to row and row to table body
$(tr).append(voterColumn).append(editColumn).append(rmvColumn);
$(tr).appendTo($(tbl).children('tbody'));
// Append table to div to be displayed
$('div#divEditPoll fieldset#selectPoll div#appendPolls').empty();
$(tbl).appendTo('div#divEditPoll fieldset#selectPoll div#appendPolls');
$(btnEditPoll).click(function() {
displayPollEditOptions(pollRef);
return false;
});
$(btnRemovePoll).click(function() {
deletePoll($(this), pollsRef);
return false;
});
}
});
});
}
The markup would be something like the following...
<div id="divEditPoll">
<form class="formee" action="">
<fieldset id="selectPoll">
<legend>SELECT A POLL</legend>
<div class="formee-msg-success">
</div>
<div class="grid-12-12" id="appendPolls">
</div>
</fieldset>
</div>
EDIT - So I've switched some lines around and now I don't set the click() events until the buttons are appended to the document, so the button elements are definitely in the DOM when the click events are attached. So could this issue result from not setting id's for these buttons? That seems strange to me, since I'm using variable references rather than ids to attach the events.
There are two things I would check for.
First, make sure you don't have two elements with the same id. If you do, jquery may only bind to the first, or not bind at all.
Second, make sure the element is added to the dom before jquery attempts to bind the click event. If the code is running asynchronously, which can easily happen if you're using ajax, then you may be trying to bind the event before creating the element. Jquery would fail to find the element then give up silently.
you should use .on() for dynamically added button