How can I calculate a price by which h3 is selected? - javascript

So I have this div and this function to add a class when one of the sizes is selected.
function Selected(n) {
var i;
var price;
var x = document.getElementsByClassName("SizesId");
for (i = 0; i < x.length; i++) {
document.getElementById("size" + i).style.backgroundColor = "#c6c6c6";
document.getElementById("size" + i).style.color = "black";
document.getElementById("size" + i).className = "SizesId";
}
document.getElementById(n).style.backgroundColor = "#666";
document.getElementById(n).style.color = "white";
document.getElementById(n).className += " selected";
}
<div class="Sizes">
<h3 class="SizesId" onclick="Selected('size0')" id="size0">5</h3>
<h3 class="SizesId" onclick="Selected('size1')" id="size1">6</h3>
<h3 class="SizesId" onclick="Selected('size2')" id="size2">7</h3>
<h3 class="SizesId" onclick="Selected('size3')" id="size3">8</h3>
</div>
How can I change the value of this :- <h2 id="pretFinal">Pret: 150 RON</h2>
For example if the size 5 is selected the price will be "Price: 100 RON" , if the size 6 is selected the price will be "Price: 120 RON" etc.
I can't get it to work, any help is appreciated.

To update the text of the given element, that of #pretFinal the following would work:
// retrieves the required element via its id property:
document.getElementById('pretFinal')
// updates the text-content of that element (assuming
// it exists; if no element is found this will result
// in an error):
.textContent =
// here we concatenate the strings:
'Pret: ' +
// the outer-most parentheses prevent the
// wrapped calculation from concatenated as
// a String, allowing the result of the
// calculation to be concatenated:
(
// this parenthesis allows the result of
// the innermost calculation to be
// multiplied by the 20 (given that
// the order of precedence in math
// would otherwise perform the
// multiplication before the addition):
(
// using the '+' to coerce the string,
// after replacing all ('g') its non-numeric
// characters ('\D' ) with an empty space,
// to a number to which 5 is added:
+n.replace(/\D/g, '') + 5
)
* 20
// finally we add the remainder of the String to be set:
) + ' RON';
function Selected(n) {
var i, price,
x = document.getElementsByClassName("SizesId");
for (i = 0; i < x.length; i++) {
document.getElementById("size" + i).style.backgroundColor = "#c6c6c6";
document.getElementById("size" + i).style.color = "black";
document.getElementById("size" + i).className = "SizesId";
}
document.getElementById(n).style.backgroundColor = "#666";
document.getElementById(n).style.color = "white";
document.getElementById(n).className += " selected";
document.getElementById('pretFinal').textContent = 'Pret: ' + (((+n.replace(/size/gi, '') + 5) * 20)) + ' RON';
}
<h2 id="pretFinal">Pret: 150 RON</h2>
<div class="Sizes">
<h3 class="SizesId" onclick="Selected('size0')" id="size0">5</h3>
<h3 class="SizesId" onclick="Selected('size1')" id="size1">6</h3>
<h3 class="SizesId" onclick="Selected('size2')" id="size2">7</h3>
<h3 class="SizesId" onclick="Selected('size3')" id="size3">8</h3>
</div>
JS Fiddle demo.
While the above approach works, so far as you've described your requirements in your question, it is a poor approach to take: in-line JavaScript results in difficult-to-manage, maintain and update code. A better approach is to use unobtrusive JavaScript, in which event-handlers are bound using JavaScript itself, which in your case leads to the following. Also, worth noting, is that CSS is far better – and gives a better interface in many situations – than using JavaScript to apply new styles. So the following approach also uses CSS to supply the styling:
// using the same named-function, but note that we
// are not explicitly passing in any variables:
function Selected() {
// defining the variables for use:
var i, price,
x = document.getElementsByClassName("SizesId"),
// 'this' is passed in automatically from the later
// use of EventTarget.addEventListener():
clicked = this,
// finding any currently-selected element, the one
// that has both the 'SizesID' and 'selected' classes,
// using document.querySelector() (since there should)
// be only one element that has both classes):
existing = document.querySelector('.SizesId.selected');
// if an element exists with both classes:
if (existing) {
// we remove the 'selected' class-name using
// the HTMLElement.classList API:
existing.classList.remove('selected');
}
// adding the 'selected' class-name to the
//clicked element:
clicked.classList.add('selected');
// setting the textContent of the element identified
// via the id of 'pretFinal', setting it to the string
// of the text-content from the clicked element (again using
// the + to coerce to a number) and then multiplying that with
// '20' (which again seems to be what you want):
document.getElementById('pretFinal').textContent = (+clicked.textContent * 20);
}
// finding the elements with the 'SizesId' selector, converting that
// non-live NodeList to an Array and then iterating over that Array
// using Array.prototype.forEach():
Array.from(document.querySelectorAll('.SizesId')).forEach(
// using an Arrow function, to the currently-selected
// Array element, of the Array of elements, 'el' to use
// EventTarget.addEventListener() to bind a 'click'
// event-listener which will run the named function
// (here 'Selected') and note the lack of parentheses:
el => el.addEventListener('click', Selected)
);
The CSS for the following is:
/* setting the styles for the elements
of this class-name: */
.SizesId {
color: #000;
background-color: #c6c6c6;
}
/* setting the styles for the 'selected'
elements with each of the class-names
below: */
.SizesId.selected {
color: #fff;
background-color: #666;
}
/* this sets the text of 'Pret'
as the text of the ::before
pseudo-element (which means
it doesn't need to be present
in the HTML, making the String
concatenation/generation far
easier): */
#pretFinal::before {
content: 'Pret: ';
}
/* this sets the text of 'RON'
as the text-content of the
::after pseudo-element: */
#pretFinal::after {
content: ' RON';
}
function Selected() {
var i, price,
x = document.getElementsByClassName("SizesId"),
clicked = this,
existing = document.querySelector('.SizesId.selected');
if (existing) {
existing.classList.remove('selected');
}
clicked.classList.add('selected');
document.getElementById('pretFinal').textContent = (+clicked.textContent * 20);
}
Array.from(document.querySelectorAll('.SizesId')).forEach(
el => el.addEventListener('click', Selected)
);
.SizesId {
color: #000;
background-color: #c6c6c6;
}
.SizesId.selected {
color: #fff;
background-color: #666;
}
#pretFinal::before {
content: 'Pret: ';
}
#pretFinal::after {
content: ' RON';
}
<h2 id="pretFinal">150</h2>
<div class="Sizes">
<h3 class="SizesId" id="size0">5</h3>
<h3 class="SizesId" id="size1">6</h3>
<h3 class="SizesId" id="size2">7</h3>
<h3 class="SizesId" id="size3">8</h3>
</div>
JS Fiddle demo.

