I am making a quick throw-together website that requires users to be able to interact with a button to execute a delete action.
I have my button (code is shown below) where I have a basic bootstrap button paired with an icon (provided by their library) in which the user clicks to delete an "Infraction"
<button
referencedInfraction="<%= i.infractionID %>"
targetUserID="<%= i.userID %>"
class="deleteButton btn btn-danger"
>
<i class="bi bi-trash-fill"></i>
</button>
The infraction that the user wants to delete is passed through an attribute to the code below.
$(".deleteButton").click((event) => {
$("#confirmDeleteSingleInfractionModal").modal("toggle");
let data = {
infractionID: $(event.target).attr("referencedInfraction"),
targetUserID: $(event.target).attr("targetUserID"),
};
console.log(data);
$("#confirmDeleteSingleInfractionModal").on("hide.bs.modal", function () {
console.log("deleting");
$.ajax({
type: "delete",
url: `${window.location.origin}/dataLink/infraction`,
data,
success: () => {
console.log("done");
},
error: () => {
showInfractionDeleteError();
},
});
});
});
However, I've run into a bit of an issue. I notice that the only way for the user to actually click the button is if they click around the edges where the <I> element is not present.
Is there any way to prevent this from showing in event.target()?
All I want is the custom attribute from the button clicked, not the image.
Any help would be appreciated.
If you set pointer-events: none on the element with the icon that should prevent the click event from firing on it and instead use the button, allowing you to get the data attributes. Example:
.bi-trash-fill {
pointer-events: none
}
Or whatever element/class makes sense for your application. This allows the user to click anywhere on the button (including on the icon) and the event will fire getting the attributes from the button.
You can check if the element which is been clicked is i tag or button and depending on this change your selector to get required data.
Demo Code :
$('.deleteButton').click((event) => {
console.log(event.target.tagName.toLowerCase())
//get tag which is clicked change selector if needed
var target = event.target.tagName.toLowerCase() === 'button' ? $(event.target) : $(event.target).parent()
//$('#confirmDeleteSingleInfractionModal').modal('toggle');
let data = {
infractionID: target.attr('referencedInfraction'),
targetUserID: target.attr('targetUserID')
}
console.log(data);
//other codes..
});
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.1.0/css/all.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.1.0/css/v4-shims.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button referencedInfraction="1" targetUserID="12" class="deleteButton btn btn-danger"> <i class="fas fa-heart"></i> </button>
<button referencedInfraction="2" targetUserID="13" class="deleteButton btn btn-danger"><i class="fas fa-heart"></i> </button>
The best way to work around this is to add a function to the icon <i></i> that is called once it is clicked and inside this function you can call the same function called by clicking on the button <button></button> itself. Also you can pass the same data from the icon element <i></i> to its function to be used in the button function.
note: you don't want to prevent clicking on the icon because it will be a bad UX (user experience) but actually you want to use it for the right purpose.
Related
please I am trying to create a FAQ like functionality, I have some elements hidden so when I click on a button it opens and hides it. I have been able to do this but I am not getting what I actually want. I might have done something wrong I suppose. So, there are 5 elements with the same className, this will help me target them all and run a for loop to kind of break them apart. However if I click on this button to open one of the element the other ones open.
const openBtn = document.querySelectorAll(".openBtn")
const openContent = document.querySelectorAll(".openContent")
for(btn of openBtn) {
btn.addEventListener('click', () => {
for(content of openContent) {
if (content.classList.contains('hidden')) {
content.classList.remove('hidden');
content.classList.add('flex')
} else {
content.classList.remove('flex');
content.classList.add('hidden')
}
}
})
}
So as you can see, If I click on the chevron icon for just one of the wither About Us, Careers or just any of the 5 every other one opens. How do I fix this ?
Since you aren't going to post even the most general version of your HTML, here is a general outline.
First, each button gets a data attribute for target,then each FAQ div gets an ID attribute that matches the data target attribute.
I attach the click handler to the document and look for openBTN on the clicked element. Then I loop through every OPENED div to close it. Then I get the target data attribute and add the appropriate classes.
document.addEventListener("click", function(e) {
if (e.target.classList.toString().includes("openBtn")) {
let opened = document.querySelectorAll(".openContent.flex");
opened.forEach(function(el) {
el.classList.add("hidden");
el.classList.remove("flex");
});
let target = document.querySelector(e.target.dataset.target)
target.classList.remove("hidden");
target.classList.add("flex");
}
});
.hidden {
display: none
}
<button data-target="#faq1" class="openBtn">OPEN</button>
<div id="faq1" class="openContent hidden">1</div>
<button data-target="#faq2" class="openBtn">OPEN</button>
<div id="faq2" class="openContent hidden">2</div>
<button data-target="#faq3" class="openBtn">OPEN</button>
<div id="faq3" class="openContent hidden">3</div>
<button data-target="#faq4" class="openBtn">OPEN</button>
<div id="faq4" class="openContent hidden">4</div>
I am working on a TODO app and I am having issues with the event listeners triggering on all of the elements with the same class.
Here is what I have so far:
"Add New Card" btn on page load, when the user clicks a dynamically generated list gets appended to the page.
The list is wrapped in <div>, containing input and "Add" btn to dynamically add list items to the list.
Roadblock:
When the user click on the "Add" btn from the dynamically generated list, it adds list items to all lists.
What I've tried:
I found solutions for triggering the 'click' on the "Add" btn only on the e.target. This doesn't work in my situation as I am not clicking on the element that needs to be added, I am clicking on the button that should add the content from the input field.
I tried this inside the function configuring the 'click' but it was unsuccessful.
e.stopPropagation();
e.stopImmediatePropagation();
I have created a codepen here where you can run the existing code: https://codepen.io/skarmen/pen/qBZYZJQ
I would appreciate any guidance and help here.
Thank you!
1 - Save the button add you clicked:
const add=$(e.target);
2 - pass it into addTask function along side input
addTask(userInput, add)
3 - Now use that button to find first list. So inf parent form, then immediately following sibling ol
$(add).parent().next("ol").append(
4 - You are generating same ids when you generate taskCardContainerthat wont work, use classes:
id="to-do-list" and id="clear-btn" needs to be: class="to-do-list" and class="clear-btn", ids needs to be unique
$(document).ready(function(event) {
/* SUBMIT FUNCTION
- listen to a click event on submit & prevent the default behaviour of the submit event
- validate the userInput and add it to the list by calling (addTask f)
*/
function configureSubmitBehaviour() {
$('.add-new-task-btn').on('click', function(e) {
e.preventDefault()
const add=$(e.target);
const $eventTargetPreviousEl = $(e.target).prev() // e target = btn, so we are looking for the input field before the add task btn
//console.log('e target:', e.target, 'this:', $(this))
//console.log('evenTargetPreviousEl:', $eventTargetPreviousEl)
// store userInput in a variable
let userInput = $($eventTargetPreviousEl).val().trim()
//console.log('userInput:', userInput)
// check if the input is valid
if (userInput !== '') {
addTask(userInput, add)
} else {
alert('Input cannot be empty. Please enter a valid task.')
}
})
}
/* ADD NEW CARD FUNCTION */
function configureAddCardBehaviour() {
$('#add-new-card-btn').on('click', function(e) {
e.preventDefault()
// append the task card container on btn click
addCard()
configureSubmitBehaviour()
})
}
function addCard() {
let $taskCardContainer = $(`
<div class="task-card-container">
<h2 class="editable"></h2>
<!-- Input New Task -->
<form>
<label for="new-task" class="sr-only">New Task</label>
<input class="new-task" type="text" placeholder="New Task" name="new-task"/>
<button type="submit" class="btn add-new-task-btn">Add</button>
</form>
<!-- Task List -->
<ol class="to-do-list" class="to-do-list sortable">
<!-- To do items added dynamically here -->
</ol>
<button class="clear-btn" class="btn clear-list-btn">Clear</button>
</div>
<!-- Task Board Container ENDS -->
`)
$('.main').append($taskCardContainer)
//console.log('addList works')
}
/* ADD TASK FUNCTION
- add the user input to the list
- clear the input field
- function is called upon submit
*/
function addTask(userInput, add) {
let removeItem = '<button id="remove">x</button>'
let checkbox = '<input type="checkbox">'
// append the added element from the list
$(add).parent().next("ol").append(`<li>${checkbox} <span data-id="editable-list-item">${userInput}</span> ${removeItem}</li>`);
$('.new-task').val('')
}
configureAddCardBehaviour()
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/jeditable.js/2.0.17/jquery.jeditable.min.js'></script>
<!-- jQuery UI -->
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<div class="wrapper">
<div class="main">
<h2>Goals List</h2>
<p><em>App description and instructions go here</em></p>
<button type="submit" id="add-new-card-btn" class="btn">Add New Task Card</button>
</div>
<!-- Main Container ENDS -->
</div>
<!-- Wrapper ENDS -->
EDIT:
Just noticed. You are doing something wrong from start, you have a loop somewhere. All this functions call each other with click events inside them is messed up.
Now when you have multiple cards, when adding item to list that is not the last one, it will add a list in right place but also loop all next inputs and issue an alert if empty. Pretty sure this is not intentional.
I'm using bootstrap 3 and dynamically creating buttons with a elements of the type
<a class="btn btn-primary" data-id="1" data-toggle="modal" data-target=".ver-persona">
<i class="fas fa-search"></i> Ver
</a>
My script to generate the button is:
let boton_elem = $('<a>');
boton_elem.addClass('btn');
for(let clase_btn in botones[boton].clases){
boton_elem.addClass(botones[boton].clases[clase_btn]);
}
for(let data_btn in botones[boton].datas){
boton_elem.data(botones[boton].datas[data_btn][0],botones[boton].datas[data_btn][1]);
}
if(botones[boton].icon !== undefined){
let icon_elem = $('<i>');
for(let clase_icon in botones[boton].icon.clases){
icon_elem.addClass(botones[boton].icon.clases[clase_icon]);
}
icon_elem.appendTo(boton_elem);
}
if(botones[boton].valor.length > 0){
boton_elem.html(boton_elem.html() + ' '+ botones[boton].valor);
}
if(botones[boton].listens !== undefined){
for(let listen in botones[boton].listens){
$('#'+tabla).on(botones[boton].listens[listen].evento,botones[boton].listens[listen].botones[boton].listens[listen].funcion);
}
}
boton_elem.appendTo(columna_elem);
The buttons are created perfect, the data is in it, and all parameters are applied correctly, but the .ver-persona modal not open with the generated button, all of buttons loaded with the page works perfectly and the modal opens normally.
Exist any way to refresh or reassing bootstrap listeners when a button is created? or some function to get button advised to open the modal?
I make this fiddle for reference: Live example
Thanks in advance.
For dynamic content, you can add data attributes using .attr of jQuery. So instead of boton_elem.data(boton.datas[data_btn][0],boton.datas[data_btn][1]); you can re-write it as boton_elem.attr("data-"+boton.datas[data_btn][0],boton.datas[data_btn][1]);
You can test it here
I have a bootstrap button with a font-awesome icon on it.
<button type="button" class="btn pull-right rotate-picture-right" aria-label="Rotate" id="picture{{ picture.pk }}">
<i class="fa fa-repeat" id="testright" aria-hidden="true"></i>
</button>
My on-click event:
$(document).on('click', '.rotate-picture-right', function (e) {
rotate_and_reload(e, "right");
});
The function:
function rotate_and_reload(e, direction){
var picture_id = e.target.id.replace("picture", "")
var url = "{% url "device-api-picture-rotate" device.pk 0 "placeholder" %}".replace("pictures/0", "pictures/"+picture_id).replace("placeholder", direction)
$.ajax({
url: url,
type: 'PATCH',
success: function() {
var d = new Date();
img = e.target.parentElement.parentElement.getElementsByTagName('img')[0];
if (img.src.includes("?")){
img.src = img.src.split("?")[0] + '?' + d.getTime();
} else {
img.src = img.src + '?' + d.getTime();
}
},
});
}
In Firefox, it doesn't make any difference whether I click on the icon or on the button, the on click event is triggered for the button.
In Chrome however, it is also possible to click on the icon and as I am using several properties of the buttons data, my function fails if I only get the icons information.
Is there a way to "disable" the icon and only make it be there and be pretty but not be able to trigger anything?
You can use CSS to disable pointer events on the icon.
<button type="button" class="btn pull-right rotate-picture-right" aria-label="Rotate" id="picture{{ picture.pk }}">
<i class="fa fa-repeat" style="pointer-events:none" id="testright" aria-hidden="true"></i>
</button>
However, a better way to handle this would be to ensure that the event.target is the same as the event.currentTarget in your click event handler. You can always get the reference of the button and not the icon by looking at the event.currentTarget property. The property name might be different based on whether you are using vanilla JS or a framework like jQuery.
Sample:
$('button').click(function(e) {
console.log(e.currentTarget); // This will be the button even if you click the icon
})
Handy reference for different Event Targets
enter image description hereI specified the button class in CSS selector and right click executed.
Then options displayed on the button, so it assure that the correct element position has been acquired.
javascript
.then(function () {
var element = driver.findElement(By.className('xxx'))
return element;
})
.then(function (element) {
var action = new webdriver.ActionSequence(driver)
action.click(element, webdriver.Button.RIGHT).perform()
})
Problem arises when just click is executed, instead of right click.
.then(function (element) {
var action = new webdriver.ActionSequence(driver)
action.click(element).perform()
})
I hope the event when the button is pressed to be executed, but it doesn't. I cannot find any error message. I really don't know why...
html
<button class="btn btn-default btn-play">
<span class="glyphicon glyphicon-play" aria-hidden="true">
</span>
" play "
</button>
I tried to select each class and result was same.
button before
button after
Since the aria-hidden="true" you can achieve this by directly clicking using javascript. :)
driver.executeScript("document.getElementsByClassName('btn btn-default btn-play')[0].click()")