How to pass element to javascript function and access the first child - javascript

I am creating check-boxes in my ASP.NET code behind a file in C#. I am adding an attribute value before adding the control to the page, therefore ASP is adding the attribute to the span surrounding the check-box and the label for the text. It looks like this:
<span data-bind="visible: showCheckBox(this)">
<input id="" type="checkbox" name="ctl00$MainContent$SecondaryForm$ctl00" value="1">
<label for="">Outside Source</label>
</span>
I have a function called showCheckBox() written in Knockout.js. It determines if the target checkbox should be displayed, based on the value of the selected item in the drop down list immediately preceding it. For example, if the value of the selected item in the drop down list is 1, then the target checkbox with a corresponding value of 1 would be visible. That function looks like this:
function showCheckBox(span) {
var value = span.firstChild.value;
return value == reason();
}
reason() is the view model variable that holds the value of the selected drop down list item.
No matter what I do, I cannot get the value of the check-box to be sent correctly. It is always undefined.

The first child in this HTML is actually a textNode, which firstChild will return if that is what is found. These are the actual characters it is returning: "↵ " (A return and a space).
You can use the firstElementChild property instead:
function showCheckBox(span) {
var value = span.firstElementChild.value;
return value == reason();
}
Also, don't forget to check the support tables for firstElementChild.

After thinking about it for a while also, a coworked suggested this solution.
Instead of assigning the value of the check box as the ID that corresponds to the drop down list view model variable, he suggested I assign it in the data-bind attribute instead. In my C# code behind file, that looks like this
cb.Attributes.Add("data-bind", String.Format("visible: showCheckBox({0})", reasonDetails[i].ReasonForSendingNoticeID.ToString()));
Which looks like this when displayed in HTML
<span data-bind="visible: showCheckBox(1)" style="display: none;">
<input id="" type="checkbox"
name="ctl00$MainContent$SecondaryForm$ctl00" value="Outside Source">
<label for="" style="display: inline;">Outside Source</label>
</span>
Then I changed the function as follows
function showCheckBox(id) {
return id == reason();
}
Doing it like so allowed us to directly pass the value into the function without having to pass the element or its child.
Thank you for the help and suggestions.