Use this:
var val = document.getElementById(n).innerHTML;
document.getElementById('pretFinal').innerHTML = 'Price: ' + val * 20 + ' RON';

You can use switch statement and use document.getElementById('id').innerHTML to change its value
function Selected(n) {
var i;
var price;
switch (n) {
case 'size0' :
price = '100';
break;
case 'size1' :
price = '120';
break;
case 'size2' :
price = '140';
break;
case 'size3' :
price = '160';
break;
}
var x = document.getElementsByClassName("SizesId");
for(i = 0; i < x.length; i++)
{
document.getElementById("size" + i).style.backgroundColor = "#c6c6c6";
document.getElementById("size" + i).style.color = "black";
// document.getElementById("size" + i).className = "SizesId";
}
document.getElementById(n).style.backgroundColor = "#666";
document.getElementById(n).style.color = "white";
document.getElementById(n).className += " selected";
document.getElementById("pretFinal").innerHTML = 'Pret: '+price+' RON';
//SelectedColor(-1);
}
<div class="Sizes">
<h3 class="SizesId" onclick="Selected('size0')" id="size0">5</h3>
<h3 class="SizesId" onclick="Selected('size1')" id="size1">6</h3>
<h3 class="SizesId" onclick="Selected('size2')" id="size2">7</h3>
<h3 class="SizesId" onclick="Selected('size3')" id="size3">8</h3>
</div>
<h2 id="pretFinal">Pret: 150 RON</h2>

Use this:
document.getElementById("pretFinal").innerHTML = "YOUR TEXT"

Related

Why does Javascript require event listeners to be functions that return functions?

I'm trying to generate a grid that responds to clicks and mouseover events. I have a tile manager object that holds tile objects, each of which have their own event listener. Currently, the actual method that gets called is in the manager object, though I would prefer they exist in the tile objects.
I would like to either find a solution more elegant than the one I currently know works, or at least understand why this solution even works at all when others do not:
for reference and clarity, cell refers to the javacript object and cell_node refers to a DOM object in the cell, additionally _handleClick and click_callback are the same function
Currently, to see if a callback happened, I have defined my callback as:
_handleClick(el,r,c,i){
console.log("element:" + el)
console.log("row:" + r)
console.log("col:" + c)
console.log("index:" + i)
}
Where _handleClick(el,r,c,i) is located in the manager object, not the tiles.
The addEventListener method looks like this:
cell_node.addEventListener('click',
(function(el,r,c,i){
return function(){
click_callback(el,r,c,i);
}
})(cell,r,c,i),false);
I cannot even begin to understand why this is necessary, or why it works when attempts such as:
cell_node.addEventListener('click', cell.some_clicked_function_with_no_arguments)
do not work. If I define cell.some_clicked_function_with_no_arguments with the this keyword, everything prints out as undefined, leaving me even more confused.
The two methods are posted in full below
class gridTile{
constructor(row, col, tile_id){
this.text= row + ", " + col;
this.child_text_div = this._make_child_text_div(this.text);
this.element = this._make_root_element()
this.row = row;
this.col = col;
this.id = tile_id;
}
_get_root_style(){
var randomColor = '#'+(Math.random() * 0xFFFFFF << 0).toString(16).padStart(6, '0');
return `
width: 100%;
padding-top: 100%;
//height: ${this.height}%;
background-color: ${randomColor};
position: relative;
`;
}
_make_child_text_div(text){
var cssobject =`
width: 100%;
height: 100%;
top: 0;
left: 0;
position: absolute;
`;
var obj = document.createElement("div");
obj.setAttribute("style", cssobject);
obj.innerHTML = text;
return obj;
}
_make_root_element(){
var root_grid_div = document.createElement("div");
root_grid_div.setAttribute("style", this._get_root_style());
root_grid_div.appendChild(this.child_text_div);
return root_grid_div;
}
get_element(){return this.element;}
clicked_callback(cell){
return function(cell){
console.log("element:" + cell.element);
console.log("row:" + cell.row);
console.log("col:" + cell.col);
console.log("index:" + cell.tile_id);
}
};
}
class Grid{
constructor(rows, columns){
this.grid_div = this._generate_grid_div(rows, columns, this._handleClick);
}
_generate_grid_div(rows, cols, click_callback){
// generate styling row and columns
var row_percent = String(1/rows * 100) + "% ";
var col_percent = String(1/cols * 100) + "% ";
console.log(row_percent + ", " + col_percent);
var rowstyle = "grid-template-rows: ";
for (var i=0; i<rows; i++){
rowstyle += row_percent
}
var colstyle = "grid-template-columns: ";
for (var i=0; i<cols; i++){
colstyle += col_percent
}
var style = `
display: grid;
${rowstyle};
${colstyle};
`
var grid = document.createElement('div');
grid.className = 'grid';
grid.setAttribute("style", style)
var i=0;
for (var r=0;r<rows;++r){
for (var c=0;c<cols;++c){
var cell = new gridTile(r, c, i);
var cell_node = grid.appendChild(cell.get_element())
cell_node.addEventListener('click',
(function(el,r,c,i){
return function(){
click_callback(el,r,c,i);
}
})(cell,r,c,i),false);
cell_node.addEventListener('mouseenter', click_callback(cell,r,c,i));
++i;
}
}
return grid;
}
_handleClick(el,r,c,i){
console.log("element:" + el)
console.log("row:" + r)
console.log("col:" + c)
console.log("index:" + i)
}
get_grid_element(){
return this.grid_div;
}
}
Wrapping the addEventListener() call in the IIFE instead might make it easier to read for you. As mentioned in comment above there isn't enough shown to know if this can be refactored to remove the IIFE
The following will act exactly the same:
(function(el, r, c, i) {
cell_node.addEventListener('click', function() {
click_callback(el, r, c, i);
}, false);
})(cell, r, c, i)
This ended up getting solved by adding an arrow function within a separate method:
_make_root_element(){
var root_grid_div = document.createElement("div");
root_grid_div.setAttribute("style", this._get_root_style());
root_grid_div.appendChild(this.child_text_div);
this._add_listeners_to_root_element(root_grid_div);
return root_grid_div;
}
get_element(){return this.element;}
_add_listeners_to_root_element(el){
el.addEventListener('click', () => this._clicked_callback());
}
_clicked_callback(){
console.log("element:" + this.element);
console.log("row:" + this.row);
console.log("col:" + this.col);
console.log("index:" + this.id);
};
This time, the this is not the element that called the event, but is now within the scope of the object the arrow function was defined in. This post helped me quite a bit in understanding what was going on, when other posts did not do so. This is more a reflection of my lack of ability to understand rather than the efforts of others to explain it to me.

