I have a Foundation 5 dropdown on my page, which works fine. However when I submit a form in the dropdown area a new element is added to the top of the page so all the elements are moved down.
After that, all my dropdowns stop to work. (If I skip the adding of the element to the DOM, everything is working fine)
My dropdown:
<div id="file-tab">
<i data-dropdown="shareForm16" aria-controls="shareForm16" aria-expanded="false" class="iconTrigger"></i>
<form data-dropdown-content class="share-form f-dropdown content" aria-hidden="true" tabindex="-1" action="" id="shareForm16">
...
</form>
</div>
I thought I need to rebind the foundation event listeners, but it doesn't work. Maybe I am just doing it wrong.
$('#file-tab').on("submit", 'form.share-form',function(e){
e.preventDefault();
var groupName = $(form.target).find('input[type="text"]').val();
var id = $(e.target).parent().children('input[type="hidden"]').val();
if (groupName) {
$(e.target).trigger('click');
window.currentFTT.share(id ,groupName); // adds the element to the DOM
// my attempt to rebind:
$('#'+e.target.id).foundation({bindings: 'events'});
$('i[aria-controls="'+e.target.id+'"]').foundation({bindings: 'events'});
}
});
Instead of calling foundation for individual elements, make a general call to foundation, passing 'reflow' as a parameter:
$(document).foundation('reflow');
E.g:
$('#file-tab').on("submit", 'form.share-form',function(e){
e.preventDefault();
var groupName = $(form.target).find('input[type="text"]').val();
var id = $(e.target).parent().children('input[type="hidden"]').val();
if (groupName) {
$(e.target).trigger('click');
window.currentFTT.share(id ,groupName); // adds the element to the DOM
// my attempt to rebind:
$(document).foundation('reflow');
}
});
Related
*UPDATE:I am new to jQuery, as well as using XPath, and I am struggling with getting a proper working solution that will blur a dynamically created HTML element. I have an .onblur event hooked up (doesn't work as expected), and have tried using the $(document.activeElement), but my implementation might be incorrect. I would appreciate any help in creating a working solution, that will blur this element (jqInput) when a user clicks anywhere outside the active element. I have added the HTML and jQuery/JavaScript below.
Some ideas I have had:
(1) Use XPath to select a dynamic HTML element (jqInput), and then use jQuery's .onClick method to blur a this element, when a user clicks anywhere outside of the area of the XPath selected element.
(2) Use the $(document.activeElement) to determine where the .onblur should fire:
var thisTitle = input0;
var activeElement = $(document.activeElement);
if (thisTitle != activeElement) {
jqInput.hide();
_layout.viewHeaderTextInput.inputOnBlurHandler(canvasObj, jqHeaderText, jqInput);
}
I am open to all working solutions. And hopefully this will answer someone else's question in the future.
My challenge: Multiple elements are active, and the .onblur does not fire. See the image below:
NOTE: The <input /> field has focus, as well as the <div> to the left of the (the blue outline). If a user clicks anywhere outside that <input />, the blur must be applied to that element.
My Code: jQuery and JavaScript
This is a code snippet where the variable jqInput and input0 is created:
var jqInput = null;
if (jqHeaderText.next().hasClass("inline-editable"))
{
//Use existing input if it already exists
jqInput = jqHeaderText.next();
}
else
{
//Creaet a new editable header text input
jqInput = $("<input class=\"inline-editable\" type=\"text\"/>").insertAfter(jqHeaderText);
}
var input0 = jqInput.get(0);
//Assign key down event for the input when user preses enter to complete entering of the text
input0.onkeydown = function (e)
{
if (e.keyCode === 13)
{
jqInput.trigger("blur");
e.preventDefault();
e.stopPropagation();
}
};
This is my .onblur event, and my helper method to blur the element:
input0.onblur = function ()
{
_layout.viewHeaderTextInput.inputOnBlurHandler(canvasObj, jqHeaderText, jqInput);
};
inputOnBlurHandler: function (canvasObj, jqHeaderText, jqInput)
{
// Hide input textbox
jqInput.hide();
// Store the value in the canvas
canvasObj.headingText = jqInput.val();
_layout.updateCanvasControlProperty(canvasObj.instanceid, "Title", canvasObj.headingText, canvasObj.headingText);
// Show header element
jqHeaderText.show();
_layout.$propertiesContent.find(".propertyGridEditWrapper").filter(function ()
{
return $(this).data("propertyName") === "Title";
}).find("input[type=text]").val(canvasObj.headingText); // Update the property grid title input element
}
I have tried using the active element, but I don't think the implementation is correct:
var thisTitle = input0;
var activeElement = $(document.activeElement);
if (thisTitle != activeElement) {
jqInput.hide();
_layout.viewHeaderTextInput.inputOnBlurHandler(canvasObj, jqHeaderText, jqInput);
}
My HTML code:
<div class="panel-header-c">
<div class="panel-header-wrapper">
<div class="panel-header-text" style="display: none;">(Enter View Title)</div><input class="inline-editable" type="text" style="display: block;"><div class="panel-header-controls">
<span></span>
</div>
</div>
</div>
I thank you all in advance.
EDIT: I cleaned up the code a bit and narrowed down the problem.
So I'm working on a Wordpress site, and I'm trying to incorporate drop-downs into my menu on mobile, which means I have to use jQuery to assign classes and id's to my already existing elements. I have this code that already works on premade HTML, but fails on dynamically created id's.
Here is the code:
...
var menuCount = 0;
var contentCount = 0;
//find the mobile menu items
var submenus = $('[title="submenu"]');
if (submenus.length && submenus.parent('.fusion-mobile-nav-item')) {
console.log(submenus);
submenus.addClass('dropdown-title').append('<i id="dropdown-angle" class="fa fa-angle-down" aria-hidden="true"></i>');
submenus.each(function() {
$(this).attr("href", "#m" + menuCount++);
})
var content = submenus.parent().find('ul.sub-menu');
content.addClass('dropdown-content');
content.each(function() {
$(this).attr("id", "m" + contentCount++);
})
}
$(document).on('click', '.dropdown-title', function(e) {
var currentAttrValue = $(this).attr('href');
if ($(e.target).is('.d-active') || $(e.target).parent('.dropdown-title').is('.d-active')) {
$(this).removeClass('d-active');
$(currentAttrValue).slideUp(300).removeClass('d-open');
} else {
$('.dropdown-title').removeClass('d-active');
$('.dropdown-content').slideUp(300).removeClass('d-open');
$(this).addClass('d-active');
console.log($(currentAttrValue));
//THIS LINE FAILS
$(currentAttrValue).slideDown(300).addClass('d-open');
}
e.preventDefault();
});
I've registered the elements with the class dropdown-title using $(document).on(...) but I can't figure out what I need to do to register the elements with the custom ID's. I've tried putting the event callback inside the .each functions, I've tried making custom events to trigger, but none of them will get the 2nd to last line of code to trigger. There's no errors in the console, and when I console log the selector I get this:
[ul#m0.sub-menu.dropdown-content, context: document, selector: "#m0"]
0
:
ul#m0.sub-menu.dropdown-content
context
:
document
length
:
1
selector
:
"#m0"
proto
:
Object[0]
So jQuery knows the element is there, I just can't figure out how to register it...or maybe it's something I'm not thinking of, I don't know.
If you are creating your elements dynamically, you should be assigning the .on 'click' after creating those elements. Just declare the 'on click' callback code you posted after adding the ids and classes instead of when the page loads, so it gets attached to the elements with .dropdown-title class.
Check this jsFiddle: https://jsfiddle.net/6zayouxc/
EDIT: Your edited JS code works... There also might be some problem with your HTML or CSS, are you hiding your submenus? Make sure you are not making them transparent.
You're trying to call a function for a attribute, instead of the element. You probably want $(this).slideDown(300).addClass('d-active'); (also then you don't need $(this).addClass('d-active'); before)
Inside submenus.each loop add your callback listener.
As you are adding the class dropdown-title dynamically, it was not available at dom loading time, that is why event listener was not attached with those elemnts.
var menuCount = 0;
var contentCount = 0;
//find the mobile menu items
var submenus = $('[title="submenu"]');
if (submenus.length && submenus.parent('.fusion-mobile-nav-item')) {
console.log(submenus);
submenus.addClass('dropdown-title').append('<i id="dropdown-angle" class="fa fa-angle-down" aria-hidden="true"></i>');
submenus.each(function() {
$(this).attr("href", "#m" + menuCount++);
// add callback here
$(this).click( function(e) {
var currentAttrValue = $(this).attr('href');
if ($(e.target).is('.d-active') || $(e.target).parent('.dropdown-title').is('.d-active')) {
$(this).removeClass('d-active');
$(currentAttrValue).slideUp(300).removeClass('d-open');
} else {
$('.dropdown-title').removeClass('d-active');
$('.dropdown-content').slideUp(300).removeClass('d-open');
$(this).addClass('d-active');
console.log($(currentAttrValue));
$(currentAttrValue).slideDown(300).addClass('d-active');
}
e.preventDefault();
});
})
var content = submenus.parent().find('ul.sub-menu');
content.addClass('dropdown-content');
content.each(function() {
$(this).attr("id", "m" + contentCount++);
})
}
Turns out my problem is that jQuery is adding to both the mobile menu and the desktop menu, where the desktop menu is being loaded first when I search for that ID that's the one that jQuery finds. So it turns out I was completely wrong about my suspicions.
I have three content boxes that i want to show and hide using controls.
The HTML is as follows:
<div id="leermat1">
Content here
<a class="pag-next">Next</a>
<a class="pag-prev">Previous</a>
</div>
<div id="leermat2">
Content here
<a class="pag-next">Next</a>
<a class="pag-prev">Previous</a>
</div>
<div id="leermat3">
Content here
<a class="pag-next">Next</a>
<a class="pag-prev">Previous</a>
</div>
I have the two anchors pag-next and pag-prev that will control which of the content divs should be visible at any given point:
I want to write jquery such as, when #leermat1 'pag-next' is clicked, it hides #leermat1 and shows #leermat2. Then when #leermat1 is hidden and #leermat2 shows, when '.pag-next' is clicked, it hides #leermat2, and shows #leermat3.
Also the 'pag-prev' should work the same way.
I started with the following but dont know where to go from here.
$(document).ready(function() {
$('.pag-next').on('click',function() {
$('#leermat1').addClass('hide');
$('#leermat2').addClass('show');
});
});
One more thing is that the '.pag-next' should stop functioning after it has shown #leermat3.
You need this
$('[class^=pag-]').click(function() {
var elem = $('[id^=leermat]').filter(":visible"); // take the visible element
var num = Number(elem[0].id.match(/\d+$/)[0]); // take the number from it
var step = $(this).is('.pag-next') ? 1 : -1; // ternary operator
$('#leermat'+ (num + step)).show(); // show next or back
elem.hide(); // hide the visible element
});
Looks like in your anchor tag you have not given it a class.
Next
You then go on in your JQuery code to add a click function to a class which does not exist.
$('.pag-next').on('click',function()
Try adding class="pag-next" to your anchor tag.
This is what worked for me through a little trial and error. Although I am not sure if this is the most efficient solution.
$('#leermat1 .pag-next').on('click',function(){
$('#leermat1').addClass('hide');
$('#leermat1').removeClass('show');
$('#leermat3').addClass('hide');
$('#leermat3').remove('show');
$('#leermat2').addClass('show');
});
$('#leermat2 .pag-next').on('click',function(){
$('#leermat1').removeClass('show');
$('#leermat2').addClass('hide');
$('#leermat2').removeClass('show');
$('#leermat3').addClass('show');
});
$('#leermat2 .pag-prev').on('click',function(){
$('#leermat2').addClass('hide');
$('#leermat2').removeClass('show');
$('#leermat1').addClass('show');
$('#leermat3').removeClass('show');
});
$('#leermat3 .pag-prev').on('click',function(){
$('#leermat3').addClass('hide');
$('#leermat2').addClass('show');
$('#leermat1').addClass('hide');
$('#leermat3').removeClass('show');
$('#leermat1').removeClass('show');
});
I have added a dynamic gridster widget with a close button.
gridster.add_widget('<li id="wdg_news" data-row="3" data-col="5" data-sizex="2" data-sizey="3" class="tile-orange" data-ng-hide="!news.length"> <div class="db-wid"><div class="db-title"> Newss <i class="icon-close"></i></div><div class="db-content cus_scroll" style="height:304px;"> <div class="w-list"> <c:forEach items="${newsList}" var="news"><div>${news.date} - ${news.summery}</div></c:forEach></div></div></div> </li>', 2, 3);
all the data is getting loaded to it without any problem and when i click the close button it goes to login screen insted of calling the javascipt.
// Remove Grid
$(".db-ctrl").on("click", function(e) {
e.preventDefault();
var parentLi = $(this).parents("li");
var eq_val = $(".gridster ul li").index(parentLi);
gridster.remove_widget($('.gridster li').eq(eq_val));
console.log(eq_val);
saveGrid();
})
This doesnt happen to already created widgets. Only to ones which created dynamically using gridster.add_widget method.
Please Help in this matter. Thanks in advance.....
What you need to do is that instead of .on("click") you need to use .delegate("click").
.on listens for elements that already exist in the DOM. .delegate listens for elements that already exist in the DOM and are added in the future (e.g. when you dynamically add a widget).
So you just need to modify your click handler
$(".gridster").delegate("click", ".db-ctrl", function(e) {
e.preventDefault();
var parentLi = $(this).parents("li");
var eq_val = $(".gridster ul li").index(parentLi);
gridster.remove_widget($('.gridster li').eq(eq_val));
console.log(eq_val);
saveGrid();
})
I'm trying to create previous / next buttons on each accordion body.
I can't figure out a way to collapse / expand a certain section. I tried removing the class in from the accordion-body but that does not seem to collapse.
$(".accordion-body").each(function(){
if($(this).hasClass("in")) {
$(this).removeClass("in");
}
});
Also whenever or whatever I use the .collapse method on, I get a javascript error saying that object has no method collapse.
The in class is just an indicator that a section is open. The Javascript module applies the same inline styles as .in does, so removing the class is insufficient.
You need to use the module's API to programmatically interact with the accordion, via the .collapse() method:
$('.accordion-body').each(function(){
if ($(this).hasClass('in')) {
$(this).collapse('toggle');
}
});
Or, you can condense this down to:
$('.accordion-body.in').collapse('toggle');
If you want only to collapse any open sections:
$('.accordion-body').collapse('hide');
If you want only to expanded any closed sections:
$('.accordion-body').collapse('show');
Here is another solution:
/**
* Make an accordion active
* #param {String} id ID of the accordion
*/
var activateAccordion = function (id) {
// Get the parents
var parents = $('a[href="#' + id + '"]').parents('.panel-group').children('.panel');
// Go through each of the parents
$.each(parents, function (idx, obj) {
// Check if the child exists
var find = $(obj).find('a[href="#' + id + '"]'),
children = $(obj).children('.panel-collapse');
if (find.length > 0) {
// Show the selected child
children.removeClass('collapse');
children.addClass('in');
} else {
// Hide the others
children.removeClass('in');
children.addClass('collapse');
}
});
};
The important part of the code is the combination, remembering the .collapse class, not just using .in:
// Show the selected child
children.removeClass('collapse');
children.addClass('in');
and
// Hide the others
children.removeClass('in');
children.addClass('collapse');
The above example has been tested in Twitter's Bootstrap v3.3.4
This works for accordions in Bootstrap 3:
var selector = $('.panel-heading a[data-toggle="collapse"]');
selector.on('click',function() {
var self = this;
if ($(this).hasClass('collapsed')) {
$.each(selector, function(key, value) {
if (!$(value).hasClass('collapsed') && value != self) {
$(value).trigger('click');
}
});
}
});
I'm simulating a click of the other accordion tabs with $(value).trigger('click');. According to the API you should just be able to use the .collapse() method i.e. $(value).collapse('hide');. But it doesn't work for some reason so trigger it is...
For this kind of problem use addClass("in"); only because of using ".collapse('toggle/Hide/Show');" will disturb the future toggle functionality.
Another related use case is when we have forms inside accordions & we want to expand accordion with validation errors. Extending the answer by Daniel Wright
https://stackoverflow.com/a/12698720/6504104, we can do something like
/**
* Expands accordions that have fields with errors
*
*/
var _expandAccordions = function () {
$form = $('.my-input-form');
// you can adjust the following lines to match your form structure / error classes
var $formGroups = $form.find('.form-group.has-error');
var $accordions = $formGroups.closest('.accordion-body');
$accordions.each(function () {
$(this).collapse('show');
});
};
I did,
$('.mb-0').click(function(){
$('.collapse').collapse('hide');
$('.collapse.in').collapse('show');
});
and this works for me
Using Bootstrap 4 add the following buttons inside the card body
<input type="button" class="btn btn-secondary btn-block btn-sm mt-3 text-center card-proceed-next" value="Proceed" />
<input type="button" class="btn btn-secondary btn-block btn-sm mt-3 text-center card-proceed-prev" value="Previous" />
Add the following javascript (includes Azhar Khattak show panels with validation errors):
$(function () {
$('.card-proceed-next').click(function (e) {
e.preventDefault();
$(e.target).closest('.collapse').collapse('hide'); // hide current accordion panel
$(e.target).closest('.card').next('.card').find('.collapse').addClass('show'); // show next accordion panel
});
$('.card-proceed-prev').click(function (e) {
e.preventDefault();
$(e.target).closest('.collapse').collapse('hide'); // hide current accordion panel
$(e.target).closest('.card').prev('.card').find('.collapse').addClass('show'); // show previous accordion panel
});
var $elErrors = $('#accordion').find('.field-validation-error'); // elements with error class
var $accordionsWithErrors = $elErrors.closest('.collapse'); // accordions with error elements
if ($accordionsWithErrors.length > 0) $('.collapse').collapse(); // collapse all accordion panels due to the first accordion panel shown as default
$accordionsWithErrors.each(function () {
$(this).addClass('show'); // show accordion panels with error messages
});
});