Get all html controls visible to the user? - javascript

How might one go about getting all controls visible to the user?
<div id=dataform>
<div>
Name
<input type=text id=name class=entry>
</div>
<div>
City
<input type=text id=city class=entry style="display:none;">
</div>
<div style="display:none;">
<label for=nocontact>nocontact</label>
<input type=checkbox id=nocontact class=entry>
</div>
<div>
<table>
<tr>
<td>
<label for=phone>Phone</label>
</td>
<td>
<input type=text id=phone class=entry>
</td>
</tr>
</table>
</div>
<div>
<select id="keys">
<option value=1>1</option>
<option value=2>2</option>
<option value=3>3</option>
</div>
</div>
Neither "city" nor "nocontact" are visible to the user.
document.getElementById("dataform").querySelectorAll("dataform > entry ???? ");
Or some other method to get all controls, inputs, that are visible (not just inputs, but selects, textarea, etc). Could add a class to each, as shown and grab all, but how to determine or get just those that are visible to the user. The nocontact checkbox wouldn't be, so ignore it. I put the table in there to demonstrate that the control is not always a direct child of the div in which it resides.
I'm afraid that cycling through them all and tagging them with a class or a data attribute is the only way and that pretty much sucks. FYI: not using jquery or other framework on this.

There's no "proper" way to do this, in part because it's unclear what "visible" is supposed to mean in the first place. jQuery (version 3.4.1) does it like this:
jQuery.expr.pseudos.visible = function( elem ) {
return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );
};
...which is great for certain definitions of "visible", but it doesn't check whether an element has "visibility" set to "hidden", or if it's scrolled off the screen; it mostly just checks for display:none (including on parent elements).

Related

Value is generated by onClick event, but it is not inserted into inner HTML. Why?