replace getElementById with getElementsByClassName

I would like to mod the JavaScript below to select by class name, rather than element id:
https://github.com/sarathsaleem/grained/blob/master/grained.js
and then ideally initialize, like so:
grained(".classname", options);
This modified version of Sarath Saleem's grained.js gets all elements with a given class name selector (i.e. '.foo') and gives them the specified noise background. I have not tested this code very much, so it is definitely not production quality in terms of efficiency. The key is just to change the element variable to elements and use document.getElementsByClassName where the, newly named, elements variable is defined. This will return an HTMLCollection of all the HTML elements with the given class (i.e. 'foo'). Then, in the part where the element's position and overflow attributes are set, use a for loop and iterate through the HTMLCollection to set the position and overflow attributes for each of the found elements with the given class.
Note: At the bottom of the grained.js file is where I call the grained function.
Also, I adjusted the places that used, previously named, elementId variable to use a new elementClass variable that is based off the given class name.
You can do this anywhere after the grained.js iife as it is in the global namespace after that.
/*! Grained.js
* Author : Sarath Saleem - https://github.com/sarathsaleem
* MIT license: http://opensource.org/licenses/MIT
* GitHub : https://github.com/sarathsaleem/grained
* v0.0.1
*/
(function (window, doc) {
"use strict";
function grained(ele, opt) {
var elements = null,
elementClass = null,
selectorElement = null;
//1
if (typeof ele === 'string') {
elements = doc.getElementsByClassName(ele.split('.')[1]);
}
//1
if (!elements) {
console.error('Grained: cannot find any elements with class ' + ele);
return;
} else {
elementClass = elements[0].className;
}
var elementsLength = elements.length
for( var i = 0; i < elementsLength; i++) {
//set style for parent
if (elements[i].style.position !== 'absolute') {
elements[i].style.position = 'relative';
}
elements[i].style.overflow = 'hidden';
};
var prefixes = ["", "-moz-", "-o-animation-", "-webkit-", "-ms-"];
//default option values
var options = {
animate: true,
patternWidth: 100,
patternHeight: 100,
grainOpacity: 0.1,
grainDensity: 1,
grainWidth: 1,
grainHeight: 1,
grainChaos: 0.5,
grainSpeed: 20
};
Object.keys(opt).forEach(function (key) {
options[key] = opt[key];
});
var generateNoise = function () {
var canvas = doc.createElement('canvas');
var ctx = canvas.getContext('2d');
canvas.width = options.patternWidth;
canvas.height = options.patternHeight;
for (var w = 0; w < options.patternWidth; w += options.grainDensity) {
for (var h = 0; h < options.patternHeight; h += options.grainDensity) {
var rgb = Math.random() * 256 | 0;
ctx.fillStyle = 'rgba(' + [rgb, rgb, rgb, options.grainOpacity].join() + ')';
ctx.fillRect(w, h, options.grainWidth, options.grainHeight);
}
}
return canvas.toDataURL('image/png');
};
function addCSSRule(sheet, selector, rules, index) {
var ins = '';
if (selector.length) {
ins = selector + "{" + rules + "}";
} else {
ins = rules;
}
if ("insertRule" in sheet) {
sheet.insertRule(ins, index);
} else if ("addRule" in sheet) {
sheet.addRule(selector, rules, index);
}
}
var noise = generateNoise();
var animation = '',
keyFrames = ['0%:-10%,10%', '10%:-25%,0%', '20%:-30%,10%', '30%:-30%,30%', '40%::-20%,20%', '50%:-15%,10%', '60%:-20%,20%', '70%:-5%,20%', '80%:-25%,5%', '90%:-30%,25%', '100%:-10%,10%'];
var pre = prefixes.length;
while (pre--) {
animation += '#' + prefixes[pre] + 'keyframes grained{';
for (var key = 0; key < keyFrames.length; key++) {
var keyVal = keyFrames[key].split(':');
animation += keyVal[0] + '{';
animation += prefixes[pre] + 'transform:translate(' + keyVal[1] + ');';
animation += '}';
}
animation += '}';
}
//add animation keyframe
var animationAdded = doc.getElementById('grained-animation');
if (animationAdded) {
animationAdded.parentElement.removeChild(animationAdded);
}
var style = doc.createElement("style");
style.type = "text/css";
style.id = 'grained-animation';
style.innerHTML = animation;
doc.body.appendChild(style);
//add custimozed style
var styleAdded = doc.getElementById('grained-animation-' + elementClass);
if (styleAdded) {
styleAdded.parentElement.removeChild(styleAdded);
}
style = doc.createElement("style");
style.type = "text/css";
style.id = 'grained-animation-' + elementClass;
doc.body.appendChild(style);
var rule = 'background-image: url(' + noise + ');';
rule += 'position: absolute;content: "";height: 300%;width: 300%;left: -100%;top: -100%;';
pre = prefixes.length;
if (options.animate) {
while (pre--) {
rule += prefixes[pre] + 'animation-name:grained;';
rule += prefixes[pre] + 'animation-iteration-count: infinite;';
rule += prefixes[pre] + 'animation-duration: ' + options.grainChaos + 's;';
rule += prefixes[pre] + 'animation-timing-function: steps(' +options.grainSpeed + ', end);';
}
}
//selecter element to add grains
selectorElement = '.' + elementClass + '::before';
addCSSRule(style.sheet, selectorElement, rule, 0);
}
window.grained = grained;
//END
})(window, document);
grained('.foo', {});
<!doctype html>
<html>
<head>
<title>Example</title>
<style>
.foo {
height: 100px;
width: 100px;
}
</style>
</head>
<body>
<div class="foo">
<div class="contents">
<p>Hello World</p>
</div>
</div>
<p>No Noise For Me!</p>
<div class="foo">
<div class="contents">
<p>Hello World</p>
</div>
</div>
<script type="text/javascript" src="grained.js"></script>
<script type="text/javascript" src="index.js"></script>
</body>
</html>
Hope this helps! If you have any questions, please ask! I like to help :D.

