Managing dynamically generated elements with javascript - javascript

I have code which generates markers on a Google map based on certain criteria. What I'm attempting to do is generate a list next to the map which would contain the address associated with each marker that appears on the list as well as a checkbox next to each address. At the bottom of this list would be a "refine" button which would return only the results which had their checkbox selected.
My question are:
How would I store the dynamically generated checkboxes and their respective address fields so that I could update and modify the selection with the refine button?
How would I (or should I) actually set each checkbox to run a function on onClick which would remove the marker from the map?

Create checkboxes on the fly inside a given div
var holder = document.getElementById('holdingDiv');
var newCheckbox = document.createElement('input');
newCheckbox.type = 'checkbox';
newCheckbox.id = 'holdingDiv_option' + someValueIdentifier;
holder.appendChild(newCheckbox);
To run through these checkboxes adding event handlers:
// modify this if not just a bunch of checkboxes in a div:
var checkboxes = holder.getElementsByTagName('input');
for(var i=0; i < checkboxes.length; ++i) {
var thisCheckBoxId = checkboxes[i].id;
// create a listener
var callback = function(event) {
myGeneralHandler(i, event);
}
if(checkboxes[i].addEventListener) {
checkboxes[i].addEventListener('click', callback, false);
} else { //IE
checkboxes[i].attachEvent('click', callback);
}
}
Then set up myGeneralHandler to handle clicks from any checkboxes.

Related

JavaScript radio onclick processed before UI reflects selection

I have the following JavaScript code:
var radios = form.elements["option_filter"];
for (var i = 0, max = radios.length; i < max; i++) {
if (typeof (radios[i].value) !== 'undefined') {
radios[i].onclick = function () {
var l = this.nextElementSibling.innerText;
<code to create a path containing selection>
window.location.replace(dest);
}
}
}
which works fine, except that when a radio is clicked, the selection is considered checked in code, but that is not reflected in the UI ... the radio button remains blank as the redirect is processed.
I tried having it sleep a bit between the onclick firing and the redirect, to no avail.
How do I get the UI to reflect the selection change so that the user sees it selected while waiting for the new page to load?
To get the UI to reflect... you'll need to set the value of the target radio button to a value...
radio[i].value = "whatever"
Also remember that the value set must match the name of one of the radio buttons in your options...

SlickGrid Check Box Select Column Plugin "Check All"

I know I can loop through the rows and get all the id and set the rows to selected via an array.
var rows = [];
for (var i = 0; i < _grid.getDataLength(); i++) {
rows.push(i);
}
_grid.setSelectedRows(rows);
But how can i fire the event programatically to check/uncheck the header checkbox which selects/unselects all the rows?
This is the snippet in the plugin JS file that looks for the header box to be checked:
function handleHeaderClick(e, args) {
if (args.column.id == _options.columnId && $(e.target).is(":checkbox")) {
// if editing, try to commit
if (_grid.getEditorLock().isActive() && !_grid.getEditorLock().commitCurrentEdit()) {
e.preventDefault();
e.stopImmediatePropagation();
return;
}
if ($(e.target).is(":checked")) {
var rows = [];
for (var i = 0; i < _grid.getDataLength(); i++) {
rows.push(i);
}
_grid.setSelectedRows(rows);
}
else {
_grid.setSelectedRows([]);
}
e.stopPropagation();
e.stopImmediatePropagation();
}
}
Setting the selected rows as you describe using grid.setSelectedRows() will update the 'Select/Deselect All' checkbox in the header row. The 'Checkbox Select Column' plugin subscribes to the grids onSelectedRowsChanged event which is triggered by grid.setSelectedRows(). When this event occurs the plugin calls the function handleSelectedRowsChanged(e, args) which determines if the 'Select/Deselect All' checkbox should be checked and updates it accordingly.
Setting the selected items to an empty array using the following snippet will clear all checkbox.
//Select no rows - clear the header checkbox
grid.setSelectedRows([]);
Setting the selected items to an array of all items using the following snippet will check the checkbox .
//Select all rows - will check header checkbox
//Generate an array of all rows ids from the grid data
var rows = [];
for (var i = 0; i < grid.getDataLength(); i++) {
rows.push(i);
}
//Set selected rows on grid
grid.setSelectedRows(rows);
If you really must trigger an event to check / uncheck the 'Select/Deselect All' header checkbox then you could use something like the below. However I'd strongly recommend sticking to the above methods.
//Determine if the checkbox is currently checked
var currentlyChecked = $('#myGrid').find('.slick-header-column:first-child').find('input').prop('checked')
//Set to reverse of current i.e. !currentlyChecked
$('#myGrid').find('.slick-header-column:first-child').find('input').prop('checked', !currentlyChecked).trigger('click');
The following fiddle demonstrates this with buttons for each of the methods for selecting items.
http://jsfiddle.net/z83fjth4/

