Dynamic button Ids - Javascript - javascript

I have a number of buttons generated from a for loop as follows and the id = like:
<button type="submit" id = "like" class="btn btn-custom btn-sm like"><span id="tx-like">Like</span></button>
I have a script that changes the "like" to "unlike" and "unlike" to "like", it gets element by Id:
$( function() {
$('#like').click( function() {
var t = $("#tx-like").html();
if(t == 'Like'){
$("#tx-like").html("Unlike");
}else{
$("#tx-like").html("Like");
}
} );
} );
This is only functional on the first button since it taking the id. How I can get it functional on all buttons that are generated dynamically?

As said in comments above, you should not have multiple IDs in the same page.
You could use classes instead, but even if it would work, there is a better approach which is to use data-attributes.
// Retrieves all your elements using the `data-like` attribute.
const likes = document.querySelectorAll('[data-like]');
// For each element as `el`
likes.forEach(el => {
el.addEventListener('click', () => {
// Get its `data-like` value.
const { like } = el.dataset;
// If the value is 'false':
if (like === 'false') {
el.innerText = 'Dislike';
el.dataset.like = 'true';
return;
}
// Else...
el.innerText = 'Like';
el.dataset.like = 'false';
});
});
/* You can easily customize your elements according to their current state. */
.like[data-like="true"] {
border: solid 1px green;
}
.like[data-like="false"] {
border: solid 1px red;
}
<!-- Remove duplicate IDs and use `data-like` instead. -->
<button type="submit" class="btn btn-custom btn-sm like" data-like="false">Like</button>
<button type="submit" class="btn btn-custom btn-sm like" data-like="false">Like</button>

Related

Find and Click button with JS

I'm trying to use a chrome extension (shortkeys) to create shortcut keys that can press buttons within our warehouse management system (so they can be matched to barcodes).
One of the buttons has no ID, and once it has been clicked the button innertext changes. Ideally I'd like the shortcut to work on either version of the button
It is either
<input type="submit" value="Create Shipment" class="btn btn-success pull-right">
or
<a class="btn btn-success" href="/Order/OrderDocumentP/15467" target="_blank">Print Label</a>
I then have another button to be assigned to a different shortcut key
<a class="btn btn-success" href="/Picking/DespatchOrder?OrderId=13413">Despatch</a>
But I'm sure once I've figured out the first one the next will be easier :)
Any help greatly appreciated, I've been through a number of other questions that are similar but not quite what I'm after and my JS knowledge is pretty rubbish
Learn CSS a bit and use https://developer.mozilla.org/ru/docs/Web/API/Document/querySelector
Your extensions probaply supports that
// cuz I don't like to type long "document.querySelector"
q = sel => document.querySelector(sel)
qq = sel => document.querySelectorAll(sel)
function clickOnly(sel) {
let list = qq(sel);
if (list.length == 1) list[0].click();
else alert('element "'+sel+'" is not unique!');
}
// handles *any* keypress
onkeypress = function (event) {
if (event.target.tagName == "INPUT") return; // noop on input focused
if (event.target.tagName == "TEXTAREA") return; // noop on input focused
console.log(event.code); // to see what the key is
let rawCode = event.code; // keyboard key, `KeyM` for M, `Digit7` for 7, `Slash` for /
let code = rawCode; // make CtrlAltShiftKeyM
if (event.shiftKey) code = 'Shift' + code;
if (event.altKey) code = 'Alt' + code;
if (event.ctrlKey) code = 'Ctrl' + code;
if (!kds[event.code]) return;
event.preventDefault(); // prevent CtrlKeyM browser handler for bookmarks or whatever
kds[event.code](event);
}
kds = {}
// it's a function so starts with `() => `
kds.KeyM = () => alert('it works!')
// a is for <a>, [href^=] is for href starts with
kds.ShiftKeyM = () => clickOnly('a[href^="/Order/OrderDocumentP/]')
// , is for one of multiple selectors
kds.CtrlKeyM = () => clickOnly('input[value="Create Shipment"], a[href^="/Order/OrderDocumentP/]')
This is a simple script on getting a button by class name and clicking it. I think this is what you are looking for, if not let me know I will rewrite it.
EDIT: I added a loop that will click all buttons or links found with the class name btn-success
I've inserted a second function so people looking for a solution by classname can also still find the first one. AutoClickBtnByValue() will click the button with inner text "click me now".
function AutoClickBtn() {
var button = document.getElementsByClassName("btn-success");
for (var i = 0; i < button.length; i++) {
button[i].click();
console.log('Success! Clicked button' + i);
}
}
setInterval(AutoClickBtn, 2000);
/* Click button by innerHTML text */
function AutoClickBtnByValue() {
var button = document.getElementsByClassName("btn-success");
for (var i = 0; i < button.length; i++) {
if (button[i].innerHTML.indexOf('click me now') > -1) {
button[i].click();
console.log('Success! Clicked button' + i + ' with value: "click me now" ');
}
}
}
setInterval(AutoClickBtnByValue, 2000);
<input type="submit" value="Create Shipment" class="btn btn-success pull-right">
<a class="btn btn-success" href="#" target="_blank">Print Label</a>
<a class="btn btn-success" href="#">Despatch</a>
<button class="btn-success">click me now</button>