how to access innerHTML of button created dynamically through JavaScript

This is a JavaScript function used for creating dynamic buttons. I want to access innerHTML of button.
function mynumber() {
var i,j = 1, num = 0; # i used for making three buttons in row and j used for repeat same process no of time and num is int text on button
do {
for (i = 1; i <= 3; i++) {
var btn = document.createElement("BUTTON"); # create button using javascript
var txt = document.createTextNode(num++); # creat text on button
btn.appendChild(txt); # attached text on button
btn.id = ('obj'+ k++) ;
document.getElementById("btnsize").appendChild(btn); # atache button with text in div
}
var next = document.createElement("BR");
document.getElementById("btnsize").appendChild(next);
j++;
}
while(j<4)
}
("btn").click(function()
{
var val = document.getElementById(this.id).innerHTML;
document.getElementById("demo").value = val;
})
You can apply css class to the button as shown in https://jsfiddle.net/3u71kzrh/
Declare a style as below in your stylesheet or between the <style> and </style> tag in the html <head>.
.aClassName{
width:40px;
height:40px;
}
Add the following line just below the code where you create the button element.
btn.className = "aClassName";
use below script in your script code where "100" is variable whatever you want to give but that would be a number ;)
btn.style.width="100px"
btn.style.height="100px"
First of all, I'd change your <button>-creation script to the following:
function mynumber () {
// a reference to the element to which we want
// to add the newly-created <button> elements:
var appendTo = document.getElementById('btnsize'),
// creating a document fragment to contain
// the elements to be added; to prevent the
// document from redrawing every time an
// element is added:
fragment = document.createDocumentFragment(),
// creating a <button> element:
button = document.createElement('button'),
// creating a <br> element:
br = document.createElement('br'),
// initialising an empty variable, for
// use within the loop:
clone;
// you want nine buttons, so use one loop
// that will run nine times:
for (var i = 0; i < 9; i++) {
// using the empty variable to
// hold the cloned <button> element:
clone = button.cloneNode();
// setting the text of the <button>
// to the current index/iteration
// (i) of the loop:
clone.textContent = i;
// appending the cloned <button>
// to the document fragment:
fragment.appendChild(clone);
// we want rows of three, but
// JavaScript is zero-based, so
// we add 1 to i, and check the
// remainder of the division by
// 3; if it's zero the number is
// evenly-divisible by 3 therefore
// it's time to add a <br>:
if ((i+1)%3 === 0) {
// appendin a cloned <br> element:
fragment.appendChild(br.cloneNode())
}
}
// appending the fragment to the
// container element:
appendTo.appendChild(fragment);
}
// calling the function:
mynumber();
function mynumber() {
var appendTo = document.getElementById('btnsize'),
fragment = document.createDocumentFragment(),
button = document.createElement('button'),
br = document.createElement('br'),
clone;
for (var i = 0; i < 9; i++) {
clone = button.cloneNode();
clone.textContent = i;
fragment.appendChild(clone);
if ((i + 1) % 3 === 0) {
fragment.appendChild(br.cloneNode())
}
}
appendTo.appendChild(fragment);
}
mynumber();
<div id="btnsize"></div>
External JS Fiddle demo, for experimentation.
But, because I like to be able to change things – and this also helps address your sizing requirement – I'd amend that to the following, which allows you to call the function, and supply some user-defined options:
// the 'opts' is an Object, containing
// user-defined settings:
function mynumber(opts) {
// these are the defaults:
var settings = {
'container': 'btnsize',
'elements': 'button',
'elementClass': 'newButton',
'height': 'equal',
'width': 'equal',
'rows': 3,
'cols': 3
};
// here we iterate over the properties in the
// opts Object, using for...in:
for (var property in opts) {
// if the object's property is user-defined:
if (opts.hasOwnProperty(property)) {
// we update the settings property to
// match (note: no sanity-checking):
settings[property] = opts[property];
}
}
// finding the element to which we're appending the
// content; using the settings.container property
// (which should contain the 'id' of the element,
// as we're using document.getElementById()):
var appendTo = document.getElementById(settings.container),
fragment = document.createDocumentFragment(),
// 'button' is perhaps a misnomer, since we're creating
// whatever element the user defined, with the
// settings.elements property:
button = document.createElement(settings.elements),
br = document.createElement('br'),
clone;
// if there is a class-name set for the created elements,
// we set that on the created-element (which will be
// copied to all clones, in the loop):
if (settings.elementClass) {
button.classList.add(settings.elementClass);
}
// if the settings.height property is not 'equal', and
// the settings.height can be parsed as a number (a
// naive check, and we're not validating units):
if (settings.height !== 'equal' && parseFloat(settings.height)) {
// we set the height of the created element to
// the value of the settings.height property:
button.style.height = settings.height;
}
// As above, but for height:
if (settings.width !== 'equal' && parseFloat(settings.width)) {
button.style.width = settings.width;
}
// iterating from 0 to the result of multiplying the
// required number of rows by the required number of columns:
for (var i = 0, len = (settings.rows * settings.cols); i < len; i++) {
clone = button.cloneNode();
clone.textContent = i;
fragment.appendChild(clone);
if ((i + 1) % settings.cols === 0) {
fragment.appendChild(br.cloneNode())
}
}
appendTo.appendChild(fragment);
// To avoid running the same test, in subsequent
// if assessments, we create a couple of Booleans
// to test whether settings.width, or
// settings.height, were set to the string 'equal':
var widthEqual = settings.width === 'equal',
heightEqual = settings.height === 'equal';
// if either of those assessments returned true:
if (widthEqual || heightEqual) {
// we find all the elements contained within the
// appended node (to which the elements were
// appended) that have the tagName of the
// created elements:
var appended = appendTo.getElementsByTagName(settings.elements),
// we get the width, and height, of the last
// of those elements - on the assumption that
// that element will have the highest, and
// therefore longest/largest number:
width = appended[appended.length - 1].clientWidth,
height = appended[appended.length - 1].clientHeight;
// if both settings.width and settings.height were
// set to 'equal':
if (widthEqual && heightEqual) {
// we use Function.prototype.call(), to apply
// Array.prototype.forEach() to the Array-like
// NodeList returned by document.getElementsByTagName()
// (from earlier):
Array.prototype.forEach.call(appended, function (btn) {
// btn - the first (and, here, only) argument
// to the anonymous function is the array-
// element over which we're iterating:
// setting the width and height of the
// current element to be equal to the
// found width/height of the last
// element:
btn.style.width = width + 'px';
btn.style.height = height + 'px';
});
// otherwise, if only the width was 'equal':
} else if (widthEqual) {
// As above, setting only the width:
Array.prototype.forEach.call(appended, function (btn) {
btn.style.width = width + 'px';
});
// As above, addressing only the height:
} else if (heightEqual) {
Array.prototype.forEach.call(appended, function (btn) {
btn.style.height = height + 'px';
});
}
}
}
// calling the function, using only the defaults:
mynumber();
// calling the function, setting
// custom values:
mynumber({
'container' : 'anotherElement',
'rows' : 5,
'cols': 4,
'elementClass': 'arbitraryClassName'
});
// calling the function, this time
// explicitly setting the height
// and width:
mynumber({
'container' : 'more',
'elements' : 'span',
'width' : '3em',
'height' : '2em'
});
function mynumber(opts) {
var settings = {
'container': 'btnsize',
'elements': 'button',
'elementClass': 'newButton',
'height': 'equal',
'width': 'equal',
'rows': 3,
'cols': 3
};
for (var property in opts) {
if (opts.hasOwnProperty(property)) {
settings[property] = opts[property];
}
}
var appendTo = document.getElementById(settings.container),
fragment = document.createDocumentFragment(),
button = document.createElement(settings.elements),
br = document.createElement('br'),
clone;
if (settings.elementClass) {
button.classList.add(settings.elementClass);
}
if (settings.height !== 'equal' && parseFloat(settings.height)) {
button.style.height = settings.height;
}
if (settings.width !== 'equal' && parseFloat(settings.width)) {
button.style.width = settings.width;
}
for (var i = 0, len = (settings.rows * settings.cols); i < len; i++) {
clone = button.cloneNode();
clone.textContent = i;
fragment.appendChild(clone);
if ((i + 1) % settings.cols === 0) {
fragment.appendChild(br.cloneNode())
}
}
appendTo.appendChild(fragment);
var widthEqual = settings.width === 'equal',
heightEqual = settings.height === 'equal';
if (widthEqual || heightEqual) {
var appended = appendTo.getElementsByTagName(settings.elements),
width = appended[appended.length - 1].clientWidth,
height = appended[appended.length - 1].clientHeight;
if (widthEqual && heightEqual) {
Array.prototype.forEach.call(appended, function(btn) {
btn.style.width = width + 'px';
btn.style.height = height + 'px';
});
} else if (widthEqual) {
Array.prototype.forEach.call(appended, function(btn) {
btn.style.width = width + 'px';
});
} else if (heightEqual) {
Array.prototype.forEach.call(appended, function(btn) {
btn.style.height = height + 'px';
});
}
}
}
mynumber();
mynumber({
'container': 'anotherElement',
'rows': 5,
'cols': 4,
'elementClass': 'arbitraryClassName'
});
mynumber({
'container': 'more',
'width': '3em',
'height': '2em'
});
button {
font-size: 2em;
}
.arbitraryClassName {
color: #fff;
border: 1px solid limegreen;
text-shadow: 0 0 2px #000;
}
<div id="btnsize"></div>
<div id="anotherElement"></div>
<div id="more"></div>
External JS Fiddle demo, for experimentation/developement.
Now, to address your question in the simplest terms possible, using the code you originally posted (incidentally, in JavaScript, the # character is not valid for comments; it's a syntax error: Uncaught SyntaxError: Unexpected token ILLEGAL), you could simply use CSS such as below:
function mynumber() {
var i, j = 1,
num = 0; // i used for making three buttons in row and j used for repeat same process no of time and num is int text on button
do {
for (i = 1; i <= 3; i++) {
var btn = document.createElement("BUTTON"); // create button using javascript
var txt = document.createTextNode(num++); // creat text on button
btn.appendChild(txt); // attached text on button
document.getElementById("btnsize").appendChild(btn); // atache button with text in div
}
var next = document.createElement("BR");
document.getElementById("btnsize").appendChild(next);
j++;
}
while (j < 4)
}
mynumber();
button {
width: 3em;
height: 4em;
}
<div id="btnsize"></div>
External JS Fiddle demo.
This CSS, of course, matches all <button> elements in the page; however you could amend the selector to be more specific to only those <button> elements within the btnsize element:
#btnsize button {
width: 3em;
height: 2em;
color: #f00;
}
function mynumber() {
var i, j = 1,
num = 0; // i used for making three buttons in row and j used for repeat same process no of time and num is int text on button
do {
for (i = 1; i <= 3; i++) {
var btn = document.createElement("BUTTON"); // create button using javascript
var txt = document.createTextNode(num++); // creat text on button
btn.appendChild(txt); // attached text on button
document.getElementById("btnsize").appendChild(btn); // atache button with text in div
}
var next = document.createElement("BR");
document.getElementById("btnsize").appendChild(next);
j++;
}
while (j < 4)
}
mynumber();
#btnsize button {
width: 3em;
height: 4em;
color: #f00;
}
<div id="btnsize"></div>
<button>Just to demonstrate that I'm not being styled</button>
External JS Fiddle demo.
Or you could add a class-name while you create your <button> elements:
#btnsize button {
width: 3em;
height: 2em;
color: #f00;
}
function mynumber() {
var i, j = 1,
num = 0; // i used for making three buttons in row and j used for repeat same process no of time and num is int text on button
do {
for (i = 1; i <= 3; i++) {
var btn = document.createElement("BUTTON"); // create button using javascript
// adding a class-name:
btn.classList.add('buttonClassName');
var txt = document.createTextNode(num++); // creat text on button
btn.appendChild(txt); // attached text on button
document.getElementById("btnsize").appendChild(btn); // atache button with text in div
}
var next = document.createElement("BR");
document.getElementById("btnsize").appendChild(next);
j++;
}
while (j < 4)
}
mynumber();
.buttonClassName {
width: 3em;
height: 4em;
color: #f00;
}
<div id="btnsize"></div>
<button>Just to demonstrate that I'm not being styled</button>
External JS Fiddle demo.
References:
Array.prototype.forEach().
document.createDocumentFragment().
document.createElement().
document.getElementById().
Element.classList.
for...in.
Function.prototype.call().
HTMLElement.style.
% (modulo) operator.
Node.appendChild().
Node.cloneNode().
Node.textContent.
Object.hasOwnProperty().

