I want to be able to remove a class from x amount of elements. The elements that I am currently using is a tags. There can be unlimited amount of a tags on the document at any given time because in a different div an user can create links. This prevents me from using specific ids. But I want to only have one tag highlighted at any given time. However, I have only seen how to do it with JQuery, which I can't use. I saw a different example using Javascript, but that was for a fixed size array.
This is the code for my highlighting class:
<DOCTYPE html>
<html>
<head>
<style>
.selected{border:dotted 2px;border-color:gray;}
</style>
</head>
<body>
<div id='linkEditDiv'>
<form name='linkEditor'>
<table>
<input type="hidden" name='linkId'>
<tr><th>Label:</th><td> <input type='text' name='label'></td></tr>
<tr><th>Link:</th><td> <input type='text' name='link'></td></tr>
<tr><th></th><td><input type="button" onclick='submitForm()' value="OK" ><input type="button" onclick='cancel()' value="Cancel" ></td></tr>
</table>
</form>
</div>
<a href='#' onclick='edit("lr1")' id='lr1'>link1</a>
<a href='#' onclick='edit("lr2")' id='lr2'>link1</a>
<a href='#' onclick='edit("lr3")' id='lr3'>link1</a>
</body>
</html>
This is the JavaScript that adds the highlighting:
function edit(link)
{
var elems = document.getElementsByClassName('selected');
for (var i = 0; i < elems.length; i++) {
elems[i].className = elems[i].className.replace('selected', '')
}
document.getElementById(link).className += "selected"
}
This is the JavaScript that I tried:
var id=document.forms['linkEditor'].elements['linkId'].value;
document.getElementById(id).className = document.getElementById(id).className.replace( /(?:^|\s)selected(?!\S)/g , '' );
Create a removeClass function that iterates over the elements and replaces the class with an empty string, thus removing it.
function removeClass(selector, klass) {
var elems = document.querySelectorAll(selector);
for (var i = elems.length; i--;) {
var reg = new RegExp("(?:^|\\s)"+ klass +"(?!\\S)", "gi");
elems[i].className = elems[i].className.replace(reg, "");
}
}
to be used as
removeClass('.classname', 'classToRemove');
FIDDLE
First you grab all selected nodes, you loop through them and then you remove the class from each node.
You can't use getElementsByClassName because once you remove the class from the first element, the live dom object updates and shifts the positions, which will lead to moving the for loop one position and skipping that element.
for (const node of document.querySelectorAll('.selected')) {
node.classList.remove('selected')
}
.selected {
color: red
}
<p class="selected">Red text</p>
<p class="selected">Red text</p>
<p>Black text</p>
Try something like this to remove the class from all of the links, and then add the "selected" class back onto the link that you want to have it:
var elems = document.getElementsByClassName('selected');
for (var i = 0; i < elems.length; i++) {
elems[i].className = elems[i].className.replace('selected', '')
}
UPDATE: Just to clarify, the code works in the form you have in the question. I have taken it and applied two tweaks. 1) The hidden input should not be inside the table tags but outside of td/th tags. 2) I added "return false;" to the links.
<DOCTYPE html>
<html>
<head>
<style>.selected{border:dotted 2px;border-color:gray;}</style>
<script>
function edit(link) {
var elems = document.getElementsByClassName('selected');
for (var i = 0; i < elems.length; i++) {
elems[i].className = elems[i].className.replace('selected', '')
}
document.getElementById(link).className += "selected"
}
</script>
</head>
<body>
<div id='linkEditDiv'>
<form name='linkEditor'>
<input type="hidden" name='linkId'>
<table>
<tr><th>Label:</th><td> <input type='text' name='label'></td></tr>
<tr><th>Link:</th><td> <input type='text' name='link'></td></tr>
<tr><th></th><td><input type="button" onclick='submitForm()' value="OK" ><input type="button" onclick='cancel()' value="Cancel" ></td></tr>
</table>
</form>
</div>
<a href='#' onclick='edit("lr1"); return false;' id='lr1'>link1</a>
<a href='#' onclick='edit("lr2"); return false;' id='lr2'>link1</a>
<a href='#' onclick='edit("lr3"); return false;' id='lr3'>link1</a>
</body>
</html>
This is no longer the issue that you were asking for help with.
Related
Hi I am dynamically adding rows with a button and when I am finished entering information, I would like it to then clear the contents. The button "Add Pokemon" is the one I want to press and it should clear all the contents.
function addPokemon() {
var pokemonName = document.getElementById("pokemon-name-container");
pokemonName.innerHTML = document.getElementById("pokemon-names").value;
for (var i = 0; i < element.length; i++) {
if (element[i].value !== "undefined") {
pokemonArray.push(element[i].value);
}
}
console.log(pokemonArray);
for (var i = 0; i < pokemonArray.length; i++) {
document.getElementById("pokemon-container").innerHTML += "<li>" + pokemonArray[i] + "</li>";
}
document.getElementById("pokemon-name-container").value = "";
document.getElementById("move-name").value = "";
}
This is my function I am using. ^^
And below is my HTML vv
<div>
<table>
<tbody id="tbody">
<tr>
<td>
<div id="pokemon-name-container">
<p>Pokémon Name:</p>
<input type="text" id="pokemon-names" size="30">
</td>
</tr>
<tr>
<td>
<p class="moves">Moves:</p>
</td>
</tr>
<tr>
<td>
<input class="move-container" type="text" id="move-name" placeholder="Enter move here">
</td>
<td>
<input class="button-container" type="button" id="remove-btn" value="Remove Move" onclick="removeRow()">
</td>
</tr>
</tbody>
</table>
</div>
<div>
<input type="button" class="add-move-button" id="add-move-button" value="Add Move" onclick="addRow()">
</div>
<div>
<input type="button" class="add-pokemon-button" id="add-pokemon-button" value="Add Pokémon" onclick="addPokemon()">
</div>
You could put to all the inputs you create a unique class that defines them under a parent with a unique id. Then use inside the function of javascript the next pice of code const childs = document.querySelectorAll('#idParent.classChilds') this querySelectorAll is kind of like the getElementsById but uses selectors of CSS so it's more powerfull. The querySelectorAll returns you a NodeList of all the elements that matches de DOM with the CSS query.
Then you would only need to do something similar to this using functional programming:
const childs = document.querySelectorAll('#idParent .classChilds')
childs.forEach(child=>{
child.value = ""
})
I'm not sure if this code works (I'm not with an code editor and a browser to check if there isn't mistakes), as I said, you could do something similar to it
HOPE IS HELPFULL
FYI, try to avoid the selectors like getElementById or getElementsByClass....
Try to use this:
document.querySelector('CSS SELECTOR') // GIVES YOU THE FIRST MATCH OF THE CSS SELECTOR
document.querySelectorAll('CSS SELECTOR') // GIVES YOU A NODELIST WITH ALL MATCHES
I am continuing work on a previous project: Google Sheet Data in a Sidebar
Now, I would like to retrieve the items that have been checked in the sidebar and return that data to the Code.gs file and ultimately the Google Sheet (see code below). Can you offer suggestions on how to do this?
Below, I am trying to add the checked items to the "students" array. I would then like the "Submit Early Release" button to send the "students" array to the Code.gs file. However, I am unsure how to push the checked items to the array properly.
Page.html
> <!DOCTYPE html>
><html>
> <head>
> <base target="_top">
> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
> </head>
> <body>
> <script>
> function addStudents(studentList){
> $('#rangeResult').text(studentList);
>
> document.write(new Date().toLocaleDateString());
>
> var students = [];
> for (var i = 0; i < studentList.length; i++) {
> document.write('<br><input type="checkbox" name="studentList[i]" id="i" value="i">'+ studentList[i]);
> }
> document.write('<br><input type="button" value="Submit Early Release" onclick="google.script.host.close()" />');
> document.write('<input type="button" value="Close" onclick="google.script.host.close()" />');
> };
>
> google.script.run.withSuccessHandler(addStudents).earlyReleaseList();
> </script>
> </body>
></html>
Thank you for your help!
Update
Madhav, thank you for your suggestions. I've adapted your code to fit my scenario, but I'm still having trouble getting the array data back to the spreadsheet. Specifically, when I click the "Submit Early Release" button the sidebar closes but no data is written into the specified cell. Would you mind taking a look?
Page.html
Added another "src=" line for jquery - not sure if this is needed???
Added "collectNames" function to get checked names and send them back
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
</head>
<body>
<script>
function addStudents(studentList){
$('#rangeResult').text(studentList);
document.write(new Date().toLocaleDateString());
//var students = [];
for (var i = 0; i < studentList.length; i++) {
document.write('<br><input type="checkbox" class="special" name='+ studentList[i]+ 'id="i" value="i">'+ studentList[i]);
}
document.write('<br><input type="button" value="Submit Early Release" onclick="collectNames()" />');
document.write('<input type="button" value="Close" onclick="google.script.host.close()" />');
};
function collectNames(){
var students = [];
var checkboxes=document.getElementsByClassName("special"); //get all checkboxes
for(var i = 0; i < checkboxes.length; i++){
if(checkboxes[i].checked){
students.push(checkboxes[i].getAttribute("name")); //if checked then push to array the value
}
}
//now send the finalarray to the destination
google.script.run.releasedStudents(students);
google.script.host.close();
};
google.script.run.withSuccessHandler(addStudents).earlyReleaseList();
</script>
</body>
</html>
Code.gs
function releasedStudents(values) {
var doc = SpreadsheetApp.openById("1OF6Y1CTU9dkIgd1P-nw-5f2lqHSS5cGZytndwzJhw-o");
var ss = SpreadsheetApp.getActiveSpreadsheet();
var cell = ss.getRange('V20').getValue();
cell.setValue(values);
}
If you want to push checked items into the array, then the first thing you will need to ensure is that each checkbox carries the value it represents in some form or the other. Your code does attempt to do that but when you write
document.write('<br><input type="checkbox" name="studentList[i]" id="i" value="i">'+ studentList[i]);
the name attribute of each checkbox is always the same because it is a constant string ("studentList[i]") and not the value from the array, so it should be replaced with :
document.write('<br><input type="checkbox" class="special"name='+studentList[i]+ ' id="i" value="i">'+ studentList[i]);
Once we are done with the input , we should be able to collect values from the checked boxes only ..one way to do that is to assign a class to all the checkboxes so that they can later be accessed via getElementsByClassName() function.
Once obtained, the value attribute of only those checkboxes should be pushed to the array which have the checked property as true.
A slightly different code demonstrating this is :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<button id="clickthis">Click this</button>
</body>
<script>
var studentList=["nevil","ron","draco","harry"];
var finalarray=[];
function runmyfun(){
var checkboxes=document.getElementsByClassName("special"); //get all checkboxes
for(var i=0;i<checkboxes.length;i++){
if(checkboxes[i].checked){
finalarray.push(checkboxes[i].getAttribute("name")); //if checked then push to array the value
}
}
//now send the finalarray to the destination
}
document.getElementById("clickthis").onclick=function(){
document.write(new Date().toLocaleDateString());
for (var i = 0; i < studentList.length; i++) {
document.write('<br><input type="checkbox" class="special"name='+studentList[i]+ ' id="i" value="i">'+ studentList[i]); //concat properly
}
document.write('<br><input type="button" value="Submit Early Release" onclick="runmyfun()" />');
document.write('<input type="button" value="Close" onclick="google.script.host.close()" />');
};
</script>
</html>
I hope this is what you were looking for and not something else.
Disclaimer: I am not looking for someone to code this for me just some pointers to help me fix this problem :)
I have the following web page that allows me to add fields dynamically to a form. The current page works. What I want to do is figure out how to make the javascript at the bottom of the page more generic. example I want to pass the templet id and the target id to the function without hard coding the templet id and the target id into the script. Here is the code that I have and works just fine.
I want to make the morefields function so that I can reuse. I want to pass to the function the template and the target. example function moreFields ( templete, target). this way I can use the same function without editing over and over in different web pages. if you look in to the moreFields function you will see that it is hard coded for "readroot" and "writeroot" I want to change the function so it will take parameters and do the same thing it is doing now.
<HTML>
<HEAD>
<META NAME="generator" CONTENT=
"HTML Tidy for Linux/x86 (vers 25 March 2009), see www.w3.org">
<TITLE></TITLE>
<STYLE TYPE="text/css">
div.c1 {display: none}
</STYLE>
</HEAD>
<BODY >
<DIV ID="readroot" CLASS="c1">
Variable Name <INPUT NAME="VarName"><BR>
</DIV>
<FORM METHOD="post" ACTION="/cgi-bin/show_params.cgi">
Function Name: <INPUT NAME="CFunction"> <BR>
Function Alias: <INPUT NAME="AFunction"><BR>
<BR>
<SPAN ID="writeroot"></SPAN>
Function return: <INPUT NAME="AFunction"><BR>
<INPUT TYPE="button" ID="AddMoreFields" VALUE="Give me more fields!" ONCLICK= "moreFields()"> <INPUT TYPE="submit" VALUE="Send form">
</FORM>
<SCRIPT >
var counter = 0;
function moreFields() {
counter++;
var newFields = document.getElementById("readroot").cloneNode(true);
newFields.id = '';
newFields.style.display = 'block';
var newField = newFields.childNodes;
for (var i=0;i<newField.length;i++) {
var theName = newField[i].name
if (theName)
newField[i].name = theName + counter;
}
var insertHere = document.getElementById("writeroot");
insertHere.parentNode.insertBefore(newFields,insertHere);
}
window.onload = moreFields()
</SCRIPT>
</BODY>
</HTML>
I'm writing a simple HTML page that is a list of buttons in a table and each button is a link to a website. What I want is that when you left click a button, it opens the link in the same window and when you right click on it, the link opens in a new window.
I've gotten that all working as below. While it does work, I am trying to clean up the code as this looks like it could be done more efficiently though I am not sure how. Right now I have to manually pass "onContextMenu" to every button and have the URL written out twice.
Is there any way to optimize this or clean up the code to be more readable and maintainable?
<script>
function RightClickFunction(url) {
//alert("You've tried to open context menu");
window.open(url);
return false;
}
</script>
...
...
<tr>
<td><form action="http://amazon.com"><input class="btn" type="submit" value="Amazon" onContextMenu="return RightClickFunction('http://amazon.com');"></form></td>
<td><form action="http://newegg.com"><input class="btn" type="submit" value="Newegg" onContextMenu="return RightClickFunction('http://newegg.com');"></form></td>
</tr>
EDIT:
Here is the work in progress for one solution.
<html>
<head>
<script>
var elems = document.querySelectorAll('input.btn');
console.log(elems);
for (var i = 0; i < elems.length; i++) {
console.log(elems[i]);
elems[i].oncontextmenu = function(e) {
return RightClickFunction(this.parentNode.getAttribute('action'));
}
}
function RightClickFunction(url) {
alert("You've tried to open context menu: " + url);
return false;
}
</script>
</head>
<body>
<table>
<tr>
<td><form action="http://amazon.com"><input class="btn" type="submit" value="Amazon"/></form></td>
<td><form action="http://newegg.com"><input class="btn" type="submit" value="Newegg"/></form></td>
</tr>
</table>
</body>
</html>
Sure, you can select all the inputs, loop through them and add event listener to each.
var elems = document.querySelectorAll('input.btn');
for (var i = 0; i < elems.length; i++) {
elems[i].oncontextmenu = RightClickFunction;
}
See it in action here (note, I'm using the form action attribute (this.parentNode.getAttribute('action')) to assign the URL to the input context menu events):
var elems = document.querySelectorAll('input.btn');
console.log(elems);
for (var i = 0; i < elems.length; i++) {
console.log(elems[i]);
elems[i].oncontextmenu = function(e) {
return RightClickFunction(this.parentNode.getAttribute('action'));
}
}
function RightClickFunction(url) {
alert("You've tried to open context menu: " + url);
//window.open(url);
return false;
}
<tr>
<td><form action="http://amazon.com">
<input class="btn" type="submit" value="Amazon" />
</form></td>
<td><form action="http://newegg.com">
<input class="btn" type="submit" value="Newegg" />
</form></td>
</tr>
I am using jQuery library for my projects:
$('input[type="button"').on('contextmenu',function(){
alert('you made a rightclick');
});
So you have a contextmenu function on every button at your site (NOTE: please include jQuery first!!).
See here for more Information: JQuery on('contextmenu') for body, but special one class
I am working on a page which has certain number of Dojo textarea elements.
<textarea readonly class="readTextBox versionText"></textarea>
The number of these elements is more than one. I am trying to add a javascript function or formatting of a textarea. The function gives me certain line count for the textarea elements. The script is :
<script type="application/javascript">
window.onload=function() {
var versionElement = document.getElementsByClassName("versionText");
console.log("length:" + versionElement.length);
var versionElementText = versionElement.value;
var lines = versionElementText.split(/\r\n|\r|\n/);
var rowCount = 0;
for(var line=0;line<lines.length;line++) {
rowCount += Math.ceil(lines[line].length/82);
}
versionElement.rows=rowCount;
}
The problem is, i am not able to add it for all the textarea elements. I am just bale to get only one textarea elements.
I tried Windo.onload still i am getting textarea element count as 1. JS snippet is placed at the very bottom of the page as well.
the parent file which calls up the textarea is :
<div class="box-content">
<div id="pnl_{$id}">
{foreach from=$version item=version name=version}
{assign var=textBoxes value=$version->textBoxes}
<div data-dojo-type="dijit.TitlePane>
{include file="addTextArea.tpl"}
</div>
{/foreach}
</div>
</div>
The generated HTML looks like:
<div class="box-content">
<div id="productPanel_80920">
<div data-dojo-type="dijit.TitlePane" data-dojo-props="title:'Version 4 <span class=versionDate>2014-01-16 15:35:21</span>'">
<table width="100%" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td class="tableCell">
<textarea readonly class="readTextBox versionText" style="resize: none; outline: none; word-wrap: break-word;" spellcheck="true"> random text
</textarea>
</td>
</tr>
</tbody>
</table>
</div>
<br class="box-divider"/>
<div data-dojo-type="dijit.TitlePane" data-dojo-props="href:'addTextArea.html?id=4059&version=3', title:'Version 3 <span class=versionDate>2014-01-10 14:52:46</span>',open:false"></div>
<br class="box-divider"/>
<div data-dojo-type="dijit.TitlePane" data-dojo-props="href:'addTextArea.html?id=4059&version=2', title:'Version 2 <span class=versionDate>2014-01-10 14:48:09</span>',open:false"></div>
<br class="box-divider"/>
<div data-dojo-type="dijit.TitlePane" data-dojo-props="href:'addTextArea.html?id=4059&version=1', title:'Version 1 <span class=versionDate>2014-01-10 14:47:41</span>',open:false"></div>
<br class="box-divider"/>
the textarea is populated anytime i click on the div.
First off, the proper HTML would be this (you were missing a closing >):
<textarea readonly class="readTextBox versionText"></textarea>
Second off, document.getElementsByClassName returns a nodeList which is like an array. You have to go through each DOM element in the nodeList to operate on all your textarea elements.
I'm not sure I know exactly what you're trying to do, but converting your code to perform the oepration you've code on each textarea returned, it would be something like this:
window.onload=function() {
var items = document.getElementsByClassName("versionText");
for (var i = 0; i < items.length; i++) {
var versionElement = items[i];
var versionElementText = versionElement.value;
var lines = versionElementText.split(/\r\n|\r|\n/);
var rowCount = 0;
for(var line=0;line<lines.length;line++) {
rowCount += Math.ceil(lines[line].length/82);
}
versionElement.rows=rowCount;
}
}
var objs=document.getElementsByTagName('textarea');
for (var i=0;i<objs.length;i++){
var obj=objs[i];
if (!obj.className||obj.className!='versionText') continue;
//do your transformation here
}