The problem is hidden, and it's because you're for a large part not using KnockoutJS at all. You should check the docs for the KnockoutJS checked binding.
There should very likely be no reason to check the DOM inside view models, not even those handling visibility. Instead, use the view model. You haven't quite posted a complete minimal repro, but here's what it should more or less look like:
function ViewModel() {
var self = this;
self.reasons = [{id: 1, txt: "Main reason"}, {id: 2, txt: "Other reason"}];
self.reason = ko.observable(self.reasons[0]);
self.showCheckBox = ko.computed(function() {
return self.reason().id === 1;
});
self.hasOutsideSource = ko.observable(false);
};
ko.applyBindings(new ViewModel());
pre { font-family: consolas; background: smoke; border: 1px solid #ddd; padding: 5px; margin: 5px; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<select data-bind="options: reasons, value: reason, optionsText: 'txt'"></select>
<span data-bind="visible: showCheckBox">
<label>
<input data-bind="checked: hasOutsideSource" type="checkbox" name="ctl00$MainContent$SecondaryForm$ctl00" value="1">
Outside Source
</label>
</span>
<hr>
This would be sent to your server / debug info:
<pre data-bind="text: ko.toJSON($root, null, 2)"></pre>
If you can't take the above approach, I strongly recommend considering skipping the use of KnockoutJS, because it'll probably cause you more trouble than it'll help you if you do to much DOM manipulation on your own.

Related

Trying to target an element if it has a label

Attempted to log a label to console via
var labelTest = document.getElementById('js_8').label;
console.log(labelTest);
However it is returning undefined.
Edit: correcting some stuff, sorry at work and trying to do this in between other tasks. What my end result needs to be is targeting the inner html of the js_8 ID, but with React it is different for each of the Pages that it is on. So I want to add an extra stipulatoin of having that label attribute.
HTML:
<span data-reactroot="" label="1715724762040702" class="_xd6" data-pitloot-persistonclick="true" display="inline" data-hover="tooltip" data-tooltip-content="Copy Text to Clipboard" id="js_8"><div class="_xd7">1715724762040702</div></span>
I'm not sure exactly what you're after, but this is a way to connect a <label> and <input> together via JavaScript.
var some_id = 'someid',
my_label = getLabel(some_id);
function getLabel(id) {
return document.querySelector('[for=' + id + ']')
}
my_label.click();
<label for='someid'>My Label</label>
<input type='text' id='someid' />
You can associate a <label> with an <input>, <output>, <select> or <textarea> element in one of two ways:
The for attribute:
<label for="js_8">Test</label>
<input id="js_8">
Or by wrapping the element with a label:
<label>Test<input id="js_8"></label>
You can then access the associated label(s) as an array like this:
var labelsTest = document.getElementById('js_8').labels;
// labelsTest will be an array of 0 or more HTMLLabelElement objects
console.log(labelsTest);
Label-able elements can have more than one label.
So essentially I believe I am going to want to utilize var x = getAttribute("label") . The fact that the attribute was titled label confused me, and in turn I goof'd.

Populate a hidden form field using the div of a drop menu (drop down menu is created using divs not select, option etc)

I have a form.
Please note I must use divs for creating the form drop down and not the select option method etc. It just has to be done that way. The code is below.
<form action="url.asp" method="get">
<div class="search-button"><i class="fa fa-search"></i><input type="submit" /></div>
<div class="search-drop-down">
<div class="title"><span>Choose Category</span><i class="fa fa-angle-down"></i></div>
<div class="list">
<div class="overflow">
<div class="category-entry" id="Category1">Category One</div>
<div class="category-entry" id="Category2">Category Two</div>
</div>
</div>
</div>
<div class="search-field"><input type="text" name="search-for" id="search-for" value="" placeholder="Search for a product" /></div>
<input type="hidden" id="ChosenCategory" name="ChosenCategory" value="CATEGORY1 OR CATEGORY2 (WHICHEVER SELECTED)" />
</form>
As shown in the code above I need to populate the hidden field value as per the chosen option which the user selects in the drop down.
I have used about 20 different variations of getElementById or onFocus functions but cannot get it to work.
The only thing I can get to work is the following JavaScript and it just populates the hidden field value with the first id ignoring completely which one has actually been selected(clicked) by the user;
var div = document.getElementById('DivID');
var hidden = document.getElementById('ChosenCategory');
hidden.value = div.innerHTML;
I'm running classic asp so if there is a vbscript way then great, otherwise if I have to use JavaScript to do it then as long as it does the job I'll be happy still.
A click handler on the options could be used to update the value.
No jQuery or any other external library is needed. Below is a working example. Of course, in your case the input element could be of type hidden, but I made it text here for the sake of demonstration.
//Add the click handlers
var options = document.getElementsByClassName('option');
var i = 0;
for (i = 0; i < options.length; i++) {
options[i].addEventListener('click', selectOption);
}
function selectOption(e) {
console.log(e.target);
document.getElementById('output').value = e.target.id;
}
div {
padding: 10px;
}
div.option {
background-color: #CCC;
margin: 5px;
cursor: pointer;
}
<div>
<div class="option" id="Category1">Category One</div>
<div class="option" id="Category2">Category Two</div>
</div>
<input type="text" id="output" />
You should be able to achieve what you're after with a fairly simple setup involving listening for clicks on two separate <div> elements, and then updating an <input> based on those clicks.
TL;DR:
I've put together a jsfiddle here of what it sounds like you're trying to make work: https://jsfiddle.net/e479pcew/5/
Long version:
Imagine we have 2 basic elements:
A dropdown, containing two options
An input
Here's what it might look like in HTML:
<div class="dropdown">
<div id="option-one">Option 1</div>
<div id="option-two">Option 2</div>
</div>
<input type="text" id="hidden-input">
The JavaScript needed to wire these elements up should be fairly easy, but let me know if it doesn't make sense! I've renamed things throughout to make things as explicit as possible, but hopefully that doesn't throw you off.
One quick thing - this is an incredibly 'naive' implementation of this idea which has a lot of potential for refactoring! However I just wanted to show in the most basic terms how to use JavaScript to make this stuff happen.
So here we go - first things first, let's find all those elements we need. We need to assign variables for the two different dropdown options, and the hidden input:
var optionOne = document.getElementById("option-one");
var optionTwo = document.getElementById("option-two");
var hiddenInput = document.getElementById("hidden-input");
Cool. Next we need to make a function that will come in handy later. This function expects a click event as an argument. From that click event, it looks at the id of the element that was clicked, and assigns that id as a value to our hiddenInput:
function valueToInput(event) {
hiddenInput.value = event.target.id;
}
Great - last thing, let's start listening for the clicks on specific elements, and if we hear any, we'll fire the above valueToInput function:
optionOne.addEventListener("click", valueToInput, false);
optionTwo.addEventListener("click", valueToInput, false);
That should get you going! Have a look at the jsfiddle I already linked to and see if it makes sense - get in touch if not.
Are you allowed to use JQuery in this project? It would make your life a lot easier. You can detect when a div is clicked and populate the hidden field.
This could do it:
$(document).ready(function() {
$('.category-entry').click(function() {
$('#ChosenCategory').val($(this).text()); }); });

How to get the bounded element DOM from an observable object

I need to get the Element DOM from an observable object.
Please look on this example.
I'm clicking on the button "Get Class" and then I want to get valueB DOM element.
Please pay attention that I want valueB DOM element and not buttonA
Example:
HTML
<button id="buttonA" data-bind="event:{click: getClassFromValueB}">Get Class</button>
<input id="valueB" class="Hello" data-bind="value: observables.idNumber"/>
VIEWMODEL
"
"
"
observables : {
idNumber: ko.observable('SomeText');
},
getClassFromValueB : function(child, event){
idNumber_DOM = this.getElementDOM(this.observables.idNumber);
},
getElementDOM : function(observable){
**//WHAT TO DO HERE????**
}
"
"
"
I seeking a solution without jQuery...
$(event.target).closest('#valueB')
UPDATE: The main reason for this question is to to clear a customized attribute in a input when one of the other inputs are change
Example:
<input id="InputA" class="Hello" data-bind="event:{change: clearInputB}"/>
<input id="InputB" class="Hello" data-bind="value: observables.idNumber"/>
<input id="InputC" class="Hello" data-bind="event:{change: clearInputB}"/>
<input id="InputD" class="Hello" data-bind="event:{change: clearInputB}"/>
Not exactly what you're asking for but maybe you can solve the problem in a different way. If you're after a DOM element's class, Knockout provides a css binding. valueB's data-bind statement would look something like this:
<input id="valueB" data-bind="css: observables.valueBClass, value: observables.idNumber"/>
Then you can have another observable named valueBClass where you can set valueB's class as well as retrieve it from other observables.
valueBClass = ko.observable('Hello');
getClassFromValueB = function() {
var theClass = observables.valueBClass();
}
You're on the right track to keep the DOM and the model separate. This is an important concept that is sometimes missed when using Knockout. As a result of keeping them separate, you would not reference any DOM elements anywhere in your Knockout model. You would only reference observables.

Synchronizing a button class with an option control using knockoutjs

usually I can figure out a way to make Knockout-js do what I want. In this case however, i'm struggling a little, so I'm opening the question up to the community here on SO.
Introduction
I'm writing an HTML5 web app using typescript, bootstrap, knockoutjs and a nodejs backend.
In my UI which is all controlled via knockoutJS I have a set of buttons, formed as a bootstrap 3 button group of select-able options.
This justified group, gives me 4 buttons horizontally, but allows the behaviour of the button selections to remain consistant with a group of option buttons.
That consistancy is important, beacuse ONLY one button at a time can ever be selected, so when one is clicked, the rest deselect.
This is a default component in BS3, as the following image shows:
As you can see in the image, the 'Discarded' button is selected, to achieve this a class of 'active' must be added to the existing class list of the label element surrounding the inner radio element that makes up the control. The following HTML is used to create this image:
<div class="btn-group btn-group-justified" data-toggle="buttons">
<label class="btn btn-primary">
<input type="radio" name="options" id="option1" checked >Rejected
</label>
<label class="btn btn-primary active">
<input type="radio" name="options" id="option2">Discarded
</label>
<label class="btn btn-primary">
<input type="radio" name="options" id="option3">Held
</label>
<label class="btn btn-primary">
<input type="radio" name="options" id="option3">Header Added
</label>
</div>
All this works great except for one small flaw, I'm using knockout JS to manage the UI.
The Problem I'm Trying to Solve
The checked state of each of the options is tied to a property inside the view model applied to the HTML, so the inner option on the rejected button for example has a normal knockout-js checked binding added to it as follows:
<input type="radio" name="options" id="option1" checked data-bind="checked: reject">Rejected
Each of the options, each have their own backing field:
reject
discard
hold
addheader
and each of those backing fields are a standard boolean value holding true/false, what I can't figure out is how to add/remove the 'active' class on the enclosing label, to reflect which of these states has been selected.
To be more precise, I cant figure out the best way to do it elegantly.
Approaches I've tried
what I know works is to add a simple computed observable to each label that returns
"btn btn-primary active"
when that option is set to true, and
"btn btn-primary"
when it is not.
I know this, because in my view model, I had a simple function:
SenderDialogViewModel.prototype.isRejectSelected = function () {
if (this.reject == true) {
return "btn btn-primary active";
}
return "btn btn-primary";
};
However, this approach means 4 functions, one for each flag to test, making it difficult to add new flags at a later date.
What I'd like to be able to do, is something like the following:
<label class="btn btn-primary" data-bind="class: isSelected(reject)">
as an example.
Which I almost got to work with a slight modification to the above:
SenderDialogViewModel.prototype.isSelected = function (selectionVariable) {
if (selectionVariable == true) {
return "active";
}
return "";
};
Where selection variable could be any of the flags available in the view model, passed in.
The problem here was, that this ONLY updated the first time the UI was drawn, subsequent changes to the flags, failed to update the UI to reflect the given status.
To try and resolve this, I changed the function to a computed observable, only to then receive a JS error when the UI was drawn, stating that the computed observable had to have a 'write' handler added to it, because I was passing a parameter in.
If I need to add a write handler, then that's fine, but I'd rather not.
Summary
So in summary, there are ways of changing the class list in sync with other options, but most of them are messy, what I'm trying to do is create a way that's easily expanded as new buttons are added (This is important as some button sets are dynamically generated), rather than adding a handler to individually check and report the status on each and every variable there, in one function call that can be added simply into the view-model and re-used again and again.
Ok... and as it ALWAYS happens, no sooner do I post this, than I actually figure out how to make it work.
The solution was staring me in the face all along, in the form of the knockout js css binding.
To quote the knockout-js docs:
The css binding adds or removes one or more named CSS classes to the associated DOM element. This is useful, for example, to highlight some value in red if it becomes negative.
What this is saying, as "I can apply or remove a single class, to the collection of classes already present, based on the value of a variable in my view model"
So, the answer to my problem, quite simply becomes:
<div class="btn-group btn-group-justified" data-toggle="buttons">
<label class="btn btn-primary" data-bind="css: { active: reject }">
<input type="radio" name="options" id="option1" checked >Rejected
</label>
<label class="btn btn-primary" data-bind="css: { active: discard }">
<input type="radio" name="options" id="option2">Discarded
</label>
<label class="btn btn-primary" data-bind="css: { active: hold }">
<input type="radio" name="options" id="option3">Held
</label>
<label class="btn btn-primary" data-bind="css: { active: addheader }">
<input type="radio" name="options" id="option3">Header Added
</label>
</div>
Along with that, if I now just add the appropriate option/checked bindings to the option controls themselves, then everything should update correctly as needed when the buttons are clicked.
A little side note on working your own answer out
I think sometimes, the exercise of just having to think through your problem, while 'virtually' trying to describe it to others, triggers the brain to think in a different direction.
It certainly helped, that I was typing this when the penny dropped, but I proceeded and then answered/updated as appropriate, because it occurs to me that this is a question that will trip others up too, hopefully this will serve as an education to fellow travelers who might like me just be suffering from a sunday evening brainfart.
Update Monday 30-June 2014
It turns out, this was a little more tricky than I first anticipated. Sure I solved the main answer to my question about syncing the CSS for the button above. BUT... syncing the Option buttons also turned out to be quite a challenge, this update is to present a full end to end solution.
First, you need to mark up your HTML like this:
<p><strong>Rule type:</strong></p>
<div class="btn-group btn-group-justified" data-toggle="buttons">
<label class="btn btn-primary" data-bind="css: { active: reject }, click: function(){ changeType('reject'); }">
<input type="radio" name="ruletype" value="reject" data-bind="checked: selectedOptionString" >Rejected
</label>
<label class="btn btn-primary" data-bind="css: { active: discard }, click: function(){ changeType('discard'); }">
<input type="radio" name="ruletype" value="discard" value="true" data-bind="checked: selectedOptionString">Discarded
</label>
<label class="btn btn-primary" data-bind="css: { active: hold }, click: function(){ changeType('hold'); }">
<input type="radio" name="ruletype" value="hold" data-bind="checked: selectedOptionString">Held
</label>
<label class="btn btn-primary" data-bind="css: { active: addheader }, click: function(){ changeType('addheader'); }">
<input type="radio" name="ruletype" value="addheader" data-bind="checked: selectedOptionString">Header Added
</label>
</div>
The KEY take away's here are as follows:
1) The CSS rule must specify the class 'active' and be tied to your independent option flag that shows true/false for that option being selected.
2) You MUST have the click handler on the button, BS3 (as I found out) processes the click on the button NOT on the option control, due to how knockout works this HAS to be an inline function, passing in a single parameter, DO NOT be tempted to tie it directly to the computed observable used by the option, it won't work.
3) You must mark the option elements up as shown, that is they must ALL have the same name attribute, the value MUST match what you want that selected option to portray, and must match the strings your button handler is sending
4) Each option element cannot be bound to a simple variable, you need to pass it through a computed observable, not just because of how I'm handling them, but even for simple single Boolean switches it uses "true" & "false" as strings and not as Boolean's as you might expect it.
Once you've marked up your HTML, you then need to build a view model to support it all, in my case I actually did this using typescript then compiled to JS, the code I'm pasting in here is the JS code produced by the TS compiler.
first and foremost you need to make sure you have the following properties on your view model:
this.reject = ko.observable(false);
this.discard = ko.observable(false);
this.hold = ko.observable(false);
this.addheader = ko.observable(false);
(use self, this, me... or what ever it is you use to define your knockout models) the important thing is that they are simple ko observable boolean's
You also need a computed observable that has both a write and a read function:
this.selectedOptionString = ko.computed({
read: function () {
if (this.reject())
return "reject";
if (this.discard())
return "discard";
if (this.hold())
return "hold";
if (this.addheader())
return "addheader";
},
write: function (value) {
console.log("rejectstr:");
console.log(value);
if (value == "reject") {
this.reject(true);
this.discard(false);
this.hold(false);
this.addheader(false);
}
if (value == "discard") {
this.reject(false);
this.discard(true);
this.hold(false);
this.addheader(false);
}
if (value == "hold") {
this.reject(false);
this.discard(false);
this.hold(true);
this.addheader(false);
}
if (value == "addheader") {
this.reject(false);
this.discard(false);
this.hold(false);
this.addheader(true);
}
}
}, this);
This could probably be done a lot more elegantly, but essentially when an option in a group is activated under knockout, knockout takes whatever is in the 'Value' attribute and sends that as a string into your view model.
If you tied this to a simple observable, then that observable would get set to the string value of that attribute. For me however, because I have a series of 4 flags to set that control various states on the UI, a chained if then was appropriate (a switch or possibly a lookup array or hashtable would have worked just as well)
The ultimate outcome of the observable is that one boolean and one only ever be set at a time, and beacuse the CSS in the button is tied to this flag, then the active class gets applied to the given button for which ever is set to true.
For a read, you need to translate your flag state back to a string for knockout to compare to the values it knows about, so the read does the reverse.
For the button click handler, you have to do this inline as shown in the markup, this is beacuse knockout reserves some parameters for automatic things like element name, event info and other's, none of which we need here, so the simplest way is to inline it.
However, in lining it means your not tying to a property and so you can't tie it directly to the computed observable used by the option controls.
Instead what you need to do is add a small stub function to your view model as follows:
SenderDialogViewModel.prototype.changeType = function (newType) {
this.selectedOptionString(newType);
};
This does not need to be observable in any way as it only gets called one way when your button is clicked.
If you want to see the full solution, I'll be making the app this is part of available on my git-hub page free for people to use, but it's not finished yet.
Hope everything above however turns out to be useful for some folk, I have to admit, it turned out to be a little bit more of a challenge than I expected.
Shawty