parseFloat returns NaN (when parsing a document style size)

the body style parseFloat keeps returning NaN when it should be -0.1 or 0.1 when I print it in console.log, I'm most-likely just getting tired, but I can't see the issue. Any help is greatly appreciated as always,
Also not looking for a jQuery answer here, this must be JS
function startUp() {
var fontButtons = document.getElementsByClassName("fontsizer");
for (var i=0; i < fontButtons.length; i++) {
document.getElementsByClassName("fontsizer")[i].addEventListener("click", resizeText);
}
};
window.onload = startUp;
function resizeText() {
var fontChange = parseFloat(this.value);
console.log(fontChange);
if (document.body.style = "") {
document.body.setAttribute("style", "font-size:1.0em;");
}
var currentFontSize = parseFloat(document.body.style.fontSize.replace('em', ''));
console.log(currentFontSize);
document.body.style.fontSize = (currentFontSize + fontChange) + "em";
};
and the html
<div id="fontbuttons">
<input class="fontsizer" value="-0.1" type="image" id="fontdown" alt="-" src="fontdown.png" />
<input class="fontsizer" value="0.1" type="image" id="fontup" alt="+" src="fontup.png" />
</div>
1 = is assignment, so this line actually mean clear body style.
if (document.body.style = "") {
And then it returns the assigned value (empty string) to the if statement, whereas "" is a falsy value, so the check is actually if(false) {.... You'll never have font size set.
You should use == or === to compare
if (document.body.style == "") {
2 document.body.style is an object, it should never be equal to empty string. You should compare document.body.style.fontSize with it.
3 Use document.body.style.fontSize = "1.0em" over document.body.setAttribute. Because setAttribute only changes the HTML attribute while what you want is DOM property.
EDIT: I missed a thing, as pointed out by Roko C. Buljan. When you set font size in CSS, document.body.style.fontSize will still be emtpy string, unless you manipulate it with JS. You'd better compare with computed style, the solution is addressed in his answer.
function startUp() {
var fontButtons = document.getElementsByClassName("fontsizer");
for (var i = 0; i < fontButtons.length; i++) {
fontButtons[i].addEventListener("click", resizeText);
}
};
window.onload = startUp;
function resizeText() {
var fontChange = parseFloat(this.value);
console.log(fontChange);
if (document.body.style.fontSize == "") {
document.body.style.fontSize = "1.0em";
}
var currentFontSize = parseFloat(document.body.style.fontSize.replace('em', ''));
console.log(currentFontSize);
document.body.style.fontSize = (currentFontSize + fontChange) + "em";
};
<div id="fontbuttons">
<input class="fontsizer" value="-0.1" type="button">
<input class="fontsizer" value="0.1" type="button">
</div>
<p>text</p>
Ok, let's say we have the font-size set at 1.2em
/*CSS:*/ body{ font-size: 1.2em; }
in JS if you test:
console.log( document.body.style.fontSize ); // *(and empty string)*
So you need to do like:
var style = window.getComputedStyle(document.body, null);
console.log( style.fontSize ); // "19.2px"
bummer, but we expected em, right?
For every browser sets the default font size to 16px (google it) you can retrieve any em value by simply dividing the px value that is returned by our getComputedStyle by 16.
var style = window.getComputedStyle(document.body, null); // .fontSize = "19.2px"
var currentFontSize_EM = parseFloat( style.fontSize ) / 16 // 1.2
document.body.style.fontSize = (currentFontSize_EM + fontChange) + "em";
that' s it.

Finding number of lines in an html textarea

I'm writing a mobile web application where scrollbars are not displayed on the device's browser. Due to this, I'm trying to dynamically modify the height of the textarea to make it bigger, however I don't know of any way to actually get the line count on an html textarea. Any help would be greatly appreciated!
EDIT
So I realize now that it's not newlines per se, but actual line wrapping. So when one line finishes it wraps the text to the next line. It appears as if it is a new line. Any way to count the number of these? Thanks!
The number of lines in the textarea would be
textarea.value.match(/\n/g).length + 1
I have created a plugin to handle line counting and wrap detection in a <textarea>.
I hope someone can use it.
Code on BitBucket
Sample Usage
var result = $.countLines("#textarea");
result.actual // The number of lines in the textarea.
result.wraps // The number of lines in the textarea that wrap at least once.
result.wrapped // The total number of times all lines wrap.
result.blank // The number of blank lines.
result.visual // The approximate number of lines that the user actually sees in the textarea
Working Demonstration
/*! Textarea Line Count - v1.4.1 - 2012-12-06
* https://bitbucket.org/MostThingsWeb/textarea-line-count
* Copyright (c) 2012 MostThingsWeb (Chris Laplante); Licensed MIT */
(function($) {
$.countLines = function(ta, options) {
var defaults = {
recalculateCharWidth: true,
charsMode: "random",
fontAttrs: ["font-family", "font-size", "text-decoration", "font-style", "font-weight"]
};
options = $.extend({}, defaults, options);
var masterCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
var counter;
if (!ta.jquery) {
ta = $(ta);
}
var value = ta.val();
switch (options.charsMode) {
case "random":
// Build a random collection of characters
options.chars = "";
masterCharacters += ".,?!-+;:'\"";
for (counter = 1; counter <= 12; counter++) {
options.chars += masterCharacters[(Math.floor(Math.random() * masterCharacters.length))];
}
break;
case "alpha":
options.chars = masterCharacters;
break;
case "alpha_extended":
options.chars = masterCharacters + ".,?!-+;:'\"";
break;
case "from_ta":
// Build a random collection of characters from the textarea
if (value.length < 15) {
options.chars = masterCharacters;
} else {
for (counter = 1; counter <= 15; counter++) {
options.chars += value[(Math.floor(Math.random() * value.length))];
}
}
break;
case "custom":
// Already defined in options.chars
break;
}
// Decode chars
if (!$.isArray(options.chars)) {
options.chars = options.chars.split("");
}
// Generate a span after the textarea with a random ID
var id = "";
for (counter = 1; counter <= 10; counter++) {
id += (Math.floor(Math.random() * 10) + 1);
}
ta.after("<span id='s" + id + "'></span>");
var span = $("#s" + id);
// Hide the span
span.hide();
// Apply the font properties of the textarea to the span class
$.each(options.fontAttrs, function(i, v) {
span.css(v, ta.css(v));
});
// Get the number of lines
var lines = value.split("\n");
var linesLen = lines.length;
var averageWidth;
// Check if the textarea has a cached version of the average character width
if (options.recalculateCharWidth || ta.data("average_char") == null) {
// Get a pretty good estimation of the width of a character in the textarea. To get a better average, add more characters and symbols to this list
var chars = options.chars;
var charLen = chars.length;
var totalWidth = 0;
$.each(chars, function(i, v) {
span.text(v);
totalWidth += span.width();
});
// Store average width on textarea
ta.data("average_char", Math.ceil(totalWidth / charLen));
}
averageWidth = ta.data("average_char");
// We are done with the span, so kill it
span.remove();
// Determine missing width (from padding, margins, borders, etc); this is what we will add to each line width
var missingWidth = (ta.outerWidth() - ta.width()) * 2;
// Calculate the number of lines that occupy more than one line
var lineWidth;
var wrappingLines = 0;
var wrappingCount = 0;
var blankLines = 0;
$.each(lines, function(i, v) {
// Calculate width of line
lineWidth = ((v.length + 1) * averageWidth) + missingWidth;
// Check if the line is wrapped
if (lineWidth >= ta.outerWidth()) {
// Calculate number of times the line wraps
var wrapCount = Math.floor(lineWidth / ta.outerWidth());
wrappingCount += wrapCount;
wrappingLines++;
}
if ($.trim(v) === "") {
blankLines++;
}
});
var ret = {};
ret["actual"] = linesLen;
ret["wrapped"] = wrappingLines;
ret["wraps"] = wrappingCount;
ret["visual"] = linesLen + wrappingCount;
ret["blank"] = blankLines;
return ret;
};
}(jQuery));
result = jQuery.countLines("#textarea");
jQuery('#display').html(
'<span>Actual: ' + result.actual + '</span>' +
'<span>Blank: ' + result.blank + '</span>' +
'<span>Visual: ' + result.visual + '</span>' +
'<span>Wrapped: ' + result.wrapped + '</span>' +
'<span>Wraps: ' + result.wraps + '</span>'
);
#textarea {
width: 150px;
height: 80px;
}
#display span {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea id="textarea">text
here
this is a longer line so that it will wrap in the box longer longer longer</textarea>
<div id="display"></div>
This is an efficient and accurate method to count the number of lines in a text area, including wrapped lines.
/** #type {HTMLTextAreaElement} */
var _buffer;
/**
* Returns the number of lines in a textarea, including wrapped lines.
*
* __NOTE__:
* [textarea] should have an integer line height to avoid rounding errors.
*/
function countLines(textarea) {
if (_buffer == null) {
_buffer = document.createElement('textarea');
_buffer.style.border = 'none';
_buffer.style.height = '0';
_buffer.style.overflow = 'hidden';
_buffer.style.padding = '0';
_buffer.style.position = 'absolute';
_buffer.style.left = '0';
_buffer.style.top = '0';
_buffer.style.zIndex = '-1';
document.body.appendChild(_buffer);
}
var cs = window.getComputedStyle(textarea);
var pl = parseInt(cs.paddingLeft);
var pr = parseInt(cs.paddingRight);
var lh = parseInt(cs.lineHeight);
// [cs.lineHeight] may return 'normal', which means line height = font size.
if (isNaN(lh)) lh = parseInt(cs.fontSize);
// Copy content width.
_buffer.style.width = (textarea.clientWidth - pl - pr) + 'px';
// Copy text properties.
_buffer.style.font = cs.font;
_buffer.style.letterSpacing = cs.letterSpacing;
_buffer.style.whiteSpace = cs.whiteSpace;
_buffer.style.wordBreak = cs.wordBreak;
_buffer.style.wordSpacing = cs.wordSpacing;
_buffer.style.wordWrap = cs.wordWrap;
// Copy value.
_buffer.value = textarea.value;
var result = Math.floor(_buffer.scrollHeight / lh);
if (result == 0) result = 1;
return result;
}
Demo here
I haven't tried using the function discussed in this blog, but you may find it useful.
http://kirblog.idetalk.com/2010/03/calculating-cursor-position-in-textarea.html
Basically, if you create a div and then copy the text into that div, with the same width and font characteristics, you can then get the information you need, such as the number of lines. The number of lines in this example would be easy, in that if you know how many pixels high a single line would be, then just find the width of the test div and you can get a pretty accurate idea as to how many lines are in your textarea.
Get scrollHeight, subtract top+bottom padding, divide by lineHeight.
I'm pretty sure there is no reasonable way to count the number of lines as displayed in the browser especially considering some browsers (Safari) allow the user to resize textareas.
It'd be hacky, but your best bet might be to just estimate based on the total characters divided by average number of characters per line. :-/
Maybe there is a way to get the "raw" number of "visual" lines. You should read the scrollHeight property of the textarea and divide it by the height of a line. Let's try.
Start with this HTML:
<textarea id="ta" cols="50" rows="10"></textarea>
Then:
var line_height = Math.floor($("#ta").height() / parseInt($("#ta").attr("rows")));
var dirty_number_of_lines = Math.ceil($("#ta")[0].scrollHeight / line_height);
I am not sure if that really works, just a mad theory.
You can calculate is as so:
var length = $('#textarea').val().split("\n").length;
The number of characters allowed per line is dictated by the "cols" attribute of the textarea.
<textarea rows="10" cols="80"></textarea>
Assuming 80 characters per line, a good estimate may be:
var approxNumLines = textareaElement.value.length / textareaElement.cols ;
Doesn't account for word-break and word-wrap.

Categories

Resources