JS appendChild() to a <div> in <form> - javascript

I am able to avoid the below issue by making my div a direct child of body and appending a select to the div, but am not able to achieve the same results when the div is a child of a form within body (either when appending to the div or to the form).
I am just making a drop-down table (select element) that is added to the web page when the user clicks a button. The drop-down select shows when the div is the direct child of body, but not when it is a child of a form that is a child of body. When the button is clicked using the below method, the dropdown appears for like 0.25s and then the page returns to the original layout. The same occurs when trying to append directly to the form.
<!DOCTYPE html> <!--text_index.html-->
<html>
<head>
<meta charset="UTF-8">
<title>Add Drop Down Menu to HTML w/ JS on Button Click</title>
<link rel="stylesheet" type="text/css" href="test_styles.css">
</head>
<body>
<form id="form-button">
<div id="user-area">
<button id="dropdown-button">Add a dropdown menu!</button><br /><br />
</div>
</form>
<script src="test_script.js"></script>
</body>
</html>
Now the JS is shown below. The console returns no error but the select element does not remain in the browser window.
let materialArray = ['Select Your Material:',
                    'Kryptonite',
                    'Odium',
                    'Quartz'];
let formButton = document.getElementById('form-button');
let divUserArea = document.getElementById('user-area');
let dropdownBtn = document.getElementById('dropdown-button');
/***********************************************************************/
/****BEGIN LOOP W/ BUTTON CLICK AND NAMED FUNCTION****/
//NOTE: YOU CANNOT appendChild() ON A <DIV> INSIDE A <FORM>.
////IF YOU CAN THEN THERE IS SOME LOGIC THAT I AM MISSING.
////YOU CANNOT appendChild() DIRECTLY ON A FORM EITHER.
let newSelect = document.createElement("select");
let newOption = [];
function onClickDropdownBtn () {
    for (let i = 0; i < materialArray.length; i++) {
        newOption[i] = document.createElement("option"); //create the new option for the new HTML select element
        newOption[i].text = materialArray[i]; //add the appropriate text to the new option
        newSelect.add(newOption[i], i); //add the completed option to the HTML select element
    }
divUserArea.appendChild(newSelect); //also tried formButton.appendChild(newSelect); and got same
result
return true;
}
dropdownBtn.addEventListener('click', onClickDropdownBtn);
if (onClickDropdownBtn === false) {
newSelect.style.display = 'hidden';
dropdownBtn.style.display = 'block';
} else {
newSelect.style.display = 'block';
dropdownBtn.style.display = 'hidden';
}
/****END LOOP W/ BUTTON CLICK AND NAMED FUNCTION ****/
Is there a way to append to the div inside the form, or to the form directly?
Thanks!
For gits and shiggles, here is the CSS. Maybe the issue is here?
select {
width: 200px;
height: 25px;
text-align: center;
}
button {
width: 200px;
height: 25px;
text-align: center;
}

