The Problem:
Before I began adding the div swaps, I could only type into the left (from_value) input and the result of the calculations would be applied via ajax to the right (to_value) input.
I would like to allow the user to type into either box and have the results display in the opposite box they're typing in.
What I am doing to make this happen:
I am swapping the left div with the right div on mouseover of the to_value input. Here's the code i'm using to do the swap:
$.fn.swapWith = function (that) {
var $this = this;
var $that = $(that);
// create temporary placeholder
var $temp = $("<div>");
// 3-step swap
$this.before($temp);
$that.before($this);
$temp.after($that).remove();
return $this;
};
var leftSide = $('#left-side');
var rightSide = $('#right-side');
$('#to_value_input').on('mouseover', function () {
$(rightSide).swapWith(leftSide);
});
This effectively swaps the divs, bringing along with it ids and names of the inputs which retains functionality of my server-side script to perform calculations. As expected, the from_unit select list and to_unit select list are swapped and their values / displayed text are also swapped from one side to the other.
I would like to swap the values and text of the two select boxes either directly before or more likely, directly after the div swap so it appears as if nothing changed on screen.
Similar questions that I have reviewed:
How to swap values in select lists with jquery?
How to swap selected option in 2 select elements?
I have tried several variations of each of the provided solutions. This seems like it should be a fairly simple thing to do but I find myself stuck. Any further help would be greatly appreciated.
The full code base is available on github if you need to see it: https://github.com/pschudar/measurement-conversion
I have made minor changes to the code hosted there to accommodate the div swaps.
I am a teacher with the worst possible slow gradebook, so much so that I would like to use some code to automate it. Basically to submit a grade I need to:
Find the cell box and click on it once to show the submit button
Then click on the button
HOWEVER: Every time you click on the box or button it gives it a new html ID. Therefore I need some code that looks for all the boxes and buttons and hits them. I am not sure how to do this without a static ID.
My code is most definitely formatted improperly, I am a total beginner.
var selectorBox = ['name_of_cell']
var selectorCollection = ['name_of_button']
selectorBox.forEach((s) =>{
let element = document.querySelector(s);
if(element) element.click();
else console.warn('No element found for the supplied selector:', s);
});
selectorCollection.forEach((s) =>{
let element = document.querySelector(s);
if(element) element.click();
else console.warn('no element found for the supplied selector:', s);
});
I need help:
Reformatting to the proper syntax / spacing etc.
Writing a function that finds and clicks on the box THEN the button (the above works).
Making my code look for the boxes then buttons, however as mentioned above the ID for each box and button switches every time you click on one, and for each different class i have (I have about 400 students.)
If you can select cells/buttons by class, you can loop through all cells, "click" them, and then "click" the button once it appears.
var cells = document.querySelectorAll('.x-grid-cell');
cells.foreach((cell) => {
cell.click();
var button = document.querySelector('.class_of_your_button')
button.click()
})
I have a form button in my web app, (purely made with Vanilla Js), The form is used to get data from user and when button is clicked, that data goes to a HTML table and so on.
To edit Table Row, I have placed Edit Button on the table column (at every row), image attached below would help to get clear idea:
The Blue Submit Button is Named as "addButton" , when the user clicks "Edit" of particular row in table, corresponding data is Shown In Input Field, So user can fill in new details.
Up to here is everything fine, now the real problem begins: After user edits the info in Input fields, Blue Submit button is used for saving those changes and spontaneously show change in table as well. (this is the required behaviour).
Instead what is happening is : The modified row is shown in table as well as a new entry is made in row with same details ("addButton" event listener is executed twice, once for saving changes done by user, and once for adding new item in row).
I have placed this event listener (same name, because I do not want separate buttons for saving edited info) twice.
For clarity, please look at code:
(this is first addButton event listener in global scope - used for adding new entry to table)
addButton.addEventListener("click", function(e){
var obj = {
id : object.count,
date : formatDate(inDate.value, inMonth.value),
day : inDay.value,
item : inItem.value,
price : parseInt(inPrice.value),
};
object.info.push(obj);
object.count += 1;
refreshDOM();
});
For "Edit and Delete" buttons in row, I have this:
modifyBtn is tied to table, so I can get click target with that.
(Inside modifyBtn second addButton event listener is present - used for saving changes and show output in table)
modifyBtn.addEventListener('click', function(e){
if(e.target.classList[0] === 'editInfo'){
var eid = e.target.getAttribute('data-id');
var eindex = getIndex(eid);
inDay.value = object.info[eindex]['day'];
inDate.value = parseInt(object.info[eindex]['date'].substr(0,2));
inMonth.value = object.info[eindex]["date"].substr(4);
inItem.value = object.info[eindex]['item'];
inPrice.value = object.info[eindex]['price'];
addButton.addEventListener("click", function(e){
object.info[eindex]['day'] = inDay.value;
object.info[eindex]['date'] = formatDate(inDate.value, inMonth.value);
object.info[eindex]['item'] = inItem.value;
object.info[eindex]['price'] = parseInt(inPrice.value);
refreshDOM();
});
}
if(e.target.classList[0] === 'deleteInfo'){
var did = e.target.getAttribute('data-id');
var dindex = getIndex(did);
console.log(dindex);
object.info.splice(dindex,1);
refreshDOM();
}
});
So after editing when user clicks blue submit button, I want only that addButton event listener to execute which is inside modifyBtn event listener,
Currently both addButton event listeners are getting executed. Sorry for Huge explanation.
The issue, as you're likely aware, is that you're assigning multiple click listeners to the same element (a behaviour that you want) at the same time (a behaviour you don't want). There are a couple of ways you could fix this.
The fastest fix
The quickest way to solve your problem would be to use .removeEventListener(). With this, you can remove the previous click listener (the one that adds a new element to the info array) before you create the second click listener that sets the edited data.
At the end of the second click listener function, you would rebind the first click listener again so the behaviour returned to normal.
Pros of this solution
It's the fastest way to solve the problem you're having
Cons of this solution
Unbinding and rebinding event listeners can make it hard to reason about your code
In order to remove an event listener, you need to supply the original function (like .removeEventListener("click", listenerFunction). As you are currently using an anonymous function expression, you'll have to move the functions currently inside click listeners elsewhere and name them (so that you can pass them to the removeEventListener function
It's not actually clear which event listener is bound to addButton at any one time
The solution
We need to move the function declaration for addButton outside of .addEventListener and give it a name. I've called it addItemClickHandler but you can call it whatever you want:
function addItemClickHandler(e) {
var obj = {
id : object.count,
date : formatDate(inDate.value, inMonth.value),
day : inDay.value,
item : inItem.value,
price : parseInt(inPrice.value),
};
object.info.push(obj);
object.count += 1;
refreshDOM();
}
addButton.addEventListener("click", addItemClickHandler);
This should work exactly the same. Now we need to move the second event listener function you're trying to add into its own named function. As we're only referring to the name of the function from inside itself, we don't even need to move it out, just give it a name. I'm going to give it editItemClickHandler:
addButton.removeEventListener("click", addItemClickHandler);
addButton.addEventListener("click", function editItemClickHandler(e){
object.info[eindex]['day'] = inDay.value;
object.info[eindex]['date'] = formatDate(inDate.value, inMonth.value);
object.info[eindex]['item'] = inItem.value;
object.info[eindex]['price'] = parseInt(inPrice.value);
refreshDOM();
addButton.removeEventListener("click", editItemClickHandler);
addButton.addEventListener("click", addItemClickHandler);
});
As you can see, we first remove the listener addItemClickHandler so that when you click on the "Add" button, it won't do anything
We then bind a different click listener, that we give the name editItemClickHandler so we can remove it after the edit is complete
We do all the edits we need to do
Finally, we remove the new edit click listener we created and re-add the original click listener, so the functionality goes back to normal
The more robust fix
Here is a codepen of your application after applying the following fixes.
The above solution is the fastest way to fix your problem, but there are more robust ways to ensure a working solution. In this solution, I'm going to tidy up some of your code in order to make it cleaner and easier to understand.
Pros of this solution
We won't have to unbind or rebind any click listeners
It's easier to reason about what's happening
Cons of this solution
It'll take longer to implement, as it requires restructuring more of your code
The solution
Step 1: Keep track of whether we're editing or not
Firstly, as we're not rebinding click listeners, we need to keep track of what we're editing. Let's create an object called editing just below object:
var editing = {
mode: false,
index: null
};
This will let us keep track of whether or not we're editing anything (editing.mode), and what the index of the item we're editing is (editing.index).
Step 2: Update the addButton event listener to use the editing object
Next, we need to modify our addButton.addEventListener to use this new editing object:
addButton.addEventListener("click", function(e){
if (editing.mode) {
var info = object.info[editing.index];
info['day'] = inDay.value;
info['date'] = formatDate(inDate.value, inMonth.value);
info['item'] = inItem.value;
info['price'] = parseInt(inPrice.value);
editing.mode = false;
} else {
var obj = {
id : object.count,
date : formatDate(inDate.value, inMonth.value),
day : inDay.value,
item : inItem.value,
price : parseInt(inPrice.value),
};
object.info.push(obj);
object.count += 1;
}
refreshDOM();
});
If editing.mode is true, when the addButton is clicked, it will update the values and then disable editing.mode, putting it back to the way it was before
If editing.mode is false, it will simply add the item to the array (same code as you had before)
No matter what happens, the DOM will get refreshed
Step 3: Update the modifyBtn event listener to use the editing object
Also, I've noticed you're using classes to modify programmatic behaviour instead of data- attributes. This is fine in a lot of cases, but for your exact use case of determining the behaviour, it's recommended to use data- attributes instead. We should also set the href's to #, as we don't need to use them as links:
<td>Edit | Delete</td>
Now, I've restructured your modifyBtn.addEventListener() to handle a these aspects differently:
modifyBtn.addEventListener('click', function(e){
e.preventDefault();
var el = e.target;
var id = parseInt(el.dataset.id);
var index = object.info.findIndex(item => item.id === id);
var info = object.info[index];
var action = el.dataset.action;
if (action === "edit") {
editing.mode = true;
editing.index = index;
inDay.value = info['day'];
inDate.value = parseInt(info['date'].substr(0,2));
inMonth.value = info["date"].substr(4);
inItem.value = info['item'];
inPrice.value = info['price'];
}
if (action === "delete") {
object.info.splice(index, 1);
}
refreshDOM();
});
Using e.preventDefault() means that the browser wont navigate to the # href when clicking on the link
I've moved duplicate code where it retrieved eid and eindex no matter what action you were performing ('editing' or 'adding') outside of those functions
As we are now editing something, we set the editing object to have enabled: true and whatever the index of the current item we're editing is
Instead of using object.info[eindex] every time, I've assigned it to a variable
You no longer need to keep your getIndex function, as you can use Array.prototype.findIndex() instead
The recommended way to get data- attributes is to use element.dataset.name instead of element.getAttribute()
As is standard now, no matter what happens the DOM will get refreshed
Step 4: Add a dynamic form header
Alright, awesome! So this totally works. One final thing I'd like to do is make it more clear what is happening, so in your index.html under your <div class="form-modal">, I'm going to add an ID to your h2:
<h2 id="form-header">Add Item</h2>
Then, back at the top of index.js:
formHeader = getElement('form-header');
And then finally in refreshDOM():
formHeader.textContent = editing.mode ? "Edit Item" : "Add Item";
This will update the text inside <h2 id="form-header"></h2> to depending on whether or not it's being edited. This is a ternary operator, and is often used as a quick way to choose different outcomes depending on a boolean variable.
I hope this wasn't too much information. I've spent a while looking at your code and really wanted to help with best practices and such! Let me know if you have any questions!
Hello and good day to everyone.
I wanna ask if there is a way for me to highlight every area of a map.
This is the scenario... There's a US map and there's a reset button. All the states' names are covered, meaning that every area is already highlighted. Clicking on one of them 'dehighlights' them, revealing their name.
Those things are already functioning properly. However, I am having trouble with the reset button.
Basically, I want the reset button to cover every names again, excluding the still covered ones (because, well, they're not clicked/revealed yet).
Google isn't being helpful for me on this issue, and the Maphilight documentation on the site...isn't really well-documented.
I hope someone can help me on this one. Thanks~
Never mind~ I figured it out! :D
I inserted this code inside [$.fn.maphilight = function(opts)]'s [return this.each(function()]
reset_hilight = function() {
var mapCount = document.getElementsByTagName("area").length;
var i = 1;
while (i <= mapCount) {
var AllAreas = $('#hlight' + i).data('maphilight') || {};
AllAreas.alwaysOn = true;
$('#hlight' + i).data('maphilight', AllAreas).trigger('alwaysOn.maphilight');
i++;
}
}
So now, whenever I click the reset button, all the areas will be highlited once again. :D
I would like to disable a button
<BUTTON name="Next Page" onClick="Next()" VALUE="NextPage">NextPage</button>
based on a javascript variable
var opening = 0;
function Next()
{
var currentdoc = viewONE.getDocIndex();
if (currentdoc == 5)
{
**[DISABLE BUTTON]**
}
what is the javascript code please?
Background information:
Simply browsing through documents using a next and previous buttons. on the first document i want the "previous" button greyed out and on the the last document i want the "next" button greyed out.
Appologise for any incorect terms, newb and never asked this type of question before.
you are welcome to correct my term in a constructive way... need to learn.
Sam's solution is good and will disable the action of the button but won't disable it functionally.
You can disable the input button by changing changing the "disabled" attribute, but you really need to give your button a valid identifier first (I wouldn't even want to let jQuery select it by name due to the space).
jQuery would look something like this:
$('#yourbuttonid').attr('disabled','disabled');
Regular Javascript would be the following:
document.getElementById('yourbuttonid').disabled = true;
Here's an example on JSBin.
All you need to do is:
var opening = 0;
function Next()
{
var currentdoc = viewONE.getDocIndex();
if (currentdoc == 5)
{
return; //This will end the function immediately.
}
}
In terms of greying out buttons, can you not add / remove classes to show different versions of the buttons? We would need more to see your html, and what you have tried so far to help further.
Tutorial on return