When I return the value from the javascript function on the onClick event, it is getting inserted but the form refreshes again and I lose the selection and the values that I have inserted.
Why does this happen and how can i avoid it?
Below is a sample of my code:
<form id="lengthConvert">
<p><b>Enter a value : </b></p><input type="number" name="inputValue" />
<p><b>Convert from : </b></p>
<select name="fromUnit">
<option value="Centimeter">Centimeter</option>
<option value="Meter">Meter</option>
<option value="Kilometer">Kilometer</option>
<option value="Miles">Miles</option>
</select>
<p><b>Convert to : </b></p>
<select name="toUnit">
<option value="Centimeter">Centimeter</option>
<option value="Meter">Meter</option>
<option value="Kilometer">Kilometer</option>
<option value="Miles">Miles</option>
</select>
<br/><p id="Output"></p>
<button type="submit" form="lengthConvert" value="Submit" onclick="getElementById('Output').innerHTML=convert()">Convert</button>
</button>
</div>
</body>
</html>
<script>
function convert(){
var value = document.getElementsByName('inputValue')[0].value;
var fromUnit= document.getElementsByName('fromUnit')[0].value;
var toUnit= document.getElementsByName('toUnit')[0].value;
if(fromUnit==toUnit){
return value;
}
}
</script>
when I am returning the value from the javascript method on onclick event, it is getting inserrted but the form refreshes again and I am loosing all the selection and values which i have inserted.
Why? and how can i avoid that?
When you click the button you're submitting your form because you're not doing anything to stop the default behavior. Change
<button type="submit"...
to
<button type="button"...
As a side note, your HTML needs to be fixed as you have an extra </button>, no closing </form>, and an unopened </div>. Also, your function doesn't appear to do any actual conversion.
You need to do two things:
Pass event object from onclick() function
Prevent form submission inside convert function by using preventDefault();
function convert(e) {
e.preventDefault();
var value = document.getElementsByName('inputValue')[0].value;
var fromUnit = document.getElementsByName('fromUnit')[0].value;
var toUnit = document.getElementsByName('toUnit')[0].value;
if (fromUnit == toUnit) {
return value;
}
}
<form id="lengthConvert">
<p><b>Enter a value : </b></p><input type="number" name="inputValue" />
<p><b>Convert from : </b></p>
<select name="fromUnit">
<option value="Centimeter">Centimeter</option>
<option value="Meter">Meter</option>
<option value="Kilometer">Kilometer</option>
<option value="Miles">Miles</option>
</select>
<p><b>Convert to : </b></p>
<select name="toUnit">
<option value="Centimeter">Centimeter</option>
<option value="Meter">Meter</option>
<option value="Kilometer">Kilometer</option>
<option value="Miles">Miles</option>
</select>
<br/>
<button type="submit" form="lengthConvert" value="Submit" onclick="getElementById('Output').innerHTML=convert(event)">Convert</button>
<div id='Output'></div>
</form>
You are submitting your form, which causes the page to reload, thus wiping out whatever was updated on it prior to the submit. Instead, you'll want to use a regular button (<button type="button">).
But, beyond that, your code is very outdated and inefficient. .getElementsByName() is generally not recommended for performance reasons, but in your case it's even worse because you only want one element, so you're scanning the entire DOM, getting a set of matching elements and then throwing away all but one of them. Use the modern .querySelector() and .querySelectorAll() for most of your DOM searches.
Your HTML is also invalid because you have an extra closing button tag an no closing form tag.
Also, the use of inline event attributes (onclick) should not be used. This is a 25+ year old technique that just won't die because it's easy and people just copy/paste other code they've found that seems to work. There are a lot of reasons not to do this.
There are a number of other concerns in your code as well. Here's your solution, cleaned up and modernized (make sure to see the HTML, CSS, and JS comments):
/* We can style the rows any way we want: */
div.row { margin-top:1em; margin-bottom:1em; font-weight:bold; }
<form id="lengthConvert">
<!--
The us of <p> elements here is really not correct as they mean paragraph, which
itself means "thought or idea". You are just using them for vertical spacing, and
we should not be using HTML for presentation, that's for CSS. Along the same vein,
<b> isn't appropriate here either because you are just using it for presentation.
Instead, a semantically neutral tag like <div> should be used for rows.
CSS classes can be added to control the presentation
-->
<div class="row">Enter a value : </div>
<div class="row">
<!--
Don't use self-termination syntax <tag />.
That syntax is very old an only applies to XHTML, which is not used
very much these days. Adding that syntax buys you nothing.
-->
<input type="number" name="inputValue">
</div>
<div class="row">Convert from :</div>
<div class="row">
<!-- You don't need to set a value for an <option> when
you want the value of an <option> to be the same as
the text of the <option>. The text of the selected
<option> will become the value of the <select> by default.
-->
<select name="fromUnit">
<option>Centimeter</option>
<option>Meter</option>
<option>Kilometer</option>
<option>Miles</option>
</select>
</div>
<div class="row">Convert to :</div>
<div class="row">
<select name="toUnit">
<option>Centimeter</option>
<option>Meter</option>
<option>Kilometer</option>
<option>Miles</option>
</select>
</div>
<div class="row" id="Output"></div>
<!-- The form attribute is only used when a form element is placed outside of the
form it relates to.
Also, don't use inline HTML event attributes.
And, there is no need to add a value attribute unless you want the value
to be something different than the text of the element.
-->
<button type="button">Convert</button>
</form>
<script>
// Get all the element references you know you'll need
let btn = document.querySelector("button[type='button'");
let output = document.getElementById('Output');
// You only have one element with a name of "inputValue". Use
// .querySelector() with a valid CSS selector as an argument
// to find the first item that matches the selector.
// Also, don't assign variables to properties of an element because
// if you ever want to get a second property value, you'll have to
// scan the document all over again for the element. Just store a
// reference to the element itself, then you can access that as often
// as you like.
let input = document.querySelector("input[name='inputValue']");
let fromUnit = document.querySelector("select[name='fromUnit']");
let toUnit = document.querySelector("select[name='toUnit']");
// Set up your event handling in JavaScript, not HTML
btn.addEventListener("click", convert);
function convert(){
console.log(fromUnit.value, toUnit.value);
if(fromUnit.value == toUnit.value){
// Use .textContent when getting/setting text that
// does not contain any HTML. Its's quicker and
// safer.
output.textContent = input.value;
}
}
</script>

How can I prevent the browser from scrolling to top of the page when checking the checkbox?

I know we can find lots of similar questions on stackoverflow. I am using ReactJS.
<body>
<table>
<tbody>
<tr>
<td>
<input type="checkbox" id="checkbox1">
<p>Check 1</p>
<a></a>
</td>
</tr>
<tr>
<td>
<input type="checkbox" id="checkbox1">
<p>Check 2</p>
<a></a>
</td>
</tr>
</tbody>
</table>
</body>
Here is an example of what I am using. there are above 35 checkboxes, and whenever I am clicking on any one of them the page is jumping to top.[![enter image description here][1]][1]
As I have tried on simple html page with 100 checkboxes, its working fine but in my React code. There is jump
Here is how I am handling change event:
<input type="checkbox" onChange={this.props.checkedList} name="article" value={item.id}/>
checkedList(e) {
let id = e.target.value
if (this.state.marked.includes(id)) {
let filteredArray = this.state.marked.filter(item => item !== id)
this.setState({marked: filteredArray});
}
else {
this.setState({marked: this.state.marked.concat(id)});
}
}
As you are calling setState in the event, it is causing the component to render which has the state marked. You should use the local variable in the component to handle the array marked or handle the shouldComponentUpdate. You can find details here to how to use it.
You might be able to use aria-label="test".
Example:
<input aria-label="test" id="test" type="checkbox" />
The aria-label attribute is used to define a string that labels the
current element. Use it in cases where a text label is not visible on
the screen. (If there is visible text labeling the element, use
aria-labelledby instead.)
Reference - https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-label_attribute

