I have this JavaScript Code that currently adds new textareas, inputs etc. inside a table and adds a unique number on each id/name
var i=1;
function addRow() {
var tbl = document.getElementById('table1');
var lastRow = tbl.rows.length;
var iteration = lastRow - 1;
var row = tbl.insertRow(lastRow);
var descriptionCell = row.insertCell(0);
var elDescription = document.createElement('textarea');
elDescription.type = 'textarea';
elDescription.name = 'description' + i;
elDescription.id = 'description' + i;
elDescription.cols = 70;
elDescription.rows = 2;
descriptionCell.appendChild(elDescription);
var unitpriceCell = row.insertCell(1);
var elUnitPrice = document.createElement('input');
elUnitPrice.type = 'text';
elUnitPrice.name = 'unitprice' + i;
elUnitPrice.id = 'unitprice' + i;
elUnitPrice.size = 20;
unitpriceCell.appendChild(elUnitPrice);
i++;
form1.number.value=i;
//alert(i);
document.getElementById("line").innerHTML = "whatever";
}
I have removed the table and now this is not working. How can I make the same thing work with text inputs and text areas without the table?
A relatively simple approach to the problem:
function createFieldset(opts){
// default settings:
var s = {
// finds the first form, could use "document.forms[0]" instead
'appendTo' : document.querySelector('form'),
// an array of elements you want to create:
'create' : ['input', 'textarea'],
// the element you want to wrap those elements with:
'wrapper' : 'fieldset',
// the className to apply to the wrapping-element:
'wrapperClassName' : 'group',
// the prefix of the 'legend' tag (if you want to use one):
'legendPrefix' : 'Group '
},
// finding out where the count should start (by finding
// the number of current wrapping elements), could use
// 'document.getElementsByTagName(s.wrapper).length' or
// 'document.getElementsByClassName(s.wrapperClassName).length':
count = document.querySelectorAll(s.wrapper).length,
// creating the wrapper element:
container = document.createElement(s.wrapper),
// finding out whether to use textContent or innerText:
textProp = 'textContent' in document.body ? 'textContent' : 'innerText',
// caching temporary variables for later:
created,
tmp;
// iterating through any user-set properties to update the
// default settings:
for (var prop in opts) {
if (opts.hasOwnProperty(prop)) {
s[prop] = opts[prop];
}
}
// if we have a prefix of length > 0 once leading/trailing white-space
// is removed, then:
if (s.legendPrefix.trim().length > 0) {
// create a 'legend' element:
var legend = document.createElement('legend');
// set the text of that legend element to 's.legendPrefix n'
legend[textProp] = s.legendPrefix + count;
// append that legend element to the container element:
container.appendChild(legend);
}
// iterating over the array of elements to be created:
for (var i = 0, len = s.create.length; i < len; i++){
// caching the string of the element at position i in the array:
tmp = s.create[i];
// creating the element at that position:
created = document.createElement(tmp);
// setting the id, and name, to element-type + n:
created.id = tmp + (count);
created.name = tmp + (count);
// appending the element to the wrapping element:
container.appendChild(created);
}
// setting the className of the wrapping element:
container.className = s.wrapperClassName;
// inserting the element to the appendTo node, before its lastElementChild:
s.appendTo.insertBefore(container, s.appendTo.lastElementChild);
}
createFieldset({
'wrapperClassName' : 'first'
});
var button = document.querySelector('#addMore');
button.addEventListener('click', function (event) {
event.preventDefault();
createFieldset();
}, false);
JS Fiddle demo.
References:
document.createElement().
document.getElementsByClassName().
document.getElementsByTagName().
document.querySelector().
document.querySelectorAll().
EventTarget.addEventListener().
Node.appendChild().
Node.insertBefore().
Object.hasOwnProperty().
String.prototype.trim().
Related
I have the following Javascript code within and HTML page. Its function is to display elements on the form based on the user pressing a + button and if the element is not needed then it removes it via the user pressing the - button. Currently its throwing an error "TypeError: docs[n]" is undefined after the following sequence of events:
Select button to add elements
Remove elements not needed
Add elements back (Error Thrown)
Any help would be most appreciated
`<script language="JavaScript">`
var idx = 0;
var d;
//Make getElementsByClassName work for all of IE revs
if (!document.getElementsByClassName) {
document.getElementsByClassName = function (cn) {
var rx = new RegExp("(?:^|\\s)" + cn+ "(?:$|\\s)");
var allT = document.getElementsByTagName("*"), allCN = [],ac="", i = 0, a;
while (a = allT[i=i+1]) {
ac=a.className;
if ( ac && ac.indexOf(cn) !==-1) {
if(ac===cn){ allCN[allCN.length] = a; continue; }
rx.test(ac) ? (allCN[allCN.length] = a) : 0;
}
}
return allCN;
}
}
function add_fields(e) {
// for some reason, adding the new fields wipes out existing values, so save and restore
var docs = document.getElementsByClassName("doc");
var revs = document.getElementsByClassName("rev");
++idx;
/* console.log("test " + idx); */
var saveDocs = new Array(idx);
var saveRevs = new Array(idx);
for (n=0; n < idx; n++) {
saveDocs[n] = docs[n].value; **//Error is thrown here**
saveRevs[n] = revs[n].value;
}
node = document.getElementById("content");
theNewRow = document.createElement("tr");
theNewCell = theNewRow.insertCell(0);
theNewCell.innerHTML = "Approver Name";
theNewCell.setAttribute("style","font-size: 12pt");
theNewCell1 = theNewRow.insertCell(1);
theNewCell1.innerHTML = "<input type='text' class='doc' style='width:180px;' id='docNum0'/>";
theNewCell1.setAttribute("style","padding-left: 10px");
theNewCell2 = theNewRow.insertCell(2);
theNewCell2.innerHTML = "Approver Email";
theNewCell2.setAttribute("style","font-size: 12pt");
theNewCell2.setAttribute("style","padding-left: 10px");
theNewCell3 = theNewRow.insertCell(3);
theNewCell3.innerHTML = "<input type='text' class='rev' style='width:180px;' id='rev0'/> <input class='minusThing' type='button' style='font-size:10px' value='- '/>";
theNewCell3.setAttribute("style","padding-left: 0px");
node.appendChild( theNewRow );
// restore old arrays and add the id tags to the fields just added
docs = document.getElementsByClassName("doc");
revs = document.getElementsByClassName("rev");
for (n=0; n < idx; n++) {
docs[n].value = saveDocs[n];
revs[n].value = saveRevs[n];
}
docs[idx].id = "docNum" + idx;
revs[idx].id = "rev" + idx;
}
//for Loop the entries
function myfunction() {
alert('Inside Function')
var values = "";
for (n=0; n <= idx; n++)
{
var doc = document.getElementById("docNum"+n).value;
var rev = document.getElementById("rev"+n).value;
//alert(doc+rev);
//Call VbScript Sub and pass value
PassValues(doc,rev);
```
If you've removed all the docs, document.getElementsByClassName("doc"); is going to return an empty array. If you're incrementing idx before your loop, the loop will execute once and try to access docs[0], which is undefined.
I'm creating a javascript function that builds a table with checkboxes.
I want associate a click event to them.
The event must be the same for all.
This is a part of my code:
var i = 0;
for (i = 0; i < data.Items.length; i++) {
var tr = $(document.createElement('tr'));
tr.addClass("MyBookings_table_content");
$("#MyBookings_table").append(tr);
//Create a CheckBox Element
var chkbox = document.createElement('input');
chkbox.type = "checkbox";
chkbox.id = "chk"+i.toString();
chkbox.name = "chk" + i.toString();
//Event here
}
Can anybody help me?
Since you are using jQuery, you can simply add this event listener :
$("#MyBookings_table").on("click", "input[type=checkbox]", clickCB);
function clickCB() {
var $cbx = $(this);
// some code on my checkbox
}
This code is to be appended after the loop, see this fiddle : http://jsfiddle.net/6f79pd5y/
create a function and link it with an addEventListener
function outerFunction(){
}
checkbox.addEventListener('click', outerFunction);
addEventListener("click",myFunction);
where you have the comments
How about:
var i = 0;
for (i = 0; i < data.Items.length; i++) {
var tr = $(document.createElement('tr'));
tr.addClass("MyBookings_table_content");
$("#MyBookings_table").append(tr);
//Create a CheckBox Element
var chkbox = document.createElement('input');
chkbox.type = "checkbox";
chkbox.id = "chk"+i.toString();
chkbox.name = "chk" + i.toString();
//Event here
$(chkbox).click(function()
{
alert("Click!");
});
tr.append(chkbox);
}
A working example:http://jsfiddle.net/mpup12z5/
If you reorganise your element-creation to use jQuery also, you can bind the click-handler at the point of creation:
var i = 0;
for (i = 0; i < data.Items.length; i++) {
var tr = $(document.createElement('tr'));
tr.addClass("MyBookings_table_content");
$("#MyBookings_table").append(tr);
$('<input />', {
'type' : 'checkbox',
'id' : chk + i, // i will be converted to a string automatically
'name' : chk + i,
'click' : functionToCall // note: no parentheses, you want to
}); // call the function, not use its return value
}
Or:
var i = 0;
for (i = 0; i < data.Items.length; i++) {
var tr = $(document.createElement('tr'));
tr.addClass("MyBookings_table_content");
$("#MyBookings_table").append(tr);
$('<input />', {
'type' : 'checkbox',
'id' : chk + i, // i will be converted to a string automatically
'name' : chk + i,
'on' : {
'click' : functionToCall
}
});
}
But, it really is preferable to delegate the event-handling to the closest ancestor element, presumably the <table>, unless a specific property of the created <input /> should call a different function, or do something somewhat different.
I'm using the following javascript to get the data in a clicked table cell:
var table = document.getElementById('ThisWeek'),
cells = table.getElementsByTagName('td');
for (var i=0,len=cells.length; i<len; i++)
{
cells[i].onclick = function()
{
document.getElementById("txtVal").value = this.innerText;
}
}
How can I modify this so I can also obtain the contents of the first cell in the clicked column and the first cell in the clicked row? E.g. if I click on "s" in the table below, I get the results "2" and "B" returned as two variables:
0 1 2 3
A q w e
B a s d
C z x c
Please note that I need a javascript solution, not jQuery. Ideally, I would also like clicks on the first row and first column to return empty strings.
This snippet uses the properties I've mentioned in my comment:
window.onload = function () {
var table = document.getElementById('table');
table.addEventListener('click', function (e) {
var target = e.target,
col = target.cellIndex,
row;
while (target = target.parentElement) {
if (!col && col !== 0) {
col = target.cellIndex;
}
if (target.tagName.toLowerCase() === 'tr') {
row = target.rowIndex;
break;
}
}
console.log(table.rows[row].cells[0].innerHTML + ', ' + table.rows[0].cells[col].innerHTML);
});
}
A live demo at jsFiddle.
I'd suggest:
function index(c){
var i = 0;
while (c.previousSibling){
/* if the previousSibling has a nodeType and that nodeType === 1
(indicating the previousSibling is an element) increment the i variable */
if (c.previousSibling.nodeType && c.previousSibling.nodeType === 1){
i++;
}
// reset c to the previousSibling
c = c.previousSibling;
}
/* i is the count of previous-siblings, and therefore the index
amongst those siblings: */
return i;
}
function getHeaders(e){
/* this is the table element,
e.target is the clicked element */
var el = e.target,
text = 'textContent' in document ? 'textContent' : 'innerText',
headers = [el.parentNode.getElementsByTagName('td')[0][text],this.getElementsByTagName('tr')[0].getElementsByTagName('td')[index(el)][text]];
// set the text of the nextElementSibling of the table:
this.nextElementSibling[text] = headers.join(', ');
}
document.getElementById('table').addEventListener('click', getHeaders);
JS Fiddle demo.
You can add this to your cells[i].onclick function:
var row = this.parentElement; // TR
var table = row.parentElement.parentElement; // TBODY > TABLE
document.getElementById("columnVal").value = row.rowIndex && this.cellIndex ? table.rows[0].cells[this.cellIndex].innerText : "";
document.getElementById("rowVal").value = row.rowIndex && this.cellIndex ? row.cells[0].innerText : "";
I am attempting to create a small program that incorporates dynamically created instances of this editor.
I have it working except for the ability to create a button that opens/closes the editor.
jsFiddle of what I've got so far.
"use strict";
$(document).ready(function() {
var createPad = $("#createPad").click(function () {
var body = document.getElementById("body");
var editorNumberCounter = 1;
var toggleOnOffCounter= 1;
var editorName = '.'+ (editorNumberCounter++);
var status = document.createElement('div');
status.className = "status";
status.id = "status";
var editorName= document.createElement('span');
editorName.className = "status";
editorName.id = "status";
$(body.appendChild(status));
$(body.appendChild(editorName));
var toggle = status.id + toggleOnOffCounter++;
$(editorName).jqte();
// settings of status
var jqteStatus = true;
$(toggle).click(function()
{
jqteStatus = jqteStatus ? false : true;
$(editorName).jqte({toggle : jqteStatus})
});
});
});
I made up some changes to your code an now seems to work.
I try to explain them point by point:
variabiles editorNumberCounter and toggleOnOffCounter must be globally scoped or you lose the incremented value
ID of elements (dynamically created or not) MUST be unique, so I create the div and span element considering the counter
for dynamically created elements you must use the bind method or the event will not be bind
the toggle property not exist! You must use the status property
the element onto JQTE is binded is get as next element after the clicked element
Code:
var editorNumberCounter = 0;
var toggleOnOffCounter = 0;
$(document).ready(function () {
var createPad = $("#createPad").click(function () {
var body = document.getElementById("body");
var editorName = '.' + editorNumberCounter++;
toggleOnOffCounter++;
var status = document.createElement('div');
status.className = "status";
status.id = "div_status" + editorNumberCounter;
var editorName = document.createElement('span');
editorName.className = "status";
editorName.id = "span_status" + editorNumberCounter;
$(body.appendChild(status));
$(body.appendChild(editorName));
$(editorName).jqte();
// settings of status
var jqteStatus = true;
$("#div_status" + editorNumberCounter).bind("click",function () {
jqteStatus = jqteStatus ? false : true;
$(this).next().jqte({
status: jqteStatus
})
});
});
});
Here is a working fiddle: http://jsfiddle.net/IrvinDominin/UfhNQ/14/
I need to create one button that will cycle through 3 states. For some reason that is incredible challenging to find. The functions in each state are simple style changes, example:
click 1: hide div
click 2: show hidden div, change bg-color
click 3: reset bg-color, change font
click 1: reset font, hide div
Any ideas? I can't use jquery (class assignment, not allowed)
Cou could use an array, with a function for each state change, and a counter variable which cycles through the possible states.
Then simply invoke the current state function through an click handler of the button.
Something like this could do it
var toggle = (function (el) {
var div = document.getElementById(el); //Get the div you want to change
var states = []; //The Array to hold the functions
var style = {} //Will be used to save the current style
for (var att in div.style) //Saves the style of the div *I was just lazy and did a for loop*
style[att] = div.style[att]
var current = 0; //The Counter
states[0] = function () { //The first state function
div.style["font-family"] = style["font-family"]
div.style.display = "none";
};
states[1] = function () {
div.style.display = "block";
div.style["background-color"] = "rgb(" + [rand(), rand(), rand()] + ")"; // [1,2,3] toString is "1,2,3"
};
states[2] = function () {
div.style["background-color"] = style["background-color"];
div.style["font-family"] = "Courier New";
}
function rand() { //ONly to return a random number for a random bgcolor
return ~~(Math.random() * 255)
}
return function () { //The function which cycles through the states
states[current]() //Invokes the current statechange function
current = (current + 1) % (states.length); //Increments the counter and uses modulo to cycle
}
})("div");
document.getElementById("click")
.addEventListener("click", toggle);
Heres an example on JSFiddle
Update:
I modified it a bit and commented the changed code, this should be able of changing the states of multiple elements on a page
function rand() {
return~~ (Math.random() * 255);
}
var makeToggle = function (states, elements) { // I Changed it to makeToggle, The first argument accepts an array of states to cycle through, the second either an array of elements, or an array of objects with the element property (and an optional state function)
var current = 0; //Sets the counter to zero
for (var i = 0, ilen = elements.length; i < ilen; i++) {
if (!elements[i].element) { //check if you passed an Object with the `element` Property
elements[i] = {
element: elements[i] //If it was an array, the arrays element will be set to an object
}; //to support arrays only
}
elements[i].style = {}; //to save the original style in the object
for (var att in elements[i].element.style) {
elements[i].style[att] = div.style[att]; // saves it
}
}
function doForElements() { //Invokes either the state function passed with an element, or the general statefunction
for (var i = 0, ilen = elements.length; i < ilen; i++) {
var state = elements[i].states;
if (state && typeof state[current] === "function") state = state[current];
else state = states[current];
state(elements[i].element, elements[i].style); //Invokes the function with the element as first parameter and the original style as second
}
}
return function () { //Returns the function for the click handler
doForElements();
current = (current + 1) % (states.length); //cycles the current state counter
};
};
var states = []; //Here the General State change functions get defined
states[0] = function (div, style) {
div.style["font-family"] = style["font-family"];
div.style.display = "none";
};
states[1] = function (div, style) {
div.style.display = "block";
div.style["background-color"] = "rgb(" + [rand(), rand(), rand()] + ")";
};
states[2] = function (div, style) {
div.style["background-color"] = style["background-color"];
div.style["font-family"] = "Courier New";
};
var elements = [].slice.call(document.getElementsByTagName("div")); //To actually get an Array of the NodeList (all divs on the page)
elements[4] = { //changes the 5th element (which should receive a special statechange function)
element: elements[4],
states: {
1: function (div, style) { //Use an Objects property to pass an single state change instead of an array with functions
div.style.display = "block";
div.style["background-color"] = "yellow";
}
}
};
var toggle = makeToggle(states, elements); //sets the function for the click handler to toggle
//Pass an Object with the Elements and an optional st
document.getElementById("click")
.addEventListener("click", toggle); //binds the function
Heres a JSBin to try it out
<div id="mydiv" data-state="0"></div>
<input type="button" id="mybutton" value="Change State" />
var states = {
1: [{ name: 'display', value: 'none'}],
2: [{ name: 'display', value: 'block'}],
3: [{ name: 'background-color', value: 'white'}, { name: 'prop', value: 'val' }]
}
window.onload = function(){
var mydiv = document.getElementById('mydiv');
var mybutton = document.getElementById('mybutton');
mybutton.onclick = function (){
var num = parseInt(mydiv.getAttribute('data-state'));
num = num < 3 ? ++num : 1;
var nameValues = states[num];
for(var i = 0; i < nameValues.length; i++)
mydiv.style[nameValues[i].name] = nameValues[i].value;
}
}