Each tasks added would have his own property

I have created a todo-apps with js but I have a problem : when I am clicking on the check button to do someting or on the edit button or the time button all tasks are changed : for example when I click on the check button on « learn js » I want that just this task changed ( underline) but when I do that all my tasks become underline. I know this is a beginner question sorry. This is my HTML code :
<h1>To Do List</h1>
<input type="text" placeholder="Name..." id="Name">
<input type="button" id="addItem" value="Add Item" />
<div class="choices">
<p id="p"></p>
</div>
<button id="btn" type="submit"> Clear Items</button>
This is my JS code :
let clear = document.getElementById("btn");
let add = document.getElementById("addItem");
let choices = [];
let vide = document.getElementById('p');
var choice = document.getElementById("Name").value;
let invalid = document.getElementById("invalid");
function main() {
add.addEventListener('click', function() {
addItems();
})
}
function addItems() {
choice = document.getElementById("Name").value;
vide.innerHTML += choice;
choices.push(choice);
document.getElementById('p').insertAdjacentHTML('beforeend', `<i id="check" class="far fa-check-circle"></i>`);
document.getElementById('p').insertAdjacentHTML( 'beforeend', `<i id="null" class="far fa-times-circle"></i>`);
document.getElementById('p').insertAdjacentHTML( 'beforeend', `<i. id="edit" class="far fa-edit"></i>`);
vide.insertAdjacentHTML('beforeend', `<br/><br/>`);
document.getElementById('p').classList.add('listClass');
document.getElementById('check').onclick = function() {
document.getElementById('p').classList.toggle("done");
document.getElementById('check').classList.toggle('opacity');
};
document.querySelector('#null').onclick = function() {
vide.innerHTML ='';
};
document.getElementById('edit').onclick = function() {
// I have not finished this part
}
}
}
main();
This is a picture of the result :
Depsite the fact that you have many mistakes(especially code redundancy) in your code, the main issue is that your IDs are not unique in the page.
As I said, the code is not that flexible to allow us building on it, so I took sometime to provide you a more modern, readable, performant solution (regarding yours of course !).
The code is commented to allow you understand easily what I'm doing.
// selecting the elements from the DOM
const todoList = document.getElementById("todo-list"),
newTodoForm = document.getElementById("new-todo-form"),
todoInp = document.getElementById("todo"),
clearBtn = document.getElementById("clear"),
choices = [],
/**
* createElement: a function that creates an HTML element with the specified attributes and events
* #param nodeName (string) the HTML element tag name
* #param opts (object) the attributes object which can contain :
* content: (object) an object to specify the element's content and it has two fields :
* html: (boolean) is the content should be inserted as HTML or a simple text. Defaults to false, pass true to treat the content as HTML
* value: (string) the actual content
* classList: (string) specifies a space-separated classes to be assigned to the element
* id: (string) the elemnt's ID
* data: (object) an object for the "data-TOKEN=VAL" attributes where each key (in camelCase) is the "TOKEN" and its value is the "VAL", example: {someDataOne: 'some value one', someDataTwo: 'some value two'} will be transformed into "data-some-data-one="some value one" data-some-data-two="some value two""
* events: (object) the keys are event names and the values are the events callbacks. Example {click: () => alert('clicked !')}
**/
createElement = (nodeName, opts) => {
// create the requested element
const el = document.createElement(nodeName);
// merge the options with the default ones
opts = Object.assign({
content: {
value: "",
html: !1
},
classList: "",
id: "",
data: {},
events: {}
},
opts
);
// apply the classes if the "opts.classList" is not empty
!!opts.classList && (el.classList = opts.classList);
// apply the ID if the "opts.id" is not empty
!!opts.id && (el.id = opts.id);
// apply the content if the "opts.content.value" is not empty and check if we want the content to be treated as HTML
!!opts.content.value &&
(el[opts.content.html === !0 ? "innerHTML" : "textContent"] =
opts.content.value);
// apply the data-* if the "opts.data" is not empty
if (Object.getOwnPropertyNames(opts.data).length) {
for (let p in opts.data)
if (opts.data.hasOwnProperty(p)) el.dataset[p] = opts.data[p];
}
// assign the events if the "opts.events" is not empty
if (Object.getOwnPropertyNames(opts.events).length) {
for (let p in opts.events)
if (opts.events.hasOwnProperty(p))
el.addEventListener(p, opts.events[p]);
}
// return the created element after applying the requested attributes and events
return el;
},
/*
* a function that generates a list-item template (HTML code that contains the todo text, buttons for edit, delete and so on...)
* #param txt (string) the todo text
*/
todoItemTpl = txt => {
// "item" is the list-item wrapper that contains all the buttons and todo text for only one todo item
// as you can see, we're using the "createElement" function so the code doesn't get redundant and also it become more readable and flexible
const item = createElement("div", {
classList: "todo-item row col-12 py-2 px-0 m-0 my-1 align-items-center"
}),
// the "p" element that shows the todo text
txtWrapper = createElement("p", {
content: {
value: txt
},
classList: "todo-text col-auto m-0"
}),
// a wrapper for the options (delete, edit and check) buttons of a todo item
btnsWrapper = createElement("div", {
classList: "todo-btns col-auto"
}),
// an array that holds the 3 option buttons so later we can loop through it and append each button to "btnsWrapper"
// every button here has its click event so the browser won't get confused which todo item should it alter
// !!: the edit functionality is not implemented it simply prints some text to the console when the edit button is clicked
optionBtns = [
createElement("button", {
content: {
value: '<i class="far fa-check-circle"></i>',
html: !0
},
classList: "option-btn check-btn ml-1",
events: {
click: function() {
this.closest('div.todo-item').classList.add('finished');
this.disabled = !0;
}
}
}),
createElement("button", {
content: {
value: '<i class="far fa-edit"></i>',
html: !0
},
classList: "option-btn edit-btn ml-1",
events: {
click: function() {
console.log('Edit functionnality not yet implemented !');
}
}
}),
createElement("button", {
content: {
value: '<i class="fa fa-times"></i>',
html: !0
},
classList: "option-btn del-btn ml-1",
events: {
click: function() {
const todoItem = this.closest('div.todo-item'),
txt = todoItem.querySelector('.todo-text').textContent;
todoItem.remove();
choices.splice(choices.indexOf(txt), 1);
}
}
})
];
// append the option buttons to the buttons wrapper
optionBtns.forEach((b) => btnsWrapper.appendChild(b));
// append the todo text to the todo-item
item.appendChild(txtWrapper);
// append the option buttons wrapper text to the todo-item
item.appendChild(btnsWrapper);
// return the newly created todo-item
return item;
};
// listen for the form submission
newTodoForm.addEventListener("submit", e => {
// store the trimmed input (the todo name) value
const inpVal = todoInp.value.trim();
// prevent form submission to disable page reload
e.preventDefault();
// stop execution if the todo item name is empty
if (!inpVal) return;
// if the todo text is not empty then :
// append the entered value to the "choices" array
choices.push(inpVal);
// append the todo-item to the todo list (that is initially empty) using "todoItemTpl" function and by passing the entered value for the todo name
todoList.appendChild(todoItemTpl(inpVal));
// finnaly, empty up the todo name input field
todoInp.value = "";
});
// listen for the click event of the clear button
// when clicked, remove all the todo-items and empty up the "choices" array
clearBtn.addEventListener('click', () => (todoList.innerHTML = '', choices.length = 0));
// styling for the demo, nothing fancy !
.todo-list * {
transition: all .4s 0s ease;
}
.todo-list {
background-color: #f5f5f5;
}
.todo-list .todo-item {
border-bottom: 1px solid #c5c5c5;
}
.todo-list .todo-item.finished {
background-color: #65d4a8;
}
.todo-list .todo-item.finished .todo-text {
text-decoration: line-through;
}
<!-- importing Bootsrap and Font-Awesome -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.1/css/all.min.css" rel="stylesheet" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.min.css" rel="stylesheet" />
<!-- some transformations to use Bootsrap's classes -->
<div class="todo-form-wrapper">
<form action="#" id="new-todo-form">
<h1>To Do List</h1>
<input type="text" placeholder="Name..." id="todo">
<button type="submit" value="Add Item">Add</button>
</form>
</div>
<!-- the todo list wrapper is initially empty -->
<div id="todo-list" class="todo-list row m-0"></div>
<button id="clear" type="button">Clear Items</button>
Eventhough the code looks good, I don't recommand using it in production as it has some issues, it doesn't support old browsers like IE for example.
I guess you are adding all the contents inside one single - p - tag and when you change the class- i.e: toggling the class of that - p - tag to "done", it is getting applied to all inner texts.
You need separate wrapper for each task assertion to handle this problem.