Javascript remove child on click based on class

I'm making a basic list using vanilla javascript. I am able to add items, and change their class when they are clicked. Now, I want the items that have been selected (so their class has been changed) to be removed when they are clicked. At the bottom of the code, I am trying to loop through the list, then if the element in the list has the selected class, an event listener will remove the element when clicked, but this isn't working for me. Any ideas on what I'm doing wrong? (live demo: http://codepen.io/nicolaswilmot/pen/oXLgyq)
Here is the code:
var list = document.getElementById("theList"); // Get the list
// Add new item to top of list
function addItem(e) {
var userTxt = document.getElementById("userInput"); // Get user text
var newItem = document.createElement("li"); // Create new list item
var itemTxt = document.createTextNode(userTxt.value); // Get the text for item
newItem.appendChild(itemTxt); // Add text to list item
list.insertBefore(newItem, list.firstChild); // Put new item at top of list
newItem.className = 'defaultItem'; // Set default class for li
document.getElementById("userInput").value = ''; // Clear the input box
e.preventDefault(); // Prevent page from reloading when page is submitted
// Changes list item class
function changeClass () {
newItem.className = 'selectedItem';
}
// Initialize array for list items
listArray = [];
// Loop through list, add items to array, update class and counter
// when items are clicked
for (var i=0; i<list.children.length; i++) {
listArray.push(newItem);
listArray[i].addEventListener("click",changeClass);
listArray[i].addEventListener("click",countStuff);
}
}
var docForm = document.getElementById("theForm"); // Get the form element
docForm.addEventListener('submit',addItem,false); // Call addItem function when form is submitted
docForm.addEventListener('submit',countStuff,false); //Call counter when form submitted
// Function for the list item counter
function countStuff() {
// Get div container for counter
var itemCount = document.getElementById("counter");
// Get all list items that have not been selected (default class)
var unselectedItems = document.querySelectorAll('li.defaultItem');
//If more than one item, display plural "items"
if (unselectedItems.length > 1) {
itemCount.innerHTML = 'You still need '+unselectedItems.length+' items!';
} else if (unselectedItems.length == 0) {
itemCount.innerHTML = 'You have all items!';
} else {
itemCount.innerHTML = 'You still need '+unselectedItems.length+' item!';
}
}
// Loop through the list
for (var i=0; i<list.children.length; i++) {
// Remove items that are in selected state
if (list.childNodes[i].className='selectedItem') {
list.childNodes[i].addEventListener('click',function () {
list.removeChild([i])},false);
}
}
The placement of your code where you are trying to remove the element once it has the selectedItem class does not make sense, because that code will only run once on page load when the page has no items in the list. Instead, in the same function where you add the selectedItem class, you can bind an event listener to that DOM element that removes it from the list on the next click. http://codepen.io/anon/pen/vOKEzz
function changeClass () {
newItem.className = 'selectedItem';
//Remove it on click!
newItem.addEventListener('click',function () {
list.removeChild(newItem)
}, false);
}

Uncheck a checkbox in dat.gui

