Detect if a form is visible or not - javascript

I have this scenario, I am detecting all forms on a site: document.forms
And I am trying to detect which forms are visible and which are not visible.
var formElement = []
for (i=0,l=document.forms.length;i<l;i++){
var formIndex = document.forms.item(i);
if (<need here just visible forms>){
formElement.push(formIndex);
}
}
Just to say I am doing this over an other pop up window that is communicating with the browser window with that forms, this depends on jQuery being present on the host site so jQuery is not a solution.
What is the best way to do this.

var isVisible = form.style.display != 'none';
UPDATE #1: hidden attribute
Also the element can be invisible if hidden attribute is specified, so the condition
could be changed to
var isVisible = form.style.display != 'none' && !form.hasAttribute('hidden');
UPDATE #2: jQuery approach:
Find all invisible forms:
$('form:hidden');
or
$('form:not(:visible)');
Find all visible forms:
$('form:visible');
Check is form visible:
$(form).is(':visible');
UPDATE #3: particular case (for original code in question)
It's working pretty well to determine visible forms using a function from my demo:
function isVisible(el) {
return el.style.display != 'none' && !el.hidden;
}
var formElement = [];
for (i=0, l=document.forms.length; i<l; i++) {
var formIndex = document.forms.item(i);
if(isVisible(formIndex)) {
formElement.push(formIndex);
}
}
console.log(formElement);
It's the same loop is this one in demo:
for(var i = document.forms.length; 0 < i--;) {
log('Form #' + i + ': ' + isVisible(document.forms[i]));
}
DEMO
UPDATE #4: pop-up window
I've adapted my example for pop-up window, but I have to say that you're NOT ABLE to deal with elements in document from other host - both pop-up and opener windows should belong to same host.
<script type="text/javascript">
var wnd = window.open('popup.html');
function isVisible(el) {
return el.style.display != 'none' && !el.hidden;
}
wnd.onload = function() {
/* This is working pretty well: */
var formElement = [];
console.log(wnd.document.forms);
for (i=0,l=wnd.document.forms.length;i<l;i++){
var formIndex = wnd.document.forms.item(i);
console.log(formIndex);
if (isVisible(formIndex)){
formElement.push(formIndex);
console.log('Form ' + formIndex.id + ' is visible');
}
}
};
</script>

var forms = document.getElementsByTagName("form");
Then, you can loop through the array and check to see if the tag is visible or not.

You can use this:
$(element).is(":visible") // Checks for display:[none|block], ignores visible:[true|false]
Ref. How do I check if an element is hidden in jQuery?

you can use :
$('#form').is(':visible')

The following will go through all forms and tell which ones are visible and which aren't:
$("form").each(function() {
if ($(this).is(":visible")) {
console.log("Visible: ", this);
} else {
console.log("Hidden: ", this);
}
});
or if you want to get all visible ones at once:
$("form:visible")
And the hidden ones:
$("form:hidden")

Related

Event is not firing on button located in a table in an iframe (no jQuery)