</form> being automattically added to my HTML in the wrong place

I have a page that I would like the following:
a <form action POST>
select List1 - with onchange set populate list 2
select List2 populated using JavaScript <div id="secondList"></div>
with an <input type="submit" value="Assign">
close the </form>
The second populates list fine. The problem seems to be (when checked in FireBug) that the /form tag is moved for some reason so that the order appears like this:
<form action POST> </form>
select list 1 - correct
list 2 - correctly populated
the <input type="submit" value="Assign">
Why does this happen and how can get around this problem?
HTML CODE
<tr>
<form action="webiste/assignToDepartment.php" method="post">
<td>
<select selected="All" name="firstItem" onchange="checkTeacherList(this.value)">
<option value="item1">item1</option>
<option value="item2">item2</option>
<option value="item3">item3</option>
</select>
</td>
<td>
<div id="secondList"></div>
</td>
<td>
<input type="submit" value="Assign">
</td>
</form>
</tr>
Your <form> element has to be placed inside the <td> item. <td> and <th> are the only valid children of <tr>. Alternatively, since your form spans several columns, you'll need to wrap the form around the containing <table>, something like:
<form>
<table>
</table>
</form>
your form element should be placed inside the tag or around the entire table
make your html proper like this
<form action="webiste/assignToDepartment.php" method="post">
<table>
<tr>
<td>
<select selected="All" name="firstItem" onchange="checkTeacherList(this.value)">
<option value="item1">item1</option>
<option value="item2">item2</option>
<option value="item3">item3</option>
</select>
</td>
<td>
<div id="secondList"></div>
</td>
<td>
<input type="submit" value="Assign">
</td>
</tr>
</table>
</form>
Firebug's HTML pane does not show the raw HTML sent by the server. Instead, it shows a nice tree graph with the structure of the document and that tree is built with the memory representation of the document nodes. It's not possible to build an invalid tree, thus invalid HTML needs to be fixed or ignored.
If you pass your HTML through the W3C HTML Validator (you might need to check the "Validate HTML fragment" option if you don't provide the complete document) you'll see it reports several errors about the document structure:
document type does not allow element "XXXX" here
end tag for "XXXX" which is not finished
You need to fix that to ensure proper rendering and, as a consequence, proper scripting.
P.S. While there're normally exact specs on how to process valid HTML, invalid HTML is often left to the browser's discretion. That's a good reason to avoid invalid tags: there can be drastic differences in the way they're rendered by different browsers.

jQuery: checkbox that selects (or deselects) all other checkboxes within its group

This may not be as simple as the title makes it out to be.
I have a div set up as a homemade checkbox via classes (1px border, 16px square, custom graphic as the check symbol applied as a background image). When clicked, 'checked' class gets applied. Simple enough. Example (unchecked): <div class="checkbox group1"> </div>
I also have another checkbox (div) that is the 'master' to select all other checkboxes within the same group (group1 in this example). This master has a special class called 'check_all': <div class="checkbox check_all group1"> </div>
So you say, "Simple; when 'check_all' is checked, just check all boxes in group1." Not quite that easy. The check_all is a universal class throughout the entire website so it may be reused on other pages; I may also have it applied to group2, group3, and so on on the same page. I need the jQuery function to check all within the same group ONLY. To further complicate matters, the group names are not sequential or related in any fashion; they could be 'invoices' or 'cart_items' or 'addresses'.
Things I have thought of:
1) Cleanse the master's class attribute of all occurrences of 'checked', 'checkbox', and 'check_all', and use the remaining string as the group name. Problem: there might be more classes than just those applied to the child checkboxes.
2) Append 'check_all_' to the group name: 'check_all_group1'. Problem: jQuery hasclass does not appear to support wildcards to perform a "begins with" to attach the handler function in the first place. Even if it did, you would still have to process the classes and perform string functions to arrive at "group1". Seems clunky and not ideal.
3) Put the master and all children divs in a parent container. This is the closest solution I have come up with, but I can't guarantee that other checkboxes belonging to other groups will not be in the container as well.
A redesign of the whole concept is welcome. I just need a simple way to drop in a reusable class that is attached to a function that will select all other 'checkbox' elements with the same group.
EDIT :
The solution must be cross-browser supported (even to IE 6.5) because visitors of this site will be worldwide and may not have the latest technology. In fact, recent analysis of the existing site reveals that 10% of the visitors are using browsers over 3 years old. Think globally.
CODE EXAMPLE
<table>
<tr>
<td><div class="checkbox check_all delete"> </div>Delete</td>
<td><div class="checkbox check_all mark">Mark</div></td>
</tr>
<tr>
<td><div class="checkbox delete"> </div></td>
<td><div class="checkbox mark"> </div></td>
<td>Line 1</td>
</tr>
<tr>
<td><div class="checkbox delete"> </div></td>
<td><div class="checkbox mark"> </div></td>
<td>Line 2</td>
</tr>
</table>
for this situation, I group div with class
<div class="check_all" id="group1"> </div>
<div class="group1"> </div>
<div class="group1"> </div>
...
<div class="check_all" id="group2"> </div>
<div class="group2"> </div>
<div class="group2"> </div>
and jQuery with
$('.check_all').on('click',function(){
$(this).hasClass('checked')?$('.'+this.id).addClass('checked'):$('.'+this.id).removeClass('checked');
});
How about doing something like:
<input type="checkbox" data-group-selector="my_group_name" class="check_all">
...
<input type="checkbox" class="my_group_name">
then...
$('.check_all').on('click', function (e) {
var group = $(this).data('group-selector');
$('.' + group).prop('checked');
});
OR
<input type="checkbox" id="select_all_my_group_name" class="check_all">
...
<input type="checkbox" class="my_group_name">
then...
$('.check_all').on('click', function (e) {
var group = $(this).attr('id').replace('select_all_', '');
$('.' + group).prop('checked');
});

