JS: Checking if no children exist not working - javascript

I'm trying to have a text input box that when you press keys on the keyboard it shows the ASCII codes underneath in a list. When you first click on the box the paragraph above is hidden. When you click away from the box I want the paragraph to reappear only if there is no list of codes underneath (nothing has been pressed or everything has been deleted).
I'm trying to check the list with "if (element.childNodes.length == 0)" but it's not working. Can anyone tell me where I'm going wrong?
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!DOCTYPE html>
<html>
<body>
<p id='press'>Press a key on the keyboard in the input field to get the Unicode character code of the pressed key.</p>
<input id="text" type="text" size=40 onkeypress="myFunction(event)" onkeydown="downFunction(event)">
<script>
function myFunction(event) {
var x = event.which || event.keyCode || event.charCode;
if(x >= 65 && x <= 90 || x >= 97 && x <= 122 || x == 32){
var element = document.getElementById('ul');
var fragment = document.createDocumentFragment();
var li = document.createElement('li');
li.textContent = String.fromCharCode(x) + ': ' + x;
fragment.appendChild(li);
element.appendChild(fragment);
};
};
function downFunction(event) {
var y = event.which || event.keyCode || event.charCode;
if( y == 8 ){
var element = document.getElementById('ul');
element.removeChild(element.lastChild);
};
};
$('#text').click(function(e){
$('#press').hide();
});
$('#text').focusout(function(e){
var element = document.getElementById('ul');
if (element.childNodes.length == 0) {
$('#press').show();
};
});
</script>
<ul id="ul">
</ul>
</body>
</html>

The reason your code doesn't work as expected is because you're using childNodes, which counts all child nodes of an element, including plain text. While there may not be any elements in your #ul, there is a text node. Note that:
<ul id = "ul">
</ul>
is differrent from:
<ul id = "ul"></ul>
To remedy your issue, use children instead, which returns an element-only collection that won't be affected by how you space your HTML code. The difference can be seen in the following snippet.
Snippet:
/* ----- JavaScript ----- */
var ul1 = document.getElementById("ul1");
var ul2 = document.getElementById("ul2");
console.log("ul1.childNodes: " + ul1.childNodes.length);
console.log("ul1.children: " + ul1.children.length);
console.log("ul2.childNodes: " + ul2.childNodes.length);
console.log("ul2.children: " + ul2.children.length);
<!------ HTML ----->
<ul id = "ul1">
</ul>
<ul id = "ul2"></ul>

Try to change your condition as below:
element.childNodes.length === 0 ||
(element.childNodes.length === 1 && !element.childNodes[0].tagName)
By this, you can control if a child is an actual dom node with a tagName

Related

Hide a part of input as password

I have a <input type="text"> in which i consider the pattern A B C
Example value:
Hello guys this is a sample
Hello is A
guys is B
this is a sample is C
I would like to transform C to be like if it was a type="password" only for C.
But I must use only one input.
So, after there are 2 spaces, next part become hidden.
Is it possible ?
I can use css/js.
You need to create a code to do it. You need to list 'keydown' and 'keyup' events.
Maybe this example can be near that you want.
https://codesandbox.io/s/hide-part-input-q5yzd
In HTML
<body>
<h1>Example</h1>
<label>Type here the word</label><input id="input" type="text" style="margin-left: 10px; width: 300px" />
<br />
<label>Without transform</label>
<input id="input2" type="text" disabled style="margin-left: 10px; width: 300px" />
<script src="src/index.js"></script>
</p>
</body>
And in javascript:
import './styles.css';
let inputTextOriginal = [];
let inputTextModified = [];
let numSpaces = 0;
const node = document.getElementById('input');
console.log('node', node);
node.addEventListener('keydown', function(event) {
const keycode = event.keyCode;
console.log('keycode', keycode);
if (
(keycode > 47 && keycode < 58) || // number keys
keycode === 32 || // spacebar
(keycode > 64 && keycode < 91) || // letter keys
(keycode > 95 && keycode < 112) // numpad keys
) {
inputTextOriginal.push(event.key);
if (numSpaces >= 2) {
inputTextModified.push('*');
} else {
inputTextModified.push(event.key);
}
if (keycode === 32) {
numSpaces++;
}
}
if (keycode === 8) {
inputTextModified.pop();
let deleteKey = inputTextOriginal.pop();
if (deleteKey === ' ') {
numSpaces--;
}
}
});
node.addEventListener('keyup', function(event) {
node.value = inputTextModified.join('');
document.getElementById('input2').value = inputTextOriginal.join('');
});
This can't be done using a single input field. One input field can have one type. You can have a hacky solution where you can put two inputs wrapped inside a div. So that, it appears as one input only.
Hope this helps

