Good morning! I am trying to create a function to generate a set of buttons on a webpage using jQuery. My sample code is below. The buttons are created (yay) but the parameters for value, onclick etc. are not used (boo). Please can someone with better skills that me show me the error in my code? Thanks so much for your help! I am beating my head against a wall here...
var buttonBar = document.getElementById("ButtonBar");
function genButton (butName, butLabel, action) {
$('#buttonBar').append
('<input type="button"
name=butName
value = butLabel
onclick = action>');
}
genButton('butAsIs', 'Buy As Is', 'funAsIs()');
function funAsIs() {
window.alert("Buy Me Pressed"); //TestCode 001
}
With your code you append just strings as parameters, not your variables.
For correct work, you may write
$('#buttonBar').append
('<input type="button" name="' + butName + '" value ="' + butLabel + '" onclick="' + action + '">');
Or use es6 syntax with ${expressions}
The butName, butLabel and Action are taken as string and not as interpolated variables inside your code. Here is a working examaple of your code :
https://jsfiddle.net/qtjtbsz6/
To create new input you should use document.createElement("input");.
// Let's create an input
var element = document.createElement("input");
Assigning new attributes are really easy in this way
element.type = 'button';
element.value = butLabel;
element.name = butName;
Adding onclick event is a bit tricky. We need to wrap it inside function then we can invoke it.
element.onclick = function() {
action(actionParams);
};
To generate new button we will use:
function generateButton(butName, butLabel, action, actionParams) { ... }
we can call this function for example like this:
generateButton('yourName', 'yourLabel', popAlert, { alertText: 'hello!', randomStuff: 'random' })
action is a function name that we pass as an argument. Remember now to add () - important part here.
actionParams are our action arguments. In our case it is just javascript object { alertText: 'hello!', randomStuff: 'random' }, you can pass anything you want.
Finally we append new input to our #result selector.
Here is working example. Run code snippet to see how it works.
function generateButton(butName, butLabel, action, actionParams) {
// Let's create an input
var element = document.createElement("input");
// Now we assign
element.type = 'button';
element.value = butLabel;
element.name = butName;
element.onclick = function() {
action(actionParams);
};
// Now we append
var result = document.getElementById("result");
result.appendChild(element);
}
function popAlert(params) {
alert(params.alertText);
console.log(params.randomStuff);
}
#result {
padding: 20px;
border: 1px solid #eee;
}
input[type="button"] {
margin: 2px;
}
<input type="button" value="click me to generate new button!" onclick="generateButton('yourName', 'yourLabel', popAlert, { alertText: 'hello!', randomStuff: 'random' })" />
<div id="result"></div>
Related
I am new to javascript/jquery and have been stuck on this problem for a while. So I have two buttons, a clear button that will clear all forms in a row of a table and a reset button that holds all initial values for each row of the table.
The issue: So currently when I run the script the reset button will keep overriding the clear button. Meaning when I click on clear it will also act as a reset instead of clearing the row. I tried creating unique classes (.clear_button, .reset_button) to be called as you see here. I find it hard to troubleshoot javascript especially being new to it so why is this happening?
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
$(document).ready(function(){
$(".clear_button").click(function(){
function my_clearFunction(i) {
document.getElementById("id_form-" + (i - 1) + "-Name").value = " ";
document.getElementById("id_form-" + (i - 1) + "-Start").value = "";
document.getElementById("id_form-" + (i - 1) + "-End").value = "";
document.getElementById("id_form-" + (i - 1) + "-Advanced").value = " ";
}
});
$(".reset_button").ready(function(){
$('#reset :input:not([type="button"])').each(function(idx, ele) {
ele.dataset.initvalue = ele.value;
});
$('#reset [type="button"]').on('click', function(e) {
// reset current row............
$(this).closest('tr').find(':input:not([type="button"])').each(function(idx, ele) {
// restore the initial value
ele.value = ele.dataset.initvalue;
})
});
});
});
</script>
Note: I understand the code is not uniform, for example, my clear button logic was not written in jquery. Sorry I couldn't attach a jsfiddle, this project is relatively pretty big and I use django to import my forms so it was difficult to set up. So any input would be greatly appreciated since I have been stuck on this for quite some time and can't seem to get it. It's also worth mentioning my input tags for the buttons so here they are.
<input type="button" class="clear_button" onclick="my_clearFunction({{ forloop.counter }})" value=" x ">
<input type="button" class="reset_button" value=" x ">
when I click on clear it will also act as a reset instead of clearing the row.
Your reset listener is declared as
$('#reset [type="button"]').on('click', function(e) {
...
})
It seems the #reset element contains both clear and reset buttons, so clicking in either will restore the initial values.
The clear button, also, has two handlers of its own. There is one declared in code which in turn declares a function (that isn't called in the handler itself) and an inline handler that tries to invoke said function. That shouldn't work, for it isn't visible from the global scope.
Instead of
$(button).on('click',(e)=>{
function doSomethingWith(i) {
...
}
doSomethingWith(e.target.id);
})
If should be
function doSomethingWith(i) {
...
}
$(document).ready(function(){
$(button).on('click',(e)=>{
doSomethingWith(e.target.id);
});
});
then it would be visible for the handler but also on the global scope, so you could call it using the inline "onclick"
<button onclick="doSomethingWith({{ forloop.counter }})" >
However, you shouldn't have an inline handler if you're also declaring one in JS. Since you're dealing with the reset button in code, stick to that approach for the clear button too.
Now, the approach you follow to clear a row needs for you to know the row relative index, and the inputs on each row, for which you compute their respective ids. Whereas, when it comes to reset the original values, you don't need to know anything:
$('.reset_button').on('click', function(e) {
// reset current row............
$(this).closest('tr').find(':input:not([type="button"])').each(function(idx, ele) {
// restore the initial value
ele.value = ele.dataset.initvalue;
})
});
The button needs only know it's inside the same <tr> element as other inputs whose value needs to be restored. It doesn't care about the index, the IDs, not even what inputs are in place, as long as they aren't buttons.
You should do the same to clear the values:
$('.clear_button').on('click', function(e) {
// reset current row............
$(this).closest('tr').find(':input:not([type="button"])').each(function(idx, ele) {
ele.value = "";
});
});
When it comes to storing the original value I'm also used to resort to jQuery.data . Anyway, for this use case you can perfectly stick to
input.dataset.initialValue = input.value
Instead of
$(input).data('initialValue',input.value)
As long as you keep in mind these approaches are not interchangeable. You can't set the initialValue with dataset then get it with jQuery.data or the other way around.
function randomTime() {
return [
Number(100 * Math.random() % 12).toFixed(0).padStart(2, '0'),
Number(100 * Math.random() % 60).toFixed(0).padStart(2, '0')
].join(':');
}
function addFormRow(player_name = 'N/A') {
let tr = $('<tr class="form_row">'),
name = $('<input type="text" name="name" class="name">'),
start = $('<input type="time" name="start" class="start">'),
end = $('<input type="time" name="end" class="end">'),
advanced = $('<input type="number" name="advanced" class="advanced">'),
clear = $('<button class="clear_button">Clear</button>'),
reset = $('<button class="reset_button">Reset</button>');
name.val(player_name);
start.val(randomTime());
advanced.val(parseInt(Math.random() * 100, 10))
end.val(randomTime());
for (let input of [name, start, end, advanced, clear, reset]) {
$('<td>').append(input).appendTo(tr);
}
tr.appendTo('#forms tbody');
}
addFormRow('player one');
addFormRow('player two');
addFormRow('player three');
$(document).ready(function() {
$('#forms tbody tr').each((index,tr)=>{
$(tr).find('input').each((idx,input)=>{
$(input).data('initialValue',$(input).val());
});
})
$(".clear_button").on('click', (e) => {
let $this = $(e.target),
tr = $this.closest('tr');
tr.find('input').each((index, input) => {
input.value = '';
});
});
$(".reset_button").on('click', (e) => {
let $this = $(e.target),
tr = $this.closest('tr');
tr.find('input').each((index, input) => {
$(input).val($(input).data('initialValue'));
});
});
});
.advanced {
width: 4em;
}
.name {
width: 9em;
}
.start,
.end {
width: 5.5em;
}
.form_row input {
height: 1.1em;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id="forms">
<thead>
<tr>
<th>name</th>
<th>start</th>
<th>end</th>
<th>advance</th>
<th colspan="2">actions</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
Your $(".clear_button").click() handler isn't doing anything. It defines a local function, but never calls it.
Instead of trying to call the function from onclick(), add a data attribute to buttons holding the index. Then the click handler can get this attribute and use it to find all the related elements that it needs to clear.
$(".clear_button").click(function() {
var i = $(this).data("rel-id");
document.getElementById("id_form-" + (i - 1) + "-Name").value = " ";
document.getElementById("id_form-" + (i - 1) + "-Start").value = "";
document.getElementById("id_form-" + (i - 1) + "-End").value = "";
document.getElementById("id_form-" + (i - 1) + "-Advanced").value = " ";
});
<input type="button" class="clear_button" data-rel-id="{{ forloop.counter }}" value=" x ">
I repeat .last().text() a lot (see below). Is there a way I can do this more efficiently so my code doesn't look so wordy? Is it possible to store .last().text() in a variable? I'm a newbie (obvs).
$("div").last().text(`${date} `).append("<span class = 'username'></span><span class = 'text'></span>");
$(".username").last().text(`${message.username}`);
$(".text").last().text(` ${message.text}`);
Put it in a function
const setTextToLastOf = (selector, text) => {
return $(selector).last().text(text);
}
Then use it
const appendText = "<span class = 'username'></span><span class = 'text'></span>";
setTextToLastOf("div", `${date} `).append(appendText);
setTextToLastOf(".username", `${message.username}`);
setTextToLastOf(".text", ` ${message.text}`);
you can create your own jQuery function.
In the below snippet you can see how to define a lastText() function for jQuery.
Updated the answer so you can set the last text too.
If you set a text the jQuery object (this) is returned so you can keep chaining functions on it. If you get the text the function returns a string with the text of the last element.
jQuery.fn.lastText = function(text) {
if(text == null) {
return this.last().text();
}
else {
return this.last().text(text);
}
};
$(document).ready(function(){
var currLastText = $('.par').lastText();
console.log("current last text: " + currLastText );
var newLastText = $('.par').lastText("Last").lastText();
console.log("New last text: " + newLastText);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p class="par">First</p>
<p class="par">Second</p>
One way to make it less messy is to use the :last selector:
$("div:last").text(`${date} `).append("<span class = 'username'></span><span class = 'text'></span>");
$(".username:last").text(`${message.username}`);
$(".text:last").text(` ${message.text}`);
I am creating a form dynamically and therefore edit the form elementsโ properties. When attempting to change the label, assigning an auto-generated id works fine but when changing this label using the generated id, the function or keyup() from jQuery keeps calling all the previously created label id(s). this means when i want to edit one label, it ends up editing every label.
HTML
<input type="text" id="change-label"><br><br>
<button id="add-button">add label</button>
<div id="add-label"></div>
JavaScript/jQuery
$('#add-button').click(function(){
var div = document.createElement('div');
var textLabel = document.createElement('label');
var labelNode = document.createTextNode('untitled');
textLabel.appendChild(labelNode);
textLabel.id = autoIdClosure();
$('#change-label').val('untitled');
div.appendChild(textLabel);
$('#add-label').append(div);
});
var autoIdClosure = (function(){
var counter = 0;
var labelId = "textInputLabel";
return function(){
counter += 1;
var id = labelId + counter;
editLabelWrapper(id)
return id;
}
})();
function editLabelWrapper(id){
function editLabel(){
var value = $(this).val();
$("#"+id).text(value);
}
$("#change-label").keyup(editLabel).keyup();
}
Iโve already found an alternative using onkeyup="$('#'+globaID).text($(this).val());", but I need to understand what I was doing wrong so I can learn from it.
JSFiddle
I think you are overthinking the matter...
Instead of using an unique id, rather use classes, makes it easier to handle.
So change <div id="add-label"></div> to <div class="add-label"></div>
Then what you want to do is, when a value is given in #change-label you want it in the last div.add-label.
So the function will become this:
$("#change-label").on('keyup', function() {
$('.add-label:last').text( $(this).val() );
});
Next what you want to do is bind a function to #add-button. Once it gets clicked, we want to add a new div.add-label after the last one. And empty the #change-label. You can do that by using this function:
$('#add-button').on('click', function() {
$('.add-label:last').after('<div class="add-label"></div>');
$('#change-label').val('');
});
Updated Fiddle
Iโd like to add a button to certain text fields to allow for additional input methods. Since the button should be able to reference the text field it belongs to, I'm adding a parameter to the function call within the buttonโs onClick() handler, containing the ID of the text field.
At least, this is my plan. When I obtain the ID of the text field, and display it in an alert, it displays nicely. However, when I use the result of $(this).attr('id') as a function parameter, I'd expect a string to be given to the function (the id of the element). Instead some weird object is given.
How do I convert that object to a string? Or is there a conceptual flaw?
<form>
<input class="joeDateTime" type="text" name="n1" id="n1" value="2014-09-01 17:30:00">
</form>
<script>
function handleJoeDateTime(e)
{
alert('Edit '+e); // shows 'Edit [object HTMLInputElement]'
}
$(document).ready(function() {
$('.joeDateTime').each(function(){
var i = $(this).attr('id');
alert(i); // shows 'n1'
$('<button onclick="handleJoeDateTime(' + i + ');return false;">๐
</button>').insertAfter($(this));
});
});
</script>
You are not passing i as a string value, you are passing it as an variable. In modern browsers the element's id are copied to properties of the window object(so you can access then as global variables).
So you need to enclose them using quotes to pass i as a string value
$('<button onclick="handleJoeDateTime(\'' + i + '\');return false;">๐
</button>').insertAfter($(this));
Demo: Fiddle
Also Instead of using inlined event handlers, I would recommend using jQuery event handlres
$('.joeDateTime').each(function () {
var i = $(this).attr('id');
console.log(i); // shows 'n1'
$('<button />', {
text: '๐
',
click: function () {
handleJoeDateTime(i);
return false;
}
}).insertAfter(this);
});
Demo: Fiddle
Your problem lies here:
$('<button onclick="handleJoeDateTime(' + i + ');return false;">๐
</button>')
where this should be
$('<button onclick=\"handleJoeDateTime(\"' + i + '\");return false;\">๐
</button>')
When you're passing an element to jQuery ( $ ), it becomes a jquery object.
It had been made to handle id, class, elements, not html chunks.
What you want is inserting a piece of concatenated elements as an html node.
so first concatenate your elements then append it with the jQuery's after() method.
(or create/append it with vanilia js var btn = document.createElement("BUTTON");)
var Button = '<button class=\"AltBut\" id=\"' + i + '\">๐
</button>';
$(this).after(Button);
or ( for compacity )
$(this).after('<button class=\"AltBut\" id=\"' + i + '\">๐
</button>');
In this exemple, I'm adding an id to each enabled buttons where I store your variable i
Then add a click listener to those buttons, avoid inline js at all price, for maintainability's sacke.
$('.AltBut').on('click',function(){
var i = $(this).attr("id");
alert("i= "+i);
return false;
})
The whole demo is here: http://jsfiddle.net/x6x4v90y/1/
this looks simple enough but I just can't get it to work. I could add DOM elements but I just can't remove them when using an array.
<script language="javascript">
fields = 0;
count = 0;
function addInput() {
if (fields != 10) {
var htmlText = "<input type='search' value='' name='field[]' />";
var remButton = "<input type='button' value='del' onclick='remove()' />";
var newElement = document.createElement('div');
newElement.id = 'SomeID'+fields;
newElement.innerHTML = htmlText + remButton + newElement.id;
var fieldsArea = document.getElementById('text');
fieldsArea.appendChild(newElement);
fields += 1;
} else {
...
}
count++;
}
// NEED HELP FROM HERE PLEASE
// You don't need to Loop, just get the button's id and remove that entire 'SomeID'
function remove() {
fieldsArea = document.getElementById('text');
fieldsArea.removeChild(SomeID1); <-----------------------THIS WORKS!
fieldsArea.removeChild(SomeID+count); <------------------THIS JUST WOULDN'T
count--;
}
</script>
In the remove function, writing SomeID1 works and delete the first added element but when I try to use a 'count', I just can't delete my 'elements'.
Any help would be most appreciated.
Thank you!
You have to get a reference to the element first. Currently you are passing an undefined variable SomeID to the function.
E.g.:
var element = document.getElementById('SomeID' + fields);
// or starting by zero: var element = document.getElementById('SomeID0');
element.parentNode.removeChild(element);
If you want to remove the div for which the button was clicked, you have to pass a reference to the corresponding div to the remove function.
'<input type="button" value="del" onclick="remove(this.parentNode)" />';
this will refer to the button and as it is a child of the div, this.parentNode refers to that div.
You also have to change your function to accept the element that should be removed:
function remove(element) {
element.parentNode.removeChild(element);
count--;
}
You probably also have to update fields, but I'm not sure how your code is supposed to work.
If you want to remove all of them you have to loop:
for(;fields--;) {
var element = document.getElementById('SomeID' + fields);
element.parentNode.removeChild(element);
}
Also have a look at the documentation of removeChild.
The removeChild needs a node (DOM element) as parameter. In this case
fieldsArea.removeChild(SomeID+count);
you could for example pass the node this way
fieldsArea.removeChild(document.getElementById('SomeID'+count));