Why is my jQuery .each iteration failing to use the attribute from each item?

I have some code that's finding the 'title' attribute from each child in a form.
It pulls out the title just correctly when I run 'console.log('title'). But when i try to apply the code to insert a label before the inner div of the fieldset, it just adds the same title ('About Me') to each of them.
html
<form action="#" method="post">
<fieldset title="About Me">
<!-- Going to convert legends to h4 // can choose the header style element? -->
<div>
<label for="name">Text Input:</label>
<input type="text" name="name" id="name" value="" tabindex="1" />
</div>
</fieldset>
<fieldset title="Radio Button Choice">
<div>
<label for="radio-choice-1">Choice 1</label>
<input type="radio" name="radio-choice-1" id="radio-choice-1" tabindex="2" value="choice-1" />
<label for="radio-choice-2">Choice 2</label>
<input type="radio" name="radio-choice-2" id="radio-choice-2" tabindex="3" value="choice-2" />
</div>
</fieldset>
<fieldset>
<div>
<label for="select-choice">Select Dropdown Choice:</label>
<select name="select-choice" id="select-choice">
<option value="Choice 1">Choice 1</option>
<option value="Choice 2">Choice 2</option>
<option value="Choice 3">Choice 3</option>
</select>
</div>
</fieldset>
</form>
jQ
kids = this.element.children('fieldset');
kids.each(function(){ //function to do something to each of the child fieldset elements
console.log(this);
title = $(this).attr('title');
console.log(title); //this logs each title fine, or 'undefined' where there isn't one
$("<legend>" + title + "</legend>").insertBefore('div:first-child')
//that's where I'm just getting 'About me', on every damn one....
});
Can anyone spot where I'm being a fool? Thanks.
Your selector is too generic - div:first-child will select all of the divs. Look for the div that is a descendant of this fieldset.
// Based on your existing code
$("<legend>" + title + "</legend>").insertBefore($(this).find('div:first-child'));
// Slightly cleaner
$(this).prepend("<legend>" + title + "</legend>")
Also, make sure you make title a local variable with the var keyword:
var title = $(this).attr('title');
Dennis beat me to it, anyhow here's working example with slightly different approach to selecting first child http://jsfiddle.net/gMb8m/1/
The problem was that you were using wrong selector.
EDIT:
To address some of OP questions.
As to using .children(0) instead of .find('div:first-child') - I would have to check with jQuery source, but I imagine using the later may be slower since it using involves parsing selector while the .children(0) probably uses native DOM .childNodes internally. Passing a 0 to it just returns first child.
One situation in which using .find('div:fist-child') would be better if on some pages fieldset first child wouldn't be a div element and you would still want to insert legend before first div NOT before the first child. In that case using .find would return the first div.
As to why using prepend over insertBefore - they're both good (as you can tell from Dennis answer) and can be used in your situtation. It's just a matter of choice how you write your selectors. In this case I find my way cleaner.
P.S. In the example I've replaced your kids with my selector for fieldsets - don't mind that.
.prepend() seems to do what you're going for:
$('fieldset').each(function() {
$(this).find('div:first-child').prepend('<legend>' + this.title + '</legend>');
});
Also, there's no need to promote a DOM object to a jQuery object as just to access a DOM attribute as in $(this).attr('title') :)

Categories