How to add working dropify inputs dynamically

I have form which gets clone when user click on add more button .
This is how my html looks:
<div class="col-xs-12 duplicateable-content">
<div class="item-block">
<button class="btn btn-danger btn-float btn-remove">
<i class="ti-close"></i>
</button>
<input type="file" id="drop" class="dropify" data-default-file="https://cdn.example.com/front2/assets/img/logo-default.png" name="sch_logo">
</div>
<button class="btn btn-primary btn-duplicator">Add experience</button>
...
</div>
This my jquery part :
$(function(){
$(".btn-duplicator").on("click", function(a) {
a.preventDefault();
var b = $(this).parent().siblings(".duplicateable-content"),
c = $("<div>").append(b.clone(true, true)).html();
$(c).insertBefore(b);
var d = b.prev(".duplicateable-content");
d.fadeIn(600).removeClass("duplicateable-content")
})
});
Now I want every time user clicks on add more button the id and class of the input type file should be changed into an unique, some may be thinking why I'm doing this, it I because dropify plugin doesn't work after being cloned, but when I gave it unique id and class it started working, here is what I've tried :
function randomString(len, an){
an = an&&an.toLowerCase();
var str="", i=0, min=an=="a"?10:0, max=an=="n"?10:62;
for(;i++<len;){
var r = Math.random()*(max-min)+min <<0;
str += String.fromCharCode(r+=r>9?r<36?55:61:48);
}
return str;
} var ptr = randomString(10, "a");
var className = $('#drop').attr('class');
var cd = $("#drop").removeClass(className).addClass(ptr);
Now after this here is how I initiate the plugin $('.' + ptr).dropify().
But because id is still same I'm not able to produce clone more than one.
How can I change the id and class everytime user click on it? is there a better way?
Working Fiddle.
Problem :
You're cloning a div that contain already initialized dropify input and that what create the conflict when you're trying to clone it and reinitilize it after clone for the second time.
Solution: Create a model div for the dropify div you want to clone without adding dropify class to prevent $('.dropify').dropify() from initialize the input then add class dropify during the clone.
Model div code :
<div class='hidden'>
<div class="col-xs-12 duplicateable-content model">
<div class="item-block">
<button class="btn btn-danger btn-float btn-remove">
X
</button>
<input type="file" data-default-file="http://www.misterbilingue.com/assets/uploads/fileserver/Company%20Register/game_logo_default_fix.png" name="sch_logo">
</div>
<button class="btn btn-primary btn-duplicator">Add experience</button>
</div>
</div>
JS code :
$('.dropify').dropify();
$("body").on("click",".btn-duplicator", clone_model);
$("body").on("click",".btn-remove", remove);
//Functions
function clone_model() {
var b = $(this).parent(".duplicateable-content"),
c = $(".model").clone(true, true);
c.removeClass('model');
c.find('input').addClass('dropify');
$(b).before(c);
$('.dropify').dropify();
}
function remove() {
$(this).closest('.duplicateable-content').remove();
}
Hope this helps.
Try this:
$(function() {
$(document).on("click", ".btn-duplicator", function(a) {
a.preventDefault();
var b = $(this).parent(".duplicateable-content"),
c = b.clone(true, true);
c.find(".dropify").removeClass('dropify').addClass('cropify')
.attr('id', b.find('[type="file"]')[0].id + $(".btn-duplicator").index(this)) //<here
$(c).insertBefore(b);
var d = b.prev(".duplicateable-content");
d.fadeIn(600).removeClass("duplicateable-content")
})
});
Fiddle
This does what you specified with an example different from yours:
<div id="template"><span>...</span></div>
<script>
function appendrow () {
html = $('#template').html();
var $last = $('.copy').last();
var lastId;
if($last.length > 0) {
lastId = parseInt($('.copy').last().prop('id').substr(3));
} else {
lastId = -1;
}
$copy = $(html);
$copy.prop('id', 'row' + (lastId + 1));
$copy.addClass('copy');
if(lastId < 0)
$copy.insertAfter('#template');
else
$copy.insertAfter("#row" + lastId);
}
appendrow();
appendrow();
appendrow();
</script>
Try adding one class to all dropify inputs (e.g. 'dropify'). Then you can set each elements ID to a genereted value using this:
inputToAdd.attr('id', 'dropify-input-' + $('.dropify').length );
Each time you add another button, $('.dropify').length will increase by 1 so you and up having a unique ID for every button.