select a value from telerik ddl using jquery

Is there a way to select a value from telerik ddl using jquery?
Here is my Telerik DDL:
<%= Html.Telerik().DropDownList().Name("Quarter")
.Items(items => {
items.Add().Text("").Value("");
items.Add().Text("Quarter1").Value("Quarter1");
items.Add().Text("Quarter2").Value("Quarter2");
items.Add().Text("Quarter3").Value("Quarter3");
items.Add().Text("Quarter4").Value("Quarter4");
})%>
I am trying the following way, but its doesnt populate the list:
$("#Quarter").val("Quarter2");
Rendered HTML for Telerik DDL after selecting value:
<div tabIndex="0" class="t-widget t-dropdown t-header" id="Quarter" style="width: 104px;" jQuery15103337264984743067="21" value="Quarter2">
<div class="t-dropdown-wrap t-state-default">
<span class="t-input">
Text - Quarter 2
<span class="t-select">
<span class="t-icon t-arrow-down">
<input name="Quarter" id="Quarter" style="display: none;" type="text"/>
http://www.telerik.com/help/aspnet/combobox/combo_client_model.html
I think the problem is that Telerik doesn't even have a hidden <select> to work with (which .val() would be how you'd normally select the value), so you have to use their (cough) stupid methodology to do it. I for one absolutely hate putting any back-end .NET code in front-end JS
Looks like with Telerik you'd have to do something like:
FindItemByValue : Returns the first RadComboBoxItem object whose Value property is equal to the passed parameter.
// Also it doesn't look like your giving your DDL an ID
var combo = <%=RadComboBox1.ClientID %>; // ClientID being whatever your ID is
combo.FindItemByValue("Quarter2");
// or
combo.SetValue("Quarter2");
try this one:
var value = $('#Quarter').data('tDropDownList').value();

Categories