I have to show a spinner upon clicking an add button in a HTML form waiting for the Ajax to load the content in a select box. The select box is located in an iframe. I have set up a simplified reproduction of what I am trying to achieve in jsfiddle. There are 2 tables, one for Research Project and one for
Primary research group. The tables contain an add icon. I want to fire a console.log upon clicking the add button of just the Research Project.
I have to use the name of the parent container in the selector as there are no other non-textual identifiers in the user interface. I want to just plain JavaScript, but if jQuery or some other framework offers a solution, then I will go for that.
var formFrame = document.getElementById('form_iframe');
formFrame.src = "/catorarn/tabLe/24/show/";
var formFrameDoc = formFrame.contentDocument;
formFrame.onload = function() {
//click event on add button
var btn = getElementByXpath("//i[contains(#class,'fa fa-plus-circle') and ../../td/span[text()='Research project']]", formFrame);
console.log('showSpinner === btn is ' + btn);
addEvent(btn, 'click', function(e){
console.log('inside event listener');
e.stopPropagation();
});
};
function getElementByXpath(path, iframe) {
console.log('iframe is ' + iframe );
var doc = iframe.contentDocument;
return doc.evaluate(path, doc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
}
function addEvent(elem, evnt, funct){
if (elem.attachEvent){
console.log('addEvent === attachEvent');
return elem.attachEvent('on' + evnt, funct);
} else {
console.log('addEvent === addEventListener == elem is ' + elem );
return elem.addEventListener(evnt, funct, false);
}
}
I have created a fiddle reproducing the problem here
The event is not registering on the button at all, when I click Run in jsfiddle.
Why is the event not registered?
var formFrame = document.getElementById('form_iframe');
formFrame.src = "/catorarn/tabLe/29/show/";
formFrame.onload = function() {
var formFrameDoc = formFrame.contentDocument;
//click event on add button
var tables = formFrameDoc.getElementsByClassName('iw-formspub-container');
var children ;
for (var i = 0 ; i < tables.length; i++ ){
children = tables[i].getElementsByTagName('*');
for (var j = 0; j < children.length; j++ ){
if( children[j].textContent === 'Research project' ){
var btn = formFrameDoc.querySelector(".fa.fa-plus-circle");
btn.addEventListener('click', function(){ console.log('finally'); });
}
}
}
};
The xpath selector that I had to use was going to be ugly and complex, something like
//i[contains(#class,'fa-plus-circle') and ../../../../../../../../../../../td/table/tbody/tr/td/span[contains(text(),'Research project')]]

apply a jQuery effect in ASP based on validation result

I have a webform with a control panel (#pnlStepOne). The panel includes two textfields "txtFname" and "txtLname". I have a validator setup for each textfield. I have tested the form and all works as desired.
My questions is how do I add a jQuery effect to the panel onclick event only if one (or both) of the textfields ("txtFname" and "txtLname") don't validate. (this effect would "shake" the panel).
And I would like to add another jQuery effect to "flip" the control panel and switch the current one (#pnlStepOne) for another one (#pnlStepTwo) if both fields are validated by the asp:RequiredFieldValidators.
Just a sample code that I will tweak once I have the right If condition.
$(document).ready(function () {
$("#btnStepOne").click(function (event) {
if (**this is the condition that I am missing**)
{
$('#pnlStepOne').css({
background: 'red',
});
}
});
});
You can modify your code to be like this:
$(document).ready(function () {
$("#btnStepOne").click(function (event) {
var fvFname = document.getElementById('client-id-of-your-fvFname-validator');
var fvLname = document.getElementById('client-id-of-your-fvLname-validator');
ValidatorValidate(fvFname);
ValidatorValidate(fvLname);
if (!fvFname.isvalid || !fvLname.isvalid) {
$('#pnlStepOne').css({
background: 'red',
});
}
});
});
Have a rad of my answer to a similar question here:
Enable/Disable asp:validators using jquery
Which has the MSDN link here: http://msdn.microsoft.com/en-us/library/aa479045.aspx
In one of my projects I use a prettifyValidation function, so you could have something like:
$(document).ready(function () {
$("#btnStepOne").click(function (event) {
prettifyValidation();
});
});
function prettifyValidation() {
var allValid = true;
if (typeof Page_Validators != 'undefined') {
// Loop through from high to low to capture the base level of error
for (i = Page_Validators.length; i >= 0; i--) {
if (Page_Validators[i] != null) {
if (!Page_Validators[i].isvalid) { // The Control is NOT Valid
$("#" + Page_Validators[i].controltovalidate).removeClass("makeMeGreen").addClass("makeMeRed");
allValid = false;
} else { // Control is valid
$("#" + Page_Validators[i].controltovalidate).removeClass("makeMeRed").addClass("makeMeGreen");
};
};
};
};
}
This will loop through all controls on the page that have an ASP.NET validator attached, and then add or remove a class depending if they are valid or not.
Obviously from here you can limit the function to a specific control by matching the controlToValidate property, and you can restyle, add controls, change classes but this should hopefully provide you a decent base to work from.

What does the createRange() function return

I have this code , which popups the highlighted text in the page . The problem I have is that the highlighted text always pops up at the end of the page ?
Does the create range return an integer value due to which I can make it popup at the middle of the line ? Is there any way that the pop up can be popped below the line under which the text is highlighted ?
The code so far is as follows
var getSelected = function() {
var t = '';
if(window.getSelection) {
t = window.getSelection();
} else if(window.selection) {
t=window.selection.createRange();
}
return t;
}
$(document).ready(function(){
$(document).bind("mouseup",mouseup);
$(document).bind("mousedown",mousedown);
});
var mouseup=function(){
$('div#pop-up').show();
var st = getSelected();
document.getElementById("pop-up").innerHTML=st;
}
var mousedown = function(){
$('div#pop-up').hide();
}
The above is the JS file for it . While the html just has a div with an id of pop-up
The problem isn't within the selector . But the problem is where it popups . It popsup at the end of the page due to the innerHTML but I want it to popup near where the text is selected . How do I do that ?
The selection range is an object that describes what text you've highlighted, in which element, and its general state.
The reason there's an if there is because this is a semi-recent (been around for years, just not 100% compatible, so few use it) standard, and different vendors used different implementations.
If you're on a browser which supports getSelection, then get the object that function returns...
...else, if your browser has a window.selection object, which holds methods for reading the actual selection, then do that.
It's like the old days of doing event-listening.
Now, everything is .addEventListener.
Once upon a time, it was:
if (document.addEventListener) { el.addEventListener(evt, func, false); }
else {
func = function () { func.call(el, window.event); };
el.attachEvent(evt, func);
}
Or XMLHttpRequest versus window.ActiveXObject("msxml2.xmlhttp");s...
remove the else if condition it should be just else in your case:
var getSelected = function() {
var t = '';
if(window.getSelection) {
t = window.getSelection();
} else {
t=window.selection.createRange();
}
return t;
}

html5 details tag open one by one javascript function working strange

I'm using the HTML5 tag details for a FAQ section of a company. An issue was that if the user opened another question the other question would not close automatically. Therefore I searched on the web and found the following solution:
function thisindex(elm){
var nodes = elm.parentNode.childNodes, node;
var i = 0, count = i;
while( (node=nodes.item(i++)) && node!=elm )
if( node.nodeType==1 ) count++;
return count;
}
function closeAll(index){
var len = document.getElementsByTagName("details").length;
for(var i=0; i<len; i++){
if(i != index){
document.getElementsByTagName("details")[i].removeAttribute("open");
}
}
}
This code does work properly in some sense but it has some small issues. Sometimes it opens two questions at the same time and works funny. Is there a method so this can work properly? This should work on desktop, tablet and mobile.
NOT DESIRED EFFECT:
I created a fiddle http://jsfiddle.net/877tm/ with all the code. The javascript is doing it's work there, ig you want to see it live click here.
Since you tagged jQuery, you can just do this:
$('.info').on('click', 'details', function () {
$('details').removeAttr('open');
$(this).attr('open', '');
});
All this does is remove the open attribute of all detail tags when you click on any detail, and then reopen the one you just clicked on.
http://jsfiddle.net/877tm/3/
the hole thisindex function is stupid and can be removed. You can simply pass the details element to closeAll.
The closeAll is quite stupid, too it searches for details in the for loop, wow.
// closeAll
function closeAll (openDetails){
var details = document.getElementsByTagName("details");
var len = details.length;
for(var i=0; i<len; i++){
if(details[i] != openDetails){
details[i].removeAttribute("open");
}
}
}
In case you want write clean code.
You should use $.on or addEventlistener.
Try to be in a specific context and only manipulate details in this context. (What happens, if you want to have two accordion areas. Or some normal details on the same site, but not inside of the group.)
Only search for details in the group, if details was opened not closed.
Give the boolen open property some love, instead of using the content attribute
I made small fiddle, which trys to do this.
To make details as accordion tag you can use below jquery.
$("#edit-container details summary").click(function(e) {
var clicked = $(this).attr('aria-controls');
closeAll(clicked);
});
function closeAll (openDetailid){
$("#edit-container details" ).each(function( index ) {
var detailid = $(this).attr('id');
var detailobj = document.getElementById(detailid);
if (openDetailid != detailid ) {
detailobj.open = false;
}
});
$('html, body').stop().animate({ scrollTop: $('#'+openDetailid).offset().top -100 }, 1000);
}
I have a solution with jQuery
$('details').on('click', function(ev){ //on a '<details>' block click
ev.preventDefault(); //prevent the default behavior
var attr = $(this).attr('open');
if (typeof attr !== typeof undefined && attr !== false){ //if '<details>' block is open then close it
$(this).removeAttr('open');
}else{ // if '<details>' block is closed then open the one that you clicked and close all others
var $that = $(this); //save the clicked '<details>' block
$(this).attr('open','open'); //open the '<details>' block
$('details').each(function(){ //loop through all '<details>' blocks
if ($that.is($(this))){ //check if this is the one that you clicked on, if it is than open it or else close it
$(this).attr('open','open');
}else{
$(this).removeAttr("open");
}
});
}
});

jQuery.css('display') only returns inline

I am trying to get checked options from a table which are set inline. There is a search function, which sets $(element).css('display','none') on objects in which there is no match with the search. Anyways, this piece of code will only return inline, no matter what the elements are set to. Even if I manually set all of them to display: none in the table itself, the alert will return inline for every single object in the table. Is there any solution to this?
JS code:
function pass_QR() {
var i = 0;
var array = [];
$("input:checkbox:checked").each(function () {
i++;
alert($(this).css('display'));
if ($(this).val() !== 0 && $(this).css('display') === 'inline') {
array.push($(this).val());
}
});
}
Fundamentally, css("display") does work, so something else is going on.
I suspect one of two things:
The checkboxes that you're making display: none are never checked, and so you don't see them in your each loop.
You're not making the checkboxes display: none, but instead doing that to some ancestor element of them. In that case, $(this).is(":visible") is what you're looking for.
Here's an example of #2: Live Copy | Live Source
<div id="ancestor">
<input type="checkbox" checked>
</div>
<script>
$("#ancestor").css("display", "none");
console.log("display property is now: " +
$("input:checkbox:checked").css("display"));
console.log("visible tells us what's going on: " +
$("input:checkbox:checked").is(":visible"));
</script>
...which outputs:
display property is now: inline-block
visible tells us what's going on: false
Applying that to your code:
function pass_QR() {
var i = 0;
var array = [];
$("input:checkbox:checked").each(function () {
i++;
alert($(this).css('display'));
if ($(this).val() !== 0 && $(this).is(':visible')) {
// Change is here -----------------^^^^^^^^^^^^^^
array.push($(this).val());
}
});
}
Side note: Every time you call $(), jQuery has to do some work. When you find yourself calling it repeatedly in the same scope, probably best to do that work once:
function pass_QR() {
var i = 0;
var array = [];
$("input:checkbox:checked").each(function () {
var $this = $(this); // <=== Once
i++;
alert($this.css('display'));
if ($this.val() !== 0 && $this.is(':visible')) {
// Other change is here -------^^^^^^^^^^^^^^
array.push($this.val());
}
});
}
try following:
$("input:checkbox:checked").each(function(i,o){
console.log($(this).css("display"));
});
working fiddle here: http://jsfiddle.net/BcfvR/2/

Categories