Simple dom inspector script counts incorrectly child nodes

I have simple js function that returns me dom path to clicked element. However sometimes some element(usually the one i have clicked) counts twice
so if i have following code
<ul>
<li><a href ='#'>Some link</a></li>
</ul>
the output could be like ul:nth-child(1) > li:nth-child(1) > a:nth-child(2)
while a should be 1 child.
Here is the code of function
var getDomPath;
getDomPath = function(el) {
var count, element, nth, path, selector, sib;
element = el;
if (!(el instanceof Element)) {
return;
}
path = [];
while (el.nodeType === Node.ELEMENT_NODE && el.id !== "jobs") {
selector = el.nodeName.toLowerCase();
if (el.id) {
selector += "#" + el.id;
} else if (el.className) {
selector += "." + el.className;
} else {
sib = el;
nth = 1;
count = 1;
while (sib.nodeType === Node.ELEMENT_NODE && (sib = sib.previousSibling) && $(el).prev().prop('tagName') !== "STYLE" && $(el).prev().prop('tagName') !== null) {
count += $(el).prevAll().size();
}
selector += ":nth-child(" + count + ")";
}
path.unshift(selector);
el = el.parentNode;
}
return path.join(" > ");
};
Code was converted from coffee to javascript, so might looks a little bad
here is html that im trying to get path to
<td width="50%"><strong>Leveringssadresse</strong>
<br>
Hans Andersen
<br>
Andevej 123
<br>
1234 Andeby
<br>
Denmark
<br>
Telefon: 31122345
<br>
Mobiltelefon: 12232345
<br>
mail#gmail.com
<br>
</td>
Here when i click on a element i expect it to be nth-child(8), but somehow i'm getting it 17(16 + 1 as increment by 1 in my code)
Just noticed you use jQuery.
Why not simplify it to just
function getDomPath(element){
var path = $(element).parents().andSelf().map(function(){
var index = $(this).index() + 1;
return this.nodeName.toLowerCase() + ':nth-child('+ index +')';
}).get();
return path.join(' > ');
}
Demo at http://jsfiddle.net/BEUrn/3/
The problem is with this line:
while (sib.nodeType === Node.ELEMENT_NODE && (sib = sib.previousSibling) && $(el).prev().prop('tagName') !== "STYLE" && $(el).prev().prop('tagName') !== null) {
I have read this line multiple times, and I can't work out what you're trying to do.
Your code says:
make sure that the currently examined node is an element
set the currently examined node to be its previous sibling
check that the element before the original node has a tag name that isn't STYLE
I have no idea what that sequence of steps is intended to be, but if the final condition passes, it will calculate n * (x + 1) + 1, where n is the number of preceding element siblings and x is the number of immediately preceding element siblings without intervening text nodes.
The next line is this:
count += $(el).prevAll().size();
This, surely, should only be run once, with the conditional not necessary at all.
count = $(el).prevAll().length + 1;
jsFiddle with working code.

Enable exact multi word search for node titles with the jstree search plugin

I use the jstree search plugin (documentation) to search for multiple IDs in the title fields of my HTML tree.
I took the original jstree code (line 3398) and changed it as suggested here to enable multi word searching in the title fields of the tree.
It works fine for "title contains" queries (e.g. title ID:40857332 of node x and title ID:408 of node y contain the search word ID:408) but I'm at loss how to change the code in order to find only exact matches (e.g. title ID:408 of node y matches the search word ID:408 exactly).
The function gets called like this:
$("#my_html_tree").jstree("search", search_words);
with the following configurations:
"search" : {"case_insensitive" : true, "search_method": "jstree_title_contains_multi"}
The variable "search_words" is a string containing several IDs:
var search_words = "ID:4 ID:7 ID:3188";
The format of the HTML tree nodes:
<li id="3188"> <a title="ID:3188">Tree node 3188</a></li>
This is my changed code:
$.expr[':'].jstree_title_contains_multi = function(a,i,m){
var word, words = [];
var searchFor = m[3].toLowerCase().replace(/^\s+/g,'').replace(/\s+$/g,'');
if(searchFor.indexOf(' ') >= 0) {
words = searchFor.split(' ');
}
else {
words = [searchFor];
}
for (var i=0; i < words.length; i++) {
word = words[i];
if((a.getAttribute("title") || "").toLowerCase().indexOf(word) >= 0) {
return true;
}
}
return false;
};
How must I change the code in order to enable searching only for exact matches?
Any help would be greatly appreciated.
I found the following solution:
Instead of using
if((a.getAttribute("title") || "").toLowerCase().indexOf(word) >= 0)
I use
if((a.getAttribute("title") || "").toLowerCase() === word )
It's cleaner to use the id attribute and not the title. You can even use both.
Configuration:
"search" : {"case_insensitive" : true,
"search_method": "jstree_codigo_descripcion"}
Search call
var codigo = "0102";
var descripcion = "sdf";
jQuery("#arbol").jstree("search",cod +"-"+ desc);
Tree
<div id="arbol">
<ul>
<li><a>01</a>
<ul>
<li><a>0101</a>
<ul>
<li><a id="010101" title="foo">010101 foo</a></li>
<li><a id="010102" title="bar">010102 bar</a></li>
</ul>
</li>
<li><a>0102</a>
<ul>
<li><a id="010201" title="asdf">010201 asdf</a></li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
The new search function
$.expr[':'].jstree_codigo_descripcion = function(a, i, m) {
var searchFor = m[3].toLowerCase();
var params = searchFor.split('-');
var codigo = params[0];
var descripcion = params[1];
// attribute id start with codigo
// attribute title contains descripcion
if (codigo.length > 0 && descripcion.length === 0) {
if ((a.getAttribute("id") || "").toLowerCase().indexOf(codigo) === 0) {
return true;
}
}
if (codigo.length === 0 && descripcion.length > 0) {
if ((a.getAttribute("title") || "").toLowerCase().indexOf(descripcion) >= 0) {
return true;
}
}
if (codigo.length > 0 && descripcion.length > 0) {
if ((a.getAttribute("id") || "").toLowerCase().indexOf(codigo) === 0 &&
(a.getAttribute("title") || "").toLowerCase().indexOf(descripcion) >= 0) {
return true;
}
}
return false;
};

JavaScript function runs for 1 second then clears?

So I've written a web page with a few JavaScript functions.
The page runs perfectly in Dreamweaver but when I tried it out in a browser (Google Chrome and Firefox) the JavaScript flashes for a split second then clears. I have no idea at all why this is.
<body>
<form name="myForm">
<ol>
<li>
<h1> TILE CALCULATOR</h1>
</li>
<li>
<label for="wall height">Wall height (cm)</label>
<input type="text" id="wall_height" />
</li>
<li>
<label for="wall width">Wall Width (cm)</label>
<input type="text" id="wall_width" />
</li>
<li>
<label for="tile height">Tile Height (cm)</label>
<input type="text" id="tile_height" />
</li>
<li>
<label for="tile width">Tile Width (cm)</label>
<input type="text" id="tile_width" />
</li>
<button onclick="javascript:validate();"> Calculate </button>
</ol>
</form>
<br />
<p id="result"></p>
<br />
<canvas id="myCanvas">
Your browser does not support this feature</canvas>
<br />
<br />
<script language="javascript" type="text/javascript">
//functoin to validate the inputs by the user
//user can only enter a number and all fields must be filled
function validate()
{
//first make sure canvas is clear
clearCanvas()
//take the inputs as variables
var x = document.getElementById("tile_width").value;
var y = document.getElementById("tile_height").value;
var z = document.getElementById("wall_width").value;
var i = document.getElementById("wall_height").value;
//check if the user has entered nothing and alert if they have
if (x==null || x=="" || y==null || y=="" || z==null || z=="" || i==null || i=="")
{
alert("All the fields have to be filled out!");
clearResult();
}
// check if the user has entered invalid values, only numbers can be entered
if (isNaN(x) == true || isNaN(y) == true || isNaN(z) == true || isNaN(i) == true)
{
alert("Dimensions can only be numbers!");
clearResult();
}
//check for negatives
if (x <= 0 || y <= 0 || z <= 0 || i <= 0)
{
alert("invalid dimension input, positive non-zero values only");
clearResult();
}
//if valid calculate tiles and print
else
tileCalculator();
}
function tileCalculator()
{
//take the input as variables
var tileWidth = document.getElementById("tile_width").value;
var tileHeight = document.getElementById("tile_height").value;
var wallWidth = document.getElementById("wall_width").value;
var wallHeight = document.getElementById("wall_height").value;
//find the areas of the tile and the wall
var tileArea = tileWidth * tileHeight;
var wallArea = wallWidth * wallHeight;
//divide these to find the number of tiles needed
var noOfTiles = (wallArea/tileArea);
//prints the result of noOfTiles
document.getElementById("result").innerHTML=" The number of Tiles you will need are : " + noOfTiles;
//scalled tiles to the canvas width of your choice
var scalledWidth = 500;
var ratioHW = wallHeight/wallWidth;
var scalledHeight = ratioHW*scalledWidth;
//scaled tile sizes
//scale the tiles to correct pixels
var scalledTileWidth = (tileWidth/wallWidth)*scalledWidth;
var scalledTileHeight = (tileHeight/wallHeight)*300;
//finds the number of tiles needs in a row
var noWidth = wallWidth/tileWidth;
//number of tiles in a column
var noHeight = wallHeight/tileHeight;
var canvas = document.getElementById("myCanvas");
canvas.style.width=scalledWidth + "px";
canvas.style.height=scalledHeight + "px";
printWall(0,0,noWidth,scalledTileWidth,(scalledTileHeight/2),noHeight);
}
//print a tile given the position and dimensions
function printTile(x,y,tileWidth,tileHeight)
{
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.fillRect(x,y,tileWidth,tileHeight);
}
//prints one row of tiles given the starting position and number to be printed
function printTileRow(x,y,numberOfTiles,tileWidth,tileHeight)
{
var start = 0;
//loops upto number of tiles in a row
while (start < numberOfTiles)
{
//prints a tile each time
printTile(x,y,tileWidth,tileHeight);
//next brick position
x = x + (tileWidth + 1); // add a space between tiles here.
start++;
}
}
//prints the wall
function printWall(x,y,numberOfTiles,tileWidth,tileHeight,numberOfRows)
{
//holds whether last row was shifted
var shiftCount = 0;
//starting index
var start = 0;
//loop up adding a row until required number of rows
while (start < numberOfRows)
{
//prints half a tile at the start of each row
printTile(0,y,(0.5 * tileWidth - 1),(tileHeight));
//prints the row
printTileRow((x+shiftCount),y,numberOfTiles,tileWidth,tileHeight);
//if shifted
if (shiftCount > 0)
{
//was shifted last row
shiftCount = 0;
}
else
{
//was not shifted last row
shiftCount = shiftCount + (0.5*tileWidth);
}
start++;
//start next row
y = y + (tileHeight + 1);
}
}
//clears the canvus each time the button is pressed
function clearCanvas()
{
//reset canvus to 300 by 300 and clear
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext('2d');
canvas.style.height="300px";
canvas.style.width="300px";
context.clearRect(0,0,300,300);
}
function clearResult()
{
document.getElementById("result").innerHTML="";
}
</script>
</body>
I would really appreciate it if someone could have a quick look for me! Thanks,
Try using onclick="javascript:validate();" on the form tag instead of on the button tag and try using 'onsubmit' instead of 'onclick'
Just replace your onclick attribute by onclick='validate(); return false'.
OR (better then previous)
Just add a type="button" attribute to your button tag, once the HTML5 button's default behavior is to submit forms.
Useful tip
Bind your handler programatically, this way (with no jQuery):
<button type="button" id="calculate"> Calculate </button>
...
window.addEventListener("load", function(){
document.getElementById("calculate").addEventListener("click", validate);
});
there is no problem with kepping the button in the form you you need to return false everytime to avoid the form submit using the return false like this
onclick="javascript:validate();return false;"

Limit number of lines in textarea and Display line count using jQuery

Using jQuery I would like to:
Limit the number of lines a user can enter in a textarea to a set number
Have a line counter appear that updates number of lines as lines are entered
Return key or \n would count as line
$(document).ready(function(){
$('#countMe').keydown(function(event) {
// If number of lines is > X (specified by me) return false
// Count number of lines/update as user enters them turn red if over limit.
});
});
<form class="lineCount">
<textarea id="countMe" cols="30" rows="5"></textarea><br>
<input type="submit" value="Test Me">
</form>
<div class="theCount">Lines used = X (updates as lines entered)<div>
For this example lets say limit the number of lines allowed to 10.
html:
<textarea id="countMe" cols="30" rows="5"></textarea>
<div class="theCount">Lines used: <span id="linesUsed">0</span><div>
js:
$(document).ready(function(){
var lines = 10;
var linesUsed = $('#linesUsed');
$('#countMe').keydown(function(e) {
newLines = $(this).val().split("\n").length;
linesUsed.text(newLines);
if(e.keyCode == 13 && newLines >= lines) {
linesUsed.css('color', 'red');
return false;
}
else {
linesUsed.css('color', '');
}
});
});
fiddle:
http://jsfiddle.net/XNCkH/17/
Here is little improved code. In previous example you could paste text with more lines that you want.
HTML
<textarea data-max="10"></textarea>
<div class="theCount">Lines used: <span id="linesUsed">0</span></div>
JS
jQuery('document').on('keyup change', 'textarea', function(e){
var maxLines = jQuery(this).attr('data-max');
newLines = $(this).val().split("\n").length;
console.log($(this).val().split("\n"));
if(newLines >= maxLines) {
lines = $(this).val().split("\n").slice(0, maxLines);
var newValue = lines.join("\n");
$(this).val(newValue);
$("#linesUsed").html(newLines);
return false;
}
});
For React functional component that sets new value into state and forwards it also to props:
const { onTextChanged, numberOfLines, maxLength } = props;
const textAreaOnChange = useCallback((newValue) => {
let text = newValue;
if (maxLength && text.length > maxLength) return
if (numberOfLines) text = text.split('\n').slice(0, numberOfLines ?? undefined)
setTextAreaValue(text); onTextChanged(text)
}, [numberOfLines, maxLength])
A much ugly , but somehow working example
specify rows of textarea
<textarea rows="3"></textarea>
and then
in js
$("textarea").on('keydown keypress keyup',function(e){
if(e.keyCode == 8 || e.keyCode == 46){
return true;
}
var maxRowCount = $(this).attr("rows") || 2;
var lineCount = $(this).val().split('\n').length;
if(e.keyCode == 13){
if(lineCount == maxRowCount){
return false;
}
}
var jsElement = $(this)[0];
if(jsElement.clientHeight < jsElement.scrollHeight){
var text = $(this).val();
text= text.slice(0, -1);
$(this).val(text);
return false;
}
});
For the React fans out there, and possibly inspiration for a vanilla JS event handler:
onChange={({ target: { value } }) => {
const returnChar = /\n/gi
const a = value.match(returnChar)
const b = title.match(returnChar)
if (value.length > 80 || (a && b && a.length > 1 && b.length === 1)) return
dispatch(setState('title', value))
}}
This example limits a textarea to 2 lines or 80 characters total.
It prevents updating the state with a new value, preventing React from adding that value to the textarea.

Categories

Resources