How to change the attribute of an input field in the DOM - javascript

I want to use the jquery plugin "datedropper". Datedropper
To set a default date, the plugin uses an attribute on the input field:
<input id="addjobstartdate" data-default-date="CURRENT DATE"/>
I want to change the default date dynamically, so I try to use:
document.getElementById("addjobstartdate").setAttribute('data-default-date', getjobdetails[31]);
or
$('#addjobstartdate').attr('data-default-date', getjobdetails[31]);
But when I look at the source-code of the page, neither of the commands changes the DOM.
data-default-date is still "CURRENT DATE". Can someone please explain this to me.

If you want to see the changes in the DOM, this worked for me:
document.getElementById("addjobstartdate").dataset.defaultDate = "TODAY"
<input id="addjobstartdate" data-default-date="CURRENT DATE"/>
Dataset documentation on MDN

When changing attrs on HTML elements via client side code, the changes are not reflected in the source code or the dev tools. Try running $('#addjobstartdate').attr('data-default-date') in the console to confirm the value.
EDIT:
I stand corrected. Dev tools does update the value. Not sure if this changed. Your code seems correct.

The "source code" is not the same thing as the "Document Object" that is currently loaded into memory. All you will ever see when looking at the source code is the original code that was delivered to the client machine for processing.
Once the client modifies that, the changes persist only in memory. You can see those changes by looking in your Developer's Tools (press F12 in your browser) and click on the Element tab. This tab shows you the code that is currently in memory (also known as the DOM or Document Object Model).
Now, when working with DOM objects in JavaScript, there are two ways to affect the current state of an element and it's important to understand the effects of working with these two techniques:
Work with the element's current HTML state (which is done with
setAttribute(), removeAttribute(), and getAttribute()). These types of changes will be visible when looking at the DOM in the Elements tab.
Work with element's current DOM Object state (which is done with the JavaScript property that represents the current state) of the element in memory. These types of changes will not cause any change to the markup shown in the Elements tab, but will affect the element by persisting in memory.
Most importantly, the property value can be different than the attribute value. This can be confusing but the HTML state is what the element looks like from the outside and the property state is what is really happening on the inside, like you can put a happy face on so that people who look at you think your happy (the HTML state), but you might actually be sad for real (the property state).
When the property state hasn't been set, the attribute state is all that matters and will have total control over the state of the element. When the property state is set, it overrides whatever the attribute state may be and controls the actual state of the element.
// Get a reference to the button
var btn = document.querySelector("[type=button]");
// Test what the current HTML state is:
console.log("disabled attribute state: ", btn.getAttribute("disabled"));
// Test what the current mapped property state is:
console.log("disabled property state: ", btn.disabled);
console.log("...changing button's disabled PROPERTY to false...");
// Change the property state, which will override the HTML state and
// and cause it to become enabled.
btn.disabled = false;
// Test what the current HTML state is:
console.log("disabled attribute state: ", btn.getAttribute("disabled")); // null because property overrode HTML
// Test what the current mapped property value is:
console.log("disabled property state: ", btn.disabled);
<input type="button" value="I'm disabled" disabled="disabled">

Related

Meteor embedded documents reactivity

