html5 details tag open one by one javascript function working strange - javascript

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");
}
});
}
});

Related

Adding to local storage only working after refreshing the page after button navigation to another page

I am developing an app that allows user to search for fishing lakes in their area. To do this they can type in their location which then displays fisheries near them that i have identified in the Json data. The search works perfectly with the code below i have written (i know it maybe a little flaky as i am not the best programmer).
//search
$(document).ready(function(){
$('#exampleSearch').keyup(function() {
var searchVal = $('#exampleSearch').val();
$('#results').empty();
console.log(searchVal);
var results = [];
$.getJSON("/data/locations.json",function(locations) {
for (var i = 0; i < locations.length; i++) {
if (locations[i].location.match(searchVal)) {
results.push(locations[i]);
}
}
$.each(results, function(index,result) {
var $resultsLi = $(
'<div class="row">'+
'<div class="twelve columns profile-information ">'+
'<div class="profile-title">'+
'<h6>'+result.name+'</h6>'+
'</div> ' +
' <img class= "favourites-pic" src="'+ result.image +'" alt="Fishery">'+
'<a class="view" href="'+ result.url + '" >View</a>'+
'</div>'+
' </div>'
)
$("#results").append($resultsLi);
});
});
});
});
I have now added a feature where users can now add their search results to their favourites page, by click a Add to favorites button on the page of the fishery they found from their search with the following code:
Javascript:
//Add to Favourites
$(function() {
$( ".addFavourites" ).on("click", function() {
try {
$(this).attr('disabled', true);
var locIdToAdd = $(this).closest("p").attr("id");
var myFavouriteLoc=JSON.parse(localStorage.getItem("favLoc"));
if (myFavouriteLoc == null) {
myFavouriteLoc = [];
}
if (myFavouriteLoc != null) {
for ( var j = 0; j < myFavouriteLoc.length; j++) {
if ( locIdToAdd == myFavouriteLoc[j]) {
alert("This property is already in your favourites");
myFavouriteLoc = [];
}
}
}
myFavouriteLoc.push(locIdToAdd);
localStorage.setItem("favLoc", JSON.stringify(myFavouriteLoc));
} catch (e) {
if (e == QUOTA_EXCEEDED_ERR) {
console.log("Error: Local storage limit exceeds");
} else {
console.log("ERROR: Saving to local storge.");
}
}
});
});
Html:
<p id="loc 1">
<input class="button-primary green addFavourites" type="submit" value="Add to Favourites">
</p>
THE PROBLEM
Upon clicking the 'view' button on the search page results, and navigating to a fishery page. I have a problem where i have to then refresh the page again before the add to favourites button will add anything to the local storage, it is clickable, but nothing happens. once i refresh the page it works fine.
Can anyone help with why i have to refresh the page first? any help is a appreciated :)
Try this jquery code to add favorites, it should work on each click:
$( document ).on("click", ".addFavourites", function(e) {
e.preventDefault();
try {
$(this).attr('disabled', true);
var locIdToAdd = $(this).closest("p").attr("id");
var myFavouriteLoc=JSON.parse(localStorage.getItem("favLoc"));
if (myFavouriteLoc == null) {
myFavouriteLoc = [];
}
if (myFavouriteLoc != null) {
for ( var j = 0; j < myFavouriteLoc.length; j++) {
if ( locIdToAdd == myFavouriteLoc[j]) {
alert("This property is already in your favourites");
myFavouriteLoc = [];
}
}
}
myFavouriteLoc.push(locIdToAdd);
localStorage.setItem("favLoc", JSON.stringify(myFavouriteLoc));
} catch (e) {
if (e == QUOTA_EXCEEDED_ERR) {
console.log("Error: Local storage limit exceeds");
} else {
console.log("ERROR: Saving to local storge.");
}
}
});
Just make sure you are not putting this jquery function inside $(document).ready(function(){}) or $(function(){}) because it is not required
I can't see any code relating to loading the list of favourites, but I think it's fair to assume to that when the page loads you're loading the items from storage and placing them in, and then the pages where you add favourites and view favourites don't involve any loading between them. Therefore, of course, loading the items for the favourites list will only be performed on load, and so placing new items into the localStorage without reloading will not add them. So I'm going to cover a few things.
Firstly, don't use == and !=. They perform type coercion, (numbers to strings, strings to numbers, null to undefined, etc.), the rules for which are unintuitive and unmemorable and are best simply avoided, lest they cause difficult to diagnose problems in your code. Use === and !== instead, these behave just like == and != does in other languages. If you want to perform type coercion I'd do so explicitly (e.g. Number('5')).
Secondly, and just as a suggestion, if you implement the local storage as an object, you won't need to loop and can simply use property names. So you can replace
if (myFavouriteLoc != null) {
for ( var j = 0; j < myFavouriteLoc.length; j++) {
if ( locIdToAdd == myFavouriteLoc[j]) {
With simply
if (myFavouriteLoc && myFavouriteLoc[locIdToAdd]) {
// ...
}
The object && object.property idiom will perform a truthy check on both the object and the property, only passing if they are both truthy (not falsy). Here's a list of falsy values.
So, to answer your actual question. I'd say you have two options:
Reload the page on each addition, or
AJAXily add the entry to your views when favourites are added. Favourites will then, of course, need be removed
Going with my assumption in the first paragraph, I'd say that the second option is your best bet, as the constant page refreshes would provide a bad user experience.
So here's the jsfiddle
(function bindBtns() {
$('.Table-btn--add').click(function () {
addBinding($(this));
});
$('.Table-btn--remove').click(function () {
removeBinding($(this));
});
})();
Here is an immediately invoked function, meaning it's run straight away. This will bind the existing buttons' behaviour, depending on whether they are add or remove buttons.
function addBinding($btn) {
$btn.parent()
.detach()
.appendTo($('#favourites'));
// add to local storage here
modifyAttributes($btn);
}
Here's the behaviour for the binding, fairly straight forward, but:
Select the buttons parent, i.e. the row
Detach it from the DOM, i.e. the current table
Attach it to the other table
Rebind the button to perform the opposite behaviour, shown below
Place your behaviour for adding or removing from local storage where I've placed the comments.
removeBinding is exactly the same except it appends to the other table.
function modifyAttributes($btn) {
if ($btn.hasClass('Table-btn--add')) {
$btn.text('Remove')
.removeClass('Table-btn--add')
.addClass('Table-btn--remove')
.off('click')
.click(removeBinding.bind(null, $btn));
} else if ($btn.hasClass('Table-btn--remove')) {
$btn.text('Add')
.removeClass('Table-btn--remove')
.addClass('Table-btn--add')
.off('click')
.click(addBinding.bind(null, $btn));
}
}
Split on whether this is an adding button, or a removing button. (The next steps will be for both versions.)
Update the text to be the opposite, (add -> remove, remove -> add)
Remove the current class
Add the new class
Remove the old click binding
Bind the new click binding. Read about .bind here
In your version you would replace .Table-btn--add with .addFavourites, and place addBinding($(this)) in your try block, and then you can just copy over the other functiona verbatim (I think).
Good luck! Sorry that got so long, and let me know how it goes.

IE click event span not triggered

I'm having this webpage
http://pocolocoadventures.be/reizen/
And it should filter (with isotope.js) the travelboxes on the page.It does in safari, chrome, firefox, opera, .. but in IE, the filter doesn't work. Even worse, JS doesn't react at all at a click event on te span.
This is the piece of js
// Travel Isotope
var container = $('#travel-wrap');
container.isotope({
animationEngine : 'best-available',
itemSelector: '.travel-box ',
animationOptions : {
duration : 200,
queue : false
},
});
$(".filters span").click(function(){
var elfilters = $(this).parents().eq(1);
if( (elfilters.attr("id") == "alleReizen") && elfilters.hasClass("non-active") )
{
$(".label").each(function(){
inActive( $(this) );
});
setActive(elfilters);
}
else{
//set label alleReizen inactive
inActive( $("#alleReizen") );
if( elfilters.hasClass("non-active") ){
setActive(elfilters);
}
else{
inActive(elfilters);
}
}
checkFilter();
var filters=[];
$(".search.filters").children().each(function(){
var filter = $(this).children().children().attr("data-filter");
if( $(this).hasClass("non-active") ){
filters = jQuery.grep(filters, function(value){
return value != filter;
});
}
else{
if(jQuery.inArray(filter,filters) == -1){
filters.push(filter);
}
}
});
filters = filters.join("");
filterItems(filters);
});
function filterItems(filters){
console.log("filter items with filters:" + filters);
container.isotope({
filter : filters,
}, function noResultsCheck(){
var numItems = $('.travel-box:not(.isotope-hidden)').length;
if (numItems == 0) {
$("#no-results").fadeIn();
$("#no-results").css("display", "block");
}
else{
$("#no-results").fadeOut();
$("#no-results").css("display", "none");
}
});
}
function setActive(el){
el.removeClass("non-active");
var span = el.find('i');
span.removeClass("fa-check-circle-o").addClass("fa-ban");
}
function inActive(el){
el.addClass("non-active");
var span = el.find('i');
span.removeClass("fa-ban").addClass("fa-check-circle-o")
}
function checkFilter(){
var filterdivs = $('.filters span').parent().parent();
if( filterdivs.not('.non-active').length == 0 ){
setActive( $("#alleReizen") );
}
var filterLabels = $(".filters .label");
if( filterLabels.not('.non-active').length == 0){
setActive( $("#alleReizen") );
}
}
function noResultsCheck() {
var numItems = $('.item:not(.isotope-hidden)').length;
if (numItems == 0) {
//do something here, like turn on a div, or insert a msg with jQuery's .html() function
alert("There are no results");
}
}
Probably something small and stupid; but I can't find it..
Thanks in advance!
On your website you've build the buttons like this:
<button>
<span>
</span>
</button>
Now the button element is designed to be a button. It differs from the input button. In the latter you'd set the caption using value. In the button element you set it as a text node. The button element can contain elements like a span. The spec isn't very clear about whether or not you should have event handlers on the children of the button element. It's a browser developers interpretation of allowing it or not.
This problem has been posted here before (a few times)
span inside button, is not clickable in ff
Missing click event for <span> inside <button> element on firefox
It seems that Firefox is allowing it, based upon your findings. IE isn't. So to be on the safe side: use the button the way it was intended.
Wrap the button inside a span (not really logical)
Put the click handler on the button.
$(".filters button").click(...);
played around in the console a bit, and this seemed to work well.
$(".filters").on('click', 'span', function(){
// foo here
console.log('foo');
});
Maybe the filters are manipulated by one of your js files after page load?
.on will allow you to select a container which listens on changes that happen inside it, passing the element you want the actual action to work on.
If it's ok for you, I'd suggest to use the <button> element, instead of the <span>.
Let me know if that works for you.

Detect if a form is visible or not

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")

Tab panel problem

How can I get selected tab index of tab panel using JavaScript and then assign a button validation group according to selected tab index?
This is my code so far:
function ActiveTab()
{
var a= $find("tcEmployee").get_activeTabIndex();
var add=document.setElementById('<%=btnAddRecord.ClientID%>');
var update=document.getElementById('<%= btnUpdateRecord.ClientID%>');
var delet document.getElementById('<%= btnDeleteRecord.ClientID%>');
if (a == 0)
{
add.ValidationGroup = "Insertion";
update.ValidationGroup = "Insertion";
delet.ValidationGroup = "Insertion";
}
else if (a == 1)
{
add.ValidationGroup = "Insertion1";
update.ValidationGroup = "Insertion1";
delet.ValidationGroup = "Insertion1";
}
else
{
add.ValidationGroup = "Insertion2";
update.ValidationGroup = "Insertion2";
delet.ValidationGroup = "Insertion2";
}
}
You can try with Jquery tab.
Have you considered using a click event on the tab?
Maybe look at the jQueryUI tab control and get the event that way.
Also, try including more information in your question so we can actually target our answers to an actual problem
edit
OK, looking at your code i think jQuery is going to be your friend.
if you give each of your controls an ID like you are doing and also a class. So for the "add" alement you may give it a class of "ADD" and for"update" a class of "UPDATE".
Then you can use jQuery like this;
$(".UPDATE").click(function(){
alert( $(this).attr("id") );
})
$(".ADD").click(function(){
alert( $(this).attr("id") );
})
etc....

Is there an easy way to remove items from a page depending on what I write in a textbox (similar to autocomplete)?

I am trying to implement a feature where if there are 50 links on a page for example, you start typing in a box and the items start to disappear depending on what you type. It is the same idea of autocomplete but instead of creating a list of possible links, you remove the irrelevant ones from the page.
I am sorry if my explanation is not clear enough, if you need more details please ask me and I will answer in the comments.
Thank you in advance.
var anchors = $('a');
$('#filter').bind('keyup', function () {
var filterValue = $.trim(this.value);
anchors.hide().filter(function () {
return $(this).text().indexOf(filterValue) !== -1;
}).show();
});
Example
$(".selector-for input").keyup(function(){
var text = this.value;
$(".selector-for a").each(function() {
var it = $(this);
it.text().indexOf(text) < 0 ? it.hide() : it.show()
});
});
Try using this.Call this function in your 'onKeyUp' event of the textbox
function hideFiltered(){
var text=$("#textbox").val();
var links=$('a[name="linksToHide"]');
for(var i=0;i<links.length;i++){
link=links[i];
if(link.html().indexof(text)>0){
link.hide();
}else{
link.show();
}
}
}
}

Categories