When you put a <button> inside a form, the default type is submit, which makes the page "refresh" (that's why you only see your dropdown for a second).
You can either change your button type, like this:
<button type='button' id="dropdown-button">Add a dropdown menu!</button>
Or you can add the event.PreventDefault() in your click function to avoid the form submission, like this:
function onClickDropdownBtn (event) {
for (let i = 0; i < materialArray.length; i++) {
newOption[i] = document.createElement("option"); //create the new option for the new HTML select element
newOption[i].text = materialArray[i]; //add the appropriate text to the new option
newSelect.add(newOption[i], i); //add the completed option to the HTML select element
}
divUserArea.appendChild(newSelect); //also tried formButton.appendChild(newSelect); and got same
event.preventDefault();
return true;
}
That will keep your dropdown component in the page and you can go from there.

Forms have the default behaviour of reloading the page (they evolved in an era before SPAs). Your code works just fine, except that it also reloads the page and you lose the state (in your case, the select element).
Just change onClickDropdownBtn() to
function onClickDropdownBtn (event) {
event.preventDefault()
...
and it should be ready to go!

Related

jquery getting an 'undefined' on the id of a div element I'm trying to grab

So I am dynamically generating HTML code with JavaScript that loads in all my images from my Firebase Realtime Database. I'm currently working on implementing a button attached to each image that will delete this image when clicked. However, after multiple attempts to grab this div's ID attribute using both standard JavaScript and Jquery, the id in the alert box is always 'undefined'. Inspecting the webpage allows me to see that the image's id is always loaded in just fine, so I know it is there.
This is the HTML Generated that I'm trying to interact with.
JavaScript function to respond to my html 'onclick event'
function deleteFile(){
var postId = $(this).closest('div').attr('id');
alert("You have selected id: " + postId);
var sure = confirm("Are you sure you want to delete this post?");
if(sure){
firebase.database().ref().child('Posts/' + postId).remove().then(function(){
alert("Post deleted sucessfully");
});
}
};
Attached image is what html is generated on the actual chrome inspector. The ID's of course will all be unique.
add this parameter on your html onclick attribute so it become deleteFile(this)
then on your js
function deleteFile(element){
var postId = $(element).closest('div').attr('id');
alert("You have selected id: " + postId);
var sure = confirm("Are you sure you want to delete this post?");
if(sure){
firebase.database().ref().child('Posts/' + postId).remove().then(function(){
alert("Post deleted sucessfully");
});
}
};
Pairing onclick with $(this) won't work as you expect because $(this) has a different context, you could view its value using console.log($(this));
What should be done is add a class to that button and bind an onclick event via jquery. Run snippet below.
Red div contains onclick event attached via jquery.
Yellow div contains onclick event attached to the button attribute onclick.
$(document).ready(function(){
$(".getIdButton").click(function() {
alert($(this).closest("div").attr("id"));
});
});
function getId() {
alert($(this).closest("div").attr("id"));
console.log($(this));
}
div {
width: 100px;
height: 100px;
padding: 20px;
display: inline-block;
}
#red {
background-color: red;
}
#yellow {
background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="red">
Just a link
<button class="getIdButton">Click Me</button>
</div>
<div id="yellow">
Just a link
<button onclick="getId()">Click Me</button>
</div>

Toggle hide/show not working on childs div

I have a script that gets data from a Google Sheet and displays it as a webpage - using JS and Tabletop.js.
There are multiple entries in the Sheet thus multiple entries in the webpage. To organise the Data I have a hide/show button. When the button is clicked on the first entry it works. However when the any of the other buttons are clicked it hides or shows the first entries data, not its own!
How do I hide/show each individual entries data? Below is the code I am working with!
I am new to JavaScript - Thanks in advance!
P.S - I struggled writing the Title to the questions!
<link href="../common/cats-copy.css" media="screen" rel="stylesheet" type="text/css" />
</head>
<style>
#add-info {
display: none
}
</style>
<body>
<div class="container">
<h1>Resturants</h1>
<div id="content"></div>
<script id="cat-template" type="text/x-handlebars-template">
<div class="entry">
<h5>{{establishment_name}}</h5>
<h6>Area: {{area}}</h6>
<h6>Cuisine: {{cuisine}}</h6>
<button id="btn" class="button-primary" onclick="myFunction()">Hide</button>
<div id="add-info">
<h6>Address: {{address}}</h6>
<h6>Google Maps: {{google_maps_location}}</h6>
<h6>Opening Times: {{opening_times}}</h6>
<h6>Rating: {{rating}}</h6>
<h6>Added By: {{added_by}}</h6>
<h6>Date Added: {{date_added}}</h6>
</div>
</div>
</script>
</div>
<!-- Don't need jQuery for Tabletop, but using it for this example -->
<script type="text/javascript" src="handlebars.js"></script>
<script type="text/javascript" src="../../src/tabletop.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script type="text/javascript">
var public_spreadsheet_url = 'https://docs.google.com/spreadsheets/d/1h5zYzEcBIA5zUDc9j4BTs8AcJj-21-ykzq6238CnkWc/edit?usp=sharing';
$(document).ready( function() {
Tabletop.init( { key: public_spreadsheet_url,
callback: showInfo,
parseNumbers: true } );
});
function showInfo(data, tabletop) {
var source = $("#cat-template").html();
var template = Handlebars.compile(source);
$.each( tabletop.sheets("food").all(), function(i, food) {
var html = template(food);
$("#content").append(html);
});
}
</script>
<script>
function myFunction() {
var x = document.getElementById("add-info");
if (x.style.display === "none") {
x.style.display = "block";
} else {
x.style.display = "none";
}
}
</script>
</body>
</html>
Are all the entries on your page filled from the given template, meaning they are divs with the class entry? If so, I think your issue is the following: Your entry div has a child div with the id="add-info". And when you click the button, your handler function (myFunction()) tries to get a reference to that div via document.getElementById("add-info"); Now, if you have multiple such entries on a page, you will have multiple divs with id="add-info". But the id attribute of an element must be unique in your whole document. See the description of id or that of getElementById().
So the root cause of your problem is that the same id is used multiple times in the document when it shouldn't be. You get the behavior you're seeing because getElementById() just happens to be returning a reference to the first element it finds on the page, regardless of which button you click. But I believe you're in undefined behavior territory at that point.
One way to solve the problem is to somehow give myFunction() information about which button was clicked, while making each div you'd like to manipulate unique so they can be found easier. For instance, you can use the order of the restaurant on your page as its "index", and use that as the id of the div you'd like to hide/show. And you can also pass this index as an argument when you call your click handler:
...
<button id="btn" class="button-primary" onclick="myFunction('{{index}}')">Hide</button>
<div id="{{index}}">
<!-- The rest of the code here... -->
...
... add the index into your template context, so Handlebars can fill in the {{index}} placeholder:
...
$.each( tabletop.sheets("food").all(), function(i, food) {
food.index = i // Give your context its 'index'
var html = template(food);
$("#content").append(html);
});
...
... and then alter your function slightly to use the given argument instead of always looking for the div with id="add-info":
function myFunction(indexToToggle) {
var x = document.getElementById(indexToToggle);
// rest of the code is same
With this approach, I expect your DOM to end up with divs that have ids that are just numbers ("3", "4", etc.) and your click handler should get called with those as arguments as well.
Also note that your <button> element has id="btn". If you repeat that template on your page, you will have multiple <button>s with the same id. If you start trying to get references to your buttons via id you will have similar issues with them too since the ids won't be unique.

Apply class to button which execCommand on contentEditable div

Is there a built in way of keeping a button active when it execute a execCommand on a content editable div ?
example :
<input type="button" onclick="doRichEditCommand('bold');" value="B"/>
function doRichEditCommand(command){
document.execCommand(command, null, false);
}
I'd like my button to remain active when the carret is within something where document.execCommand(command, null, false); has been applied. I suppose it's doable by checking the parent elements but if there is a built in way it would be better.
In other words I'd like my bold button to be orange when the carret is somewhere which should be bold.
It's doable, but it's really annoying.
Every time the contenteditable changes you call document.queryCommandState() to find the state of the text where the caret is, and then update the button class to match. So something like:
let div = document.getElementById("myContentEditable");
div.oninput = () => {
if (document.queryCommandState('bold')) {
console.log("bold!");
} else {
console.log("not bold.");
}
return false;
};
From there you can apply or remove a style from your bold button to indicate whether the cursor's in a bold section or not. Repeat for the other styles.
The annoying part is that you need to update the button state on several different events. This seems fairly exhaustive to me:
div.oninput = div.onselect = div.onchange = div.onkeyup = eventhandler;
...but I could be wrong.
<head>
<script type="text/javascript">
var editorDoc;
function InitEditable () {
var editor = document.getElementById ("editor");
editorDoc = editor.contentWindow.document;
var editorBody = editorDoc.body;
// turn off spellcheck
if ('spellcheck' in editorBody) { // Firefox
editorBody.spellcheck = false;
}
if ('contentEditable' in editorBody) {
// allow contentEditable
editorBody.contentEditable = true;
}
else { // Firefox earlier than version 3
if ('designMode' in editorDoc) {
// turn on designMode
editorDoc.designMode = "on";
}
}
}
function ToggleBold () {
editorDoc.execCommand ('bold', false, null);
}
</script>
</head>
<body onload="InitEditable ();">
First, write and select some text in the editor.
<br />
<iframe id="editor" src="editable.htm"></iframe>
<br /><br />
You can toggle the bold/normal state of the selected text with this button:
<br />
<button onclick="ToggleBold ();">Bold</button>
</body>
editable.html:
<!DOCTYPE html>
<html>
<head>
<title>Editable content example</title>
<meta charset="utf-8" />
</head>
<body>
Some text in the editor.
</body>
</html>
some possible solution here: execCommand method examples
This is what I achieved:
JS
function format(command) {
document.execCommand(command, false);
const button = document.getElementById(command);
button.classList.toggle("button--active");
}
HTML
<button id="bold" onclick="format(this.id)">Bold</button>
SCSS
button {
//Whatever you want
&--active {
//Whatever you want
}
}
However, it works for general writing. If you select text and apply an effect, the button will be kept active.

Keeping buttons in place when using .hide()

Not sure if this is because I'm new to meteor or if I am making an error in my syntax with my HTML or jQuery. Ideally I would like the whole grid to stay in place when a button is clicked. For example if you clicked the button in the middle of the grid there would be a empty spot where that button was before. My question is, why is it that when I click a button the button disappears but moves the whole grid and what do I do to fix this?
HTML:
<head>
<title>bubblepopper</title>
</head>
<body>
<center>{{> grid}}</center>
</body>
<template name ="grid">
<div id="container">
{{#each buttons}}
<button class="button" type="button"></button>
{{/each}}
</div>
</template>
JS:
Buttons = new Meteor.Collection("buttons");
if (Meteor.isClient) {
player = prompt("What is your name?")
Template.grid.buttons = function () {
}
Template.grid.buttons = function () {
var list = [];
for(var i=1; i<=64; i++){
list.push({value: i});
}
return list;
};
Template.grid.events({
'click .button': function(ev) {
$(ev.target).hide()
}
});
}
if (Meteor.isServer) {
}
.hide() works by adding the style display: none to the element. This removes the space used by the element in the rendered page.
If you want to make something invisible but keep its space on the page, use the visibility style:
$(ev.target).css('visibility', 'hidden');
To restore it, set the visibility to visible.

Append Form Fields with Javascript

When using Javascript to add additional form fields dyanamically that are contained in a single fieldset, I am running into the issue of the closing fieldset tag still be applying after the first form field. This is causing the layout to break and I need to figure out how I can work around that.
I tried to move the closing fieldset tag outside of the DIV where the additional fields are being appended to, but Firebug inspection still shows it as closing after the first item.
JAVASCRIPT
<script type="text/javascript">
$(function()
{
var template = $('#inventoryItems .inventory:first').clone(),
inventoryCount = 1;
var addInventory = function()
{
inventoryCount++;
var inventory = template.clone().find(':input').each(function()
{
var newId = this.id.substring(0, this.id.length-1) + inventoryCount;
$(this).prev().attr('for', newId); // update label for (assume prev sib is label)
this.name = this.id = newId; // update id and name (assume the same)
}).end() // back to .attendee
.attr('id', 'inv' + inventoryCount) // update attendee id
.appendTo('#inventoryItems'); // add to container
};
$('.btnAddInventory').click(addInventory); // attach event
});
</script>
HTML
<div id="inventoryItems" class="inventoryItems" style="margin:0; padding:0;">
<fieldset style="width:62%; float:left; margin-left: 19%;">
<div id="inv1" class="inventory">
<label>Inventory</label>
<select name="invItem" style="width:92%;">
<?php
$invItem_values = array("id", "name");
display_options_list($dp_conn, $invItem_values, "inventory", "id");
?>
</select>
<a class="btnAddInventory"><img src="images/icn_new_article.png"></a>
<img src="images/icn_trash.png">
</div>
</div>
</fieldset><div class="clear"></div>
You have an error in your HTML markup, an extra closing DIV tag, right before the closing FIELDSET tag. Move it after the .clear tag and you should be ok.
When parsing your HTML and encountering the closing DIV tag (which corresponds to #inventoryItems), it notices you still have a FIELDSET tag that is open so it inserts the close tag for it. Afterwards, when encountering the actual closing FIELDSET tag, it is simply ignored, because there are no open FIELDSET tags.
The original error is in your JS code. The following line adds to the DIV #inventoryItems instead of the FIELDSET.
.appendTo('#inventoryItems'); // add to container
Try replacing it with:
.appendTo('#inventoryItems > fieldset'); // add to fieldset

Categories

Resources