When I change the option of a dropdown menu, I want all the checkboxes to be unchecked. Here's the code that I put inside a function that's called when the dropdown menu changes:
var inputs = document.getElementsByTagName("input");
for(var i = 0; i < inputs.length; i++) {
if(inputs[i].type == "checkbox") {
inputs[i].checked = false;
}
}
This does indeed uncheck the checkbox. However, to recheck the checkbox, it takes two clicks. It appears that dat.gui still thinks the checkbox is checked, so it takes one click to uncheck it, and one more to check it.
How do I make dat.gui update the checkboxes?
Edit: Here's the current state of the problem.
gui = new dat.GUI;
controllers = [];
var menu = {
'This is an example': false,
}
controllers[0] = gui.add(menu, 'This is an example').onFinishChange(
function(value) {console.log('example');} ).listen();
menu['This is an example'] = false;
With this code, the checkbox is unchecked, due to the .listen() call and setting the variable to false. However, it still takes two clicks for the check to show--one to "uncheck" the checkbox, and one to check it.
I've recently come across the same issue with having to click the checkbox twice to get the proper behavior, so here's what worked for me and will hopefully spare other readers a few minutes of head-scratching:
// the usual
var menu = { "foo":false };
// store this reference somewhere reasonable or just look it up in
// __controllers or __folders like other examples show
var o = menu.add(menu, "foo").onChange(function() { });
// some later time you manually update
o.updateDisplay();
o.__prev = o.__checkbox.checked;
First set up data binding by telling dat.gui to listen to the value you need to bind to by including .listen() after your .add()
gui = new dat.GUI;
controllers = [];
var menu = {
'This is an example': false,
}
controllers[0] = gui
.add(menu, 'This is an example')
.listen()
.onFinishChange(
function(value) {
console.log('example');
}
);
Then set your variable that dat.gui is controlling via the checkbox to false.
menu['This is an example'] = false;
Some more info about the details of dat.gui: http://dat-gui.googlecode.com/git-history/561b4a1411ed13b37be8ff974174d46b1c09e843/index.html

Attaching eventListener to dynamic elements in Javascript

I'm making a todo list and I have li and button tags added dynamically when adding a new list item. The button is an x which is supposed to remove the list item. I have tried several things but can't figure out how to make an eventListener for each individual x button and remove the corresponding list item when it is clicked.
The renderTodos function is where all of the dynamically added content is created. I have a data-index set to each button in which I was trying to use to access each button to attach an eventListener on each dynamic button, but I wasn't sure how to implement that. From what I have read there should be a way to do this using the currentTarget or target of the event but I don't understand how that works.
var input = document.querySelector('input[name=todoItem]'),
btnAdd = document.querySelector('button[name=add]'),
btnClear = document.querySelector('button[name=clear]'),
list = document.querySelector('.todo'),
storeList = [];
function renderTodos(){
var el = document.createElement('li'),
x = document.createElement('button');
listLength = storeList.length;
//Set text for remove button
x.innerHTML = 'x';
for(var i = 0; i < listLength; i++){
el.innerHTML = storeList[i];
list.appendChild(el);
x.setAttribute('data-index', i);
el.appendChild(x);
}
// check for correct data-index property on x button
}
function addTodos(){
storeList.push(input.value);
// Check that input is getting pushed to list array
console.log(storeList);
renderTodos();
}
function clearList(){
// make list empty
list.innerHTML = '';
storeList.splice(0, storeList.length);
//render empty list
renderTodos();
//Check that list array is empty
console.log(storeList);
}
btnAdd.addEventListener('click', addTodos);
btnClear.addEventListener('click', clearList);
Everything else on the list works so far I just can't figure out how to implement this eventListener.
One simple example can be
//a click hadler is added to #mylist which is already present in the dom
document.querySelector('#mylist').addEventListener('click', function(e) {
//assuming that the the `x` is in a span and it is the only span in the `li` we check for that, we can improve this check more to make sure we have actually clicked on the delete button
if (e.target.tagName == 'SPAN') {
//if so then since we know the structure we can delete the parent node of the target which is the span element
e.target.parentNode.parentNode.removeChild(e.target.parentNode);
}
}, false);
//kindly forgive the use of jQuery here
for (var i = 0; i < 10; i++) {
$('<li />', {
text: i
}).append('<span class="x">X</span>').appendTo('#mylist');
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<ul id="mylist"></ul>
This is a very basic implementation of event delegation, where the actual event is bound to an ancestor element but then we use the actual event target to determine whether to act on it. We can improve the if condition to test for an class for any other attribute!!!
You can add a listener to each button using something like:
x.innerHTML = '';
x.onclick = function(){
var node = this.parentNode;
node.parentNode.removeChild(node);
};
Or you can keep the renderTodos code as it is and delegate the remove to the parent UL:
// Add the listener
list.addEventListener('click', removeItem);
// The listener function
function removeItem(event) {
var node = event.target;
// Check that the click came from an X button
// better to check against a class name though
if (node.tagName &&
node.tagName.toLowerCase() == 'button' &&
node.innerHTML == 'x') {
node = node.parentNode;
node.parentNode.removeChild(node);
}
}
basically what you want to do is add an event on the parent container and wait for the event to bubble up and identify if the event originating is from your x mark and if it is then trigger the callback function.. This is the concept I think most of the libraries use..
Or use a library like jQuery, why solve a problem that has already been solved by others.

Categories

Resources