How can you select code nodes that were added through templates? - javascript

So I am using the template method to dynamically add HTML content in a page, and I want to change the value of an input through an event listener.
Here's a completely random snippet of code as an example (it's nonsensical on purpose):
favoriteElement += `<div class="favorite__page JS-favoritePage">
<p id="JS-amountOfFavorites">Quantity of saved pages : ${amount}</p>
<input type="number" class="favoritesQuantity" name="amountOfFavorites" min="1" max="100" value="${value}">
</div>`
So let's say that I want to have access to the value of the input, I'll declare a variable and get it through their query selector :
let inputFavoritesQuantity = document.querySelector('input [class="favoritesQuantity"]');
Now I'll add an event listener:
inputFavoritesQuantity.addEventListener("input", function(e){
let valueOfInput = e.target.value;
//Other code
}
Though the problem is that I do not have access to the input because it's added with a template, so it gives an error Uncaught TypeError: Cannot read properties of null (reading 'addEventListener')
I could add everything by hand using the properties createElement,setAttribute,appendChild...
But it would make the code VERY long and difficult to maintain! (without even considering the fact on my code project I'd have to add 5 nested elements which have 5 attributes each!)
Is there another efficient method to have access to an element with templates?

The DOMParser compiles strings into a document. You need to access the
documentElement in order to add to the existing dom. Here's an example of use
let amount = 100
let value = 50
favoriteElement = `<div class="favorite__page JS-favoritePage">
<p id="JS-amountOfFavorites">Quantity of saved pages : ${amount}</p>
<input type="number" name="amountOfFavorites" min="1" max="100" value="${value}" />
</div>`
// This converts the string and gets the documentElement.
var node = new DOMParser().parseFromString(favoriteElement, "text/html").documentElement
//Now we are working with an actual element and not a string of text.
let inputFavoritesQuantity = node.querySelector('input [class="favoritesQuantity"]');
node.addEventListener("input", function(e){
let valueOfInput = e.target.value;
console.log('value changed', valueOfInput);
})
var outputDiv = document.getElementById('content')
outputDiv.appendChild(node);
<div id="content">
</div>

Related

bind Knockoutjs arrays to Bootstrap slider

I'm trying to implement a bootstrap slider in a webapplication and use knockoutjs for the data handling.
Javascript
$(document).ready(() => {
function ViewModel() {
singleValue = ko.observable(20)
arrayValues = ko.observableArray([20, 50])
}
ko.applyBindings(ViewModel)
})
HTML
<input
type ='text'
data-slider-min = 0
data-slider-max = 100
data-slider-step = 1
data-slider-tooltip ='hide'
data-provide ='slider'
data-slider-value = 20
data-bind = 'value: singleValue'
/>
<p data-bind='text: singleValue'></p>
That works perfectly fine. If I change the value of the slider, the value in p is changing accordingly.
<input
type ='text'
data-slider-min = 0
data-slider-max = 100
data-slider-step = 1
data-slider-tooltip ='hide'
data-provide ='slider'
data-slider-value = [20, 40]
data-bind = 'value: arrayValues'
/>
<p data-bind='text: arrayValues()'></p>
// output is 20,50, so knockout is taking precedence,
// because I set the initial value to [20, 40] in html
If I change the value of the slider, it seems the values are changing accordingly. But it's not an array anymore, its a string value. But the minimum and maximun values are changing correctly with the slider.
<p data-bind='text: arrayValues()[0]'></p>
Here the output is 20 when site is rendered. Which is what I expected.
But as soon as I start to move the slider, the array is becoming a string and the output changes to 2, which is the first char of 20,40.
Does anyone knows what I'm doing wrong?
I believe the value binding for <input>, <textarea> etc. really just understands string values. This is not true for <select>, which accepts arbitrary data types, even objects.
My suggestion would be to work-around the problem. In the example below, I use a writable computed to parse '20,40' into an array of the form you described. You might just provide such a computed using data-bind = 'value: val'.
let _val = ko.observable([20, 50]);
let val = ko.computed({
read: () => _val(),
write: v => {
_val(v.split(',').map(n => parseInt(n)));
},
});
console.log(val());
val('20,40');
console.log(val());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>

How should i display number in HTML for later calculation in javascript

I am trying to figure out how to display number a right and efficient way for later calculation in HTML. This is what i can think of right now but doesn't seems right.
<p class = "price"> <span class ="sign">$</span> 10 </p>
Later implementation includes
$("p.price") * (the desire currency rate being called)
It then updates the whole page with the p.price
Consider using data attributes:
<p class="price" data-usd-price="10"> any markup you want </p>
You can then format it however you like and access the raw value later with:
$("p.price").data("usd-price")
Here a bit more complicated example:
<p class="price" data-usd-price="10">foo<span class="converted"></span></p>
<p class="price" data-usd-price="30">bar<span class="converted"></span></p>
<p class="price" data-usd-price="49.99">buzz<span class="converted"></span></p>
<p class="price" data-usd-price="99.99"><span class="converted"></span></p>
$('p.price').each(function () {
$(this)
.children('span.converted')
.html(
$(this).data('usd-price') * 22
)
})
The selector $("p.price") will give you an array of all paragraph elements with the class price. So your first issue is that you need to be aware of that, and your current multiplication code is not.
Second, you're trying to multiply the elements rather than the value of the one element.
Third, the value will be a string and you need a number.
I'd try something like:
<p class="price"><span>$</span><span class="amount">10</span>
Then your JS could look like this (minus smart error checking and optimization and such)
var amount = parseFloat($("span.amount:first").text(), 10);
$("span.amount:first").text(amount * exchangeRate);
Try to loop through paragraph children and check, if nodeName of the children is text then parse it's wholeText
var pContent = $('.price')[0].childNodes,
elem, num;
$.each(pContent, function (i, e) {
elem = $(e)[0];
if (elem && elem.nodeName == "#text") {
num = parseInt(elem.wholeText);
}
})
console.log(num)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<p class = "price"> <span class ="sign">$</span> 10</p>
when the page load the span is left empty but i want it to be shown (GBP as the base)
Simply change the spans text on window load instead of onchange event
var selectedIndex = select.selectedIndex;
$('.sign').text(prefix[selectedIndex ]);
$('.converted').text(currency[selectedIndex ] * $(price).data('price'));
Also i have some notes, if you have just one element you don't need to implement each function , and you don't need to make loop on each change as selectedIndex will filter the option which has selected attribute. http://jsfiddle.net/whoq9zd0/2/

Meteor creating dynamic checkboxes

So i want to create a certain number of check boxes based on the "timesPerWeek" value. The issue I'm having is that when trying to createElement I get Cannot read property 'createElement' of null. Pretty sure it is because the JS renders Before the HTML but i can't figure out what needs to be done to make it so that I can call my function to make the check boxes. This code currently doesn't depend on timesPerweek because I just trying to get a checkbox to show up first.
JS
'pie':function()
{
var progress = document.getElementById('progress');
var stuff= progress.createElement('input');
//var stuff = progress.createElement('input');
stuff.type = "checkbox";
stuff.name = "1";
stuff.value = "set1";
stuff.id = "stuff";
var label = progress.createElement('label');
label.htmlFor = "stuff";
label.appendChild(document.createTextNode('pie'));
progress.appendChild(stuff);
progress.appendChild(label);
}
HTML
<div id="progress" class = "col-md-4">
progress: {{timesPerWeek}}
<br/>
{{pie}}
</div>
I should be able to create elements under progress but it just dies with the error stated above.
The correct way to do this in Meteor is to use an #each helper:
<div id="progress">
{{#each timesPerWeek}}
<input type="checkbox" name="checkbox{{#index}}">
{{/each}}
</div>
That should get you several checkboxes to show up.

Angular JS - ng-repeat repeating old values

I am new to Angular JS. I have created a code in angular using app and controller. What I am tyring to do is to add name dynamically to a array when a button is clicked.
By default my array has two value passed. When i give an input and click the add button,it adds the string for the first time.
But when i give another input and click add again, the old string is replaced by the new string and the new string is added again.
Here is the piece of code on JSFiddle: http://jsfiddle.net/5DMjt/3680/
var demo= angular.module("demo",[]);
var simplecontroller = function($scope){
$scope.members = [{id:1,name:'prateek'},{id:2,name:'Ruchi'}];
$scope.addmember = function(newmember){
newmember.id = $scope.members.length+1;
$scope.members.push(newmeber);
demo.controller(simplecontroller);
}
}
and here is the HTML Code:
<div ng-app="demo">
<div ng-controller="simplecontroller">
<ul>
<li ng-repeat="member in members">{{member.id}}-{{member.name}}</li>
</ul>
Name<input type="Text" ng-model="inputmember.name">
</br><h2>
{{inputmember}}
</h2>
<input type="button" value="Add" ng-click="addmember(inputmember)">
</div>
</div>
Please Help !
What i analyzed is that push function is passing the address that is why binding still exists.What u can do is pass the value instead like i did below-:
$scope.addmember = function(newmember){
newmember.id = $scope.members.length+1;
$scope.members.push({id:newmember.id,name:newmember.name});
demo.controller(simplecontroller);
}
Hope this solves your problem.Happy learning :)
You have two options.
Either you can reinitialize it every time what I would not recommend.
And the other one is to, pass the parameters with values.
$scope.members.push({id:newmember.id,name:newmember.name});
:)
See this updated fiddle: http://jsfiddle.net/5DMjt/3689/
Name<input type="Text" ng-model="newname">
This gives you a scope variable newname.
<input type="button" value="Add" ng-click="addmember()">
And addmember function uses this newname to create a new object and add it to the list:
$scope.addmember = function(){
var newmember = {};
newmember.id = $scope.members.length+1;
newmember.name = $scope.newname;
$scope.members.push(newmember);
}
You have a syntax error. See console error for more info.
Your variable inputmember is not defined anywhere.
Also you need to push to array new reference of the object, so the old one in array does not change each time you type value.
Here is a working version.
http://jsfiddle.net/a9zvgm8k/
$scope.addmember = function(newMember){
newMember.id = $scope.members.length+1;
$scope.members.push(angular.extend({}, newMember));
demo.controller(simplecontroller);
}
$scope.members = $scope.members.concat({id: newmember.id, name: newmember.name});
Solved : http://jsfiddle.net/5DMjt/3693/
Before pushing to $scope.members you need to create a new object and populate it with id and name from the input.

Get next textarea outside div without id

I need to get the next textarea, but I'm not being able with next or even find.
Sample code HTML:
<div>
<div class="guides_chapters_button" onclick="surroundtest('[center]', '[/center]');">Center</div>
<div class="guides_chapters_button" style="text-decoration: underline;" onclick="surround('[u]', '[/u]');">u</div>
</div>
<textarea class="guides_chapters_textarea" id="textareamatch" name="matchupm" rows="7" cols="25"></textarea>
JS:
window.surround = function surround(text2,text3){
$("#textareamatch").surroundSelectedText(text2, text3);
}
function surroundtest(text2,text3){
var c = $(this).parent().next('textarea');
c.surroundSelectedText(text2, text3);
}
JS FIDDLE: http://jsfiddle.net/qmpY8/1/
What I need working is surroundtest, the other is an example working but using the id. I would love to replace that one because Im usinc cloned objects.
The this statement in surroundtest applies to the window object and not the element. What you should do is to change the function definition as so:
function surroundtest(element, text2,text3){
var c = $(element).parent().next('textarea');
...
}
And the HTML accordingly:
<div class="guides_chapters_button" onclick="surroundtest(this, '[center]', '[/center]');">Center</div>
If this is the HTML you are going with, then .closest() can also be used to get the textarea element.Like below:
var c = $(element).parent().closest('textarea');

Categories