jquery get value of each button

I'm trying to get the value of each button, but what i get is the value of the first button. This is my content
<div class="my_btn">
<button id="id_button" value="page-1">Page One</button>
<button id="id_button" value="page-2">Page Two</button>
<button id="id_button" value="page-3">Page Three</button>
<button id="id_button" value="page-4">Page Four</button>
</div>
and this is my script
jQuery('.my_btn').on('click',function(){
var my_content = jQuery('#id_button').val();
var my_link = '<li>Link</li>';
if( !tinyMCE.activeEditor || tinyMCE.activeEditor.isHidden()) {
jQuery('textarea#content').val(my_link);
} else {
tinyMCE.execCommand('mceInsertContent', false, my_link);
}
});
Basically this is a wordpress function. I'm trying to add different links inside the textarea box.
Thanks in advance
Apply the event listener to the buttons instead of the wrapper, then get the value with this.value.
jQuery('.my_btn button').on('click',function(){
var my_content = this.value;
var my_link = '<li>Link</li>';
if( !tinyMCE.activeEditor || tinyMCE.activeEditor.isHidden()) {
jQuery('textarea#content').val(my_link);
} else {
tinyMCE.execCommand('mceInsertContent', false, my_link);
}
});
Also, you should use unique values for each id attribute. While it won't effect this script, if you try to select by id then it will only affect the first element with that id.
jQuery('.my_btn button').on('click',function(){
var my_content = this.value;
alert(my_content);
/*var my_link = '<li>Link</li>';
if( !tinyMCE.activeEditor || tinyMCE.activeEditor.isHidden()) {
jQuery('textarea#content').val(my_link);
} else {
tinyMCE.execCommand('mceInsertContent', false, my_link);
}*/
});
<div class="my_btn">
<button id="id_button_1" value="page-1">Page One</button>
<button id="id_button_2" value="page-2">Page Two</button>
<button id="id_button_3" value="page-3">Page Three</button>
<button id="id_button_4" value="page-4">Page Four</button>
</div>
<!-- jQuery -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
first off id_button should be a class because it is on multiple elements
second your first jquery command should look like this, this way it will handle the button click and get the correct value
jQuery('.my_btn').on('click', '.id_button',function(){
var my_content = $(this).val();
var my_link = '<li>Link</li>';
if( !tinyMCE.activeEditor || tinyMCE.activeEditor.isHidden()) {
jQuery('textarea#content').val(my_link);
} else {
tinyMCE.execCommand('mceInsertContent', false, my_link);
}
});

JQuery Button Data Returning As Null?

I have a button and when I click it, I want the html object (aka button) to be passed as a parameter to another javascript function. I want the javascript function to print the data-hi from the element in the button.
HTML BUTTON
<button type = "button" onclick = "whoIsRdns(this)" class="dns-information btn btn-xs btn-info pull-right" data-toggle="modal" data-target = "#whois_rdns_modal" data-path="{{ path( '_who_is_rdns', { 'peer': peer.number, 'ip': peer.mac } ) }}" data-hi = "hi2">
<i class="icon-search"></i>
</button>
JS FUNCTION(W/ JQUERY)
function whoIsRdns(thisButton){
//Enable jQuery properties from the param of the HTML object
var btn = $(thisButton);
var test = btn.data('hi');
console.log('Value is ' + test);
}
Why would test return as null?
Shouldn't var btn = $("thisButton"); be var btn = $(thisButton); (without quotes)
Just a typo
$("thisButton") !== $(thisButton);
drop the quotes so you are not looking for an element with a tag name thisButton
var btn = $("thisButton");
needs to be
var btn = $(thisButton);

Categories

Resources