I have a bootstrap modal that will display info about a parent document. The modal then has next/prev buttons that can rotate around a list of parent documents and reactively change the info displayed on the modal. It does this by simply changing a ReactiveVar of the current parent's onClick of next/prev. This works like a charm.
My problem is that each parent shows some child data as well. The child data is an array of embedded documents. And each document's property has some HTML inputs. I'm trying to pre-populate the values of the html inputs. However, I'm finding this does not work. id and foo are properties on the child document(s).
A call to {{parent}} is a Helper method that returns the current parent from MiniMongo. This seems to work fine as the parent's metadata changes reactively.
{{#each parent.childrenArray}}
<input type="text" id="{{id}}" value="{{foo}}">
{{/each}}
So the issue is that the HTML value does not change. Clicking next will still show the old child's value. I understand there's no reactivity for embedded documents and I'm sure this is the problem. Strangely, upon inspecting, I am in fact seeing the input's id being changed though. Does anybody know the solution to this? Thanks!
This seems to happen when I try to save the children back to the DB. I.e. hitting next/prev should save whatever value users puts into the HTML input before moving on to the next Parent and pre-loading its children
If parent is a helper, then I don't think you can use the subscript notation to get its properties, at least not reactively. However you should be able to use this instead:
{{#with parent}}
{{#each childrenArray}}
<input {{attributes}}>
{{/each}}
{{/with}}
Update: The other thing that is probably required is to use an attribute helper. I've also updated the code above to use this.
Template.yourTemplateName.helpers({
attributes() {
return {
type: 'text',
id: this.id,
value: this.foo
};
}
});
Update 2:
Working "offline" in a git repo by the OP I realized that the modal body was better done as a separate template, such that the data context for that template would change reactively. The part of the code that was at odds is not represented in the question, so read this question and answer with a grain of salt.

How can I manipulate HTML elements which are added during the runtime?

The code im working on first makes a call to the database. Through this call, it is determined wether there are available workstations in the office or not.
If there are available workstations, "option" elements are added to a "select" element. This is achieved via jquery:
$('#idofselectelement').html(data)
Where "data" represents the markup to be inserted into the "select" element.
Now, my problem is that I'm trying to implement some code which checks wether your "favorite workstation" is available in the selected timeframe and then automatically preselects the respective workstation from the dropdownmenu in the "select" element. Everything is working so far, except for the selection of the workstation from the dropdown menu :/
The part of
I'm rather new to programming with javascript and the HTML DOM, so I'm not sure whether the fact that the options im trying to chose from are added during the runtime?
The code I've tried to manipulate the dropdown menu with is like this:
$('#idofselectelement').val(favoriteworkstation);
However, as I said, this doesn't work.
I've also already tried to output (console.log) the select element's length property right after the code which adds the markup with the available options has run.
But according to the output Im getting, the length is zero Oo
However, the dropdownmenu is definitely being created AND I can indeed manipulate it, but unfortunately not in the way I want to.
If I add an onclick event which sets the value of the respective select element, then the value in the select field indeed changes to the value specified in the event handler.
So I wonder why I can't have the favorite workstation preselected after the timeframe was chosen...
EDIT:
For further insight into the problem, I'm adding a bit more code here.
This is what the HTML Select element looks like BEFORE anything is added during the runtime:
<label for="#sitz">Sitz Nr.</label>
<select type="text" class="form-control" id="sitz" name="sitz" value="">
the markup which is added during the runtime
<option>workstationvalue</option>
<option>workstationvalue</option>
//and so on, depending on the situation...
This is a timing issue.
The js trying to find the element is faster than the actual add of the element to DOM.
Can you describe what you want to do? You might be able to do that before adding the element to DOM.
Editing before adding to DOM is possible if you convert your String to an jQuery object
var $jqueryObject = $(data);
$jqueryObject.find('.classYouSearch').val(value);
$('.whereToAd').html($jqueryObject);

Checking states of Slide toggle not working in Cypress Tests

I am trying to test the states of slide-toggles within my app using Cypress.
These time out and fails the test:
cy.get('label.mat-slide-toggle-label').eq(2).should('be.checked')
or
cy.get('div.mat-slide-toggle-bar').eq(2).should('be.checked')
Where as these pass
cy.get('label.mat-slide-toggle-label').eq(2).should('not.checked')
or
cy.get('div.mat-slide-toggle-bar').eq(2).should('not.checked')
The only difference is that the state of the toggle has changed.
Can someone help explain why the "not.checked" tests pass, but the others don't?
The documentation states:
The <mat-slide-toggle> uses an internal <input type="checkbox">
to provide an accessible experience. This internal checkbox
receives focus and is automatically labelled by the text content of
the <mat-slide-toggle> element.
When Angular Material adds the switch, it adds a whole little hierarchy of elements under the outer <mat-slide-toggle> element; divs with classes like mat-slide-toggle-label, mat-slide-toggle-bar, etc. But it also adds a real (but hidden) <input> element.
The 'checked' test only applies to input elements (this is probably why your should('not.be.checked') tests are working--because non-input elements can never be checked. So, to use Cypress's should('be.checked') test, you need to tell Cypress to get a reference to the actual <input> contained within the <mat-slide-toggle>, and not one of the other mat-xxx elements.
Example:
cy.get('mat-slide-toggle#whateverId input').should('be.checked');
// get reference to the single <input> inside the <mat-slide-toggle>
or:
cy.get('mat-slide-toggle#whateverId .mat-slide-toggle-input').should('be.checked');
// get reference to the element with class "mat-slide-toggle-input" inside the <mat-slide-toggle> (which is the <input> itself)
I was going to invite you to use the GUI snapshots panel to better understand what could be wrong, and maybe increase the timeout(s).
But in fact, I'm tempted to conclude that neither <label> nor <div> can be checked. <input type="checkbox"> can.
Is there another property you can assert on your label ?
I have managed to find a element for each toggle that allows me to check the state (checked or not checked).
input#mat-slide-toggle-29-input.mat-slide-toggle-input.cdk-visually-hidden
All I need to do is change the number to related to the toggle under test. I can check that the toggle is checked, press the master switch and then check that it is unchecked. I will also created a test where I test each toggle individually to ensure that the toggle works in a ground and singularly.

Aurelia JS - multi level binding/update?

I'm not sure whether I'm using the right terminology here - but here is an example:
https://gist.run/?id=57ed46429e4583eb4c3fb11814451a55
This is how it looks like in Chromium:
Basically, the entries on top (red outline) are a visualization of an array as the "first-level" data display; here one can toggle each element's selection, and make a multi-element selection (red background). The array that is the source of the "first-level" display is mydata in first-level-items.js.
Those items that are selected in "first-level", are then shown again in "second-level" (green outline); here the same information of name and value is displayed, although a bit differently. Here also one can toggle an elements selection - but only one "second-level" element can be selected. The array that is the source of the "second-level" display is myseldata in second-level-items.js.
The intent here, is that once a "second-level" selection has been made, a slider appears, to change the .value property of the particular object which is the selected array element.
My original question (which is why I started this post/example at all), was:
How do I ensure that whenever the slider is changed, the value is updated in both second-level and first-level display?
... however, for reasons beyond me, this in fact does work somewhat in this gist.run example (but it still doesn't work in my actual project, which forced me to come up with the example to begin with). Also it only works somewhat, in the sense that when loading the example page at first, after making first and second level selections, and then changing the slider, the .value will be updated in both first- and second-level display. But as soon as I try deselecting on second level - or changing the selection on second level - then updating stops. So, I guess this question still stands...
After a second-level selection has been made, deselecting on second level (by clicking to toggle) does NOT remove the slider; how can I have it behave like that?
The update happens only on the slider's onChange - basically, while you drag and slide, this component emits onSlide, but it will generate onChange only at the end when the mouse is released (that is, when the sliding has stopped). How can I update both first- and second- level display while the slider is sliding?
And finally - is this how this kind of a problem is best addressed in Aurelia? That is - I currently have one array in first-level-items.js; first-level-items.js then has a singleton reference to second-level-items.js, so it can call a method within it, to change a filtered copy of the array on the second level, which then serves as a source both for second-level display and the slider... Is there a better way to organise this?
Boy, this was a pain, but here's what I think is the solution:
https://gist.run/?id=c09fea3b82a9ebc41e0a4c90e8665b04
Here are some notes:
Apparently, there is something wrong applying if.bind or show.bind on the input element of the slider - instead, the input element should be put in an enclosing div, and the div should have if/show.bind applied
Furthermore, if.bind should not be used, as it re-instantiates the slider element - use show.bind so we can get a reference to the slider widget at start already, even if it is hidden
Seemingly, using TaskQueue in attached() is the only way to get a reference to the slider at start
Once we have a reference to the widget, re-apply it on each second level element, whenever they change
Do not set this.myselChanging to null to specify no target of the slider (simply count on hiding the slider appropriately)
For a continuous change (onSlide), simply use this.myselChanging.value = e.value; in the handler - both first-level and second-level values will be changed
Beyond this, it seems arrays are copied by reference, so the multi-level update happens without further intervention...
Though, would still love to know what is the proper way to do this...

How to invoke this Angular method that's bound to ng-click

I'm inspecting the DOM on an Angular application and trying to figure out how I can reproduce one of the events that's bound to a button via the console. The button looks like this:
<button class="button tiny text player-add-button" ng-class="{ 'player-add-button': !player.inLineup, 'player-remove-button': player.inLineup }" ng-click="player.inLineup ? draft.rosterRemove(player) : draft.rosterAdd(player)">
What I'm trying to access here is draft.rosterAdd(). The problem is, this is a table, and there's multiple buttons and player is changing for every button. I'm not entirely sure how to define player here, even if I get into the scope of of the draft object, to pass it in as an argument to rosterAdd()
What's the best way to figure out how I can define player so that I can invoke draft.rosterAdd(player) for all of the players I want to add via the console?
Try this (in Chrome):
Right-click the desired button and select Inspect element;
Open the console tab (make sure the button markup remains selected);
Type draft = angular.element($0).scope().draft;
Type player = angular.element($0).scope().player.
Now you should be able to see how player is structured and call draft.roasterAdd() passing whatever you want.
Some useful references about the code above:
angular.element
Chrome's command line API

Categories

Resources