<?php foreach ($communities as $community) { ?> <div class="community-container"> <p><?php echo $community->Title ?></p> <button class="del-btn" data-id="<?php echo $community->ID ?>">Delete</button> </div> <?php } ?> <Script> const deleteBtns = document.getElementsByClassName('del-btn'); var deleteBtnsArray = Array.from(deleteBtns); deleteBtnsArray.map(deleteBtn => { deleteBtn.addEventListener('click', () => { const delRequest = new XMLHttpRequest(); let params = []; params = `deleteCommunity=true&communityID=${deleteBtn.dataset.id}`; delRequest.open('POST', '/ajax/delete-community'); delRequest.onreadystatechange = function() { if (delRequest.responseText === 'success') { deleteBtn.parentElement.remove(); } } delRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); delRequest.send(params); }) }) </Script>
I have communities list on my page and each community item has a delete button. Now when I click on a delete button I want to delete related community item.
But…
I can inspect these delete buttons from the browser and change it’s data-id
value to something else(if a data-id
of a delete button is 10, I can change it to 1000). Then when I click on that delete button after changing its data-id
value from the client side, it will delete some other community instead correct one. (If a community exists with that changed data-id
value) because buttons data-id
is changed and JavaScript
code takes that value to make AJAX
request. I can’t stop user changing data-id
from the client-side. Therefore, How can I handle this situation if user changed data attributes from client side?
Extra information
$communities
is a array of community objects and each community item has a Name and ID.
Advertisement
Answer
You could read the data-
prop(s) after the page loads. Store them in an array or whatever and then delete the data-
prop(s) from the elements.
Be aware that validation should probably happen somewhere on the server side instead of client side
// Wrap everything in an IIFE so we don't create global variables (() => { const ID_MAP = new WeakMap(); const onClickAction = ({ currentTarget }) => { // Exit if there is no ID stored if(!ID_MAP.has(currentTarget)) return; // Retrieve and log ID const id = ID_MAP.get(currentTarget); console.log(id); } const btns = document.querySelectorAll('button'); for(const btn of btns) { // Skip if it doesn't have an ID if(!btn.dataset.id) continue; // Store and hide `data-id` attribute ID_MAP.set(btn, btn.dataset.id); btn.removeAttribute('data-id'); // Add event listener btn.addEventListener('click', onClickAction, false); } })();
<button data-id="001">id: 001</button> <button data-id="002">id: 002</button> <button data-id="003">id: 003</button> <button data-id="004">id: 004</button> <button data-id="005">id: 005</button>
EDIT: using suggestions from comments (event delegation)
// Wrap everything in an IIFE so we don't create global variables (() => { const ID_MAP = new WeakMap(); const onClickAction = ({ target }) => { // Exit if it's not a button if(target.nodeName !== 'BUTTON') return; // Exit if there is no ID stored if(!ID_MAP.has(target)) return; // Retrieve and log ID const id = ID_MAP.get(target); console.log(id); } const btns = document.querySelectorAll('button'); for(const btn of btns) { // Skip if it doesn't have an ID if(!btn.dataset.id) continue; // Store and hide `data-id` attribute ID_MAP.set(btn, btn.dataset.id); btn.removeAttribute('data-id'); } // Add event listener, instead of `document` you can also use a common parent container document.addEventListener('click', onClickAction, false); })();
<button data-id="001">id: 001</button> <button data-id="002">id: 002</button> <button data-id="003">id: 003</button> <button data-id="004">id: 004</button> <button data-id="005">id: 005</button>