am constructing a slightly more complex drop down menu system using Jquery's slideDown() and slideUp() animations as well as the "hover()" event.
Now I have a certain element which triggers by "hover()", that another element is being displayed. Unfortunately it's not possible to make those two elements, the only childs of another element (since the trigger is in another table).
Still I want this new element which has been displayed, to show until my mouse leaves BOTH the new element as well as the trigger element.
Is there a way to achieve this?
Thanks in advance :)
I used .mouseenter and .mouseleave to achieve what may you want:
jsFiddle
var groups = {};
groups[1] = {
main: false,
sub: false
};
$('.menu').mouseenter(function(e) {
var $target = $(e.target);
var group = $target.attr('data-group');
var type = $target.attr('data-type');
if (!(groups[group].sub || groups[group].main)) {
$('.sub[data-group='+ group +']').toggle(true);
}
groups[group][type] = true;
});
$('.menu').mouseleave(function(e) {
var $target = $(e.target);
var group = $target.attr('data-group');
var type = $target.attr('data-type');
groups[group][type] = false;
if (!(groups[group].sub || groups[group].main)) {
$('.sub[data-group='+ group +']').toggle(false);
}
});
Just track the group of main and sub item. A little ugly, but hope it may helps.
Related
I'm creating a toggle review form that displays when you hit Edit, but I only want this to work on on the review the button is a sibling of, as opposed to all review edit forms on the page (as there may be multiple).
I know how to do this in jquery but am trying to keep this project all vanilla js.
const button = document.querySelector('.toggle-edit-form');
const form = document.querySelector('.edit-review-form');
button.onclick = function() {
// toggle the edit button text on click
button.innerHTML === 'Edit' ? button.innerHTML = "Cancel" : button.innerHTML = "Edit";
// toggle visibility of edit review form
form.classList.toggle('toggle')
};
I could use nextSibling I suppose (as it is currently the next sibling) but would prefer a solution that won't break the code if change the order / html.
Thanks!
#Mike you can try creating a function that uses a while loop to keep track of siblings , I hope this helps
var getSiblings = function (elem) {
// Setup siblings array and get the first sibling
var siblings = [];
var sibling = elem.parentNode.firstChild;
// Loop through each sibling and push to the array
while (sibling) {
if (sibling.nodeType === 1 && sibling !== elem) {
siblings.push(sibling);
}
sibling = sibling.nextSibling
}
return siblings;
};
i'm building a webpage where many span needs to be transitioned from one class to another to create a bg-color fadein effect. Distribution of elements of same classes is mixed through the page, but they are all grouped under common classes.
I want to create a behavior that does the following: when you click any elements of class-n, the other elements of that class transitions, with the clicked element acting as the starting point.
This is mostly figured out, thanks to some help on SO; see the jsfiddle.
$(".div").click(function () {
var itemClasses = this.classList;
var itemThread = itemClasses[1];
colorThread($(this), itemThread);
console.log(itemThread);
});
function colorThread($div, tId) {
tId = '.'+tId;
$div.toggleClass('div-clicked');
setTimeout(function () {
(function togglePrev($div) {
$div.toggleClass('div-clicked');
setTimeout(function () {
togglePrev($div.prev(tId));
}, 100);
})($div.prev(tId));
(function toggleNext($div) {
$div.toggleClass('div-clicked');
setTimeout(function () {
toggleNext($div.next(tId));
}, 100);
})($div.next(tId));
}, 100);
}
However, I am still struggling around a particular issue: I don't want the transition to stop if if encounter different class, I just want it not to toggle and keep iterating. If the jsfiddle, that would translate in all of the same color div to transition, regardless of their placement in the DOM tree.
In my togglePrev/toggleNext function, I have tried something along
if($div.hasClass(".classToTransition"))
{
$div.toggleClass(".div-clicked");
}
but couldn't get it to work properly (it doesn't ieterate to the next elements). There is something that I can't seem to grasp in the structure of that conditional. Anyone has a lead?
You really did manage to complicate something that should be pretty simple ?
$(".div").click(function () {
var coll = $('.'+this.className.replace(/(div-clicked|div)/g, '').trim()),
idx = coll.index($(this).toggleClass('div-clicked'));
$.each(coll, function(i) {
setTimeout(function() {
if (idx + i <= coll.length) coll.eq(idx + i).toggleClass('div-clicked');
if (idx - i >= 0) coll.eq(idx - i).toggleClass('div-clicked');
},i*200);
});
});
FIDDLE
It gets all the elements with the same class as the one currently clicked, and the index of the currently clicked, and then just adds and subtract 1 to the current index to get the next and previous elements. The checks are to make sure it stops when it reaches the end.
I don't want the transition to stop if if encounter different class, I just want it not to toggle and keep iterating
You might want to use nextAll(tId).first()/prevAll(tId).first() to select the next to-be-toggled element: http://jsfiddle.net/35uNW/4/. .next() does only look at the next sibling, and if that doesn't match your tId selector, no element will be selected.
If you want to iterate the different-classed elements so that you wait for each one, but don't want to toggle it, you can use your if-condition but you must remove the tId selector from the next()/prev() calls: http://jsfiddle.net/35uNW/3/.
This was a fun one. I did it a slightly different way, getting all of the matched elements and splitting them into before and after arrays.
var $allItems = $(".div");
$(".div").click(function () {
var itemClasses = this.classList;
var itemThread = itemClasses[1];
colorThread($(this), itemThread);
});
function colorThread($div, classname) {
var tId = '.'+classname,
$divs = $allItems.filter(tId),
index = $divs.index($div),
$before = $divs.slice(0, index),
before = $before.get().reverse(),
$after = $divs.slice(index+1);
$div.toggleClass('div-clicked');
$(before).each(function(i, item){
setTimeout(function () {
$(item).toggleClass('div-clicked');
}, i*100);
});
$($after).each(function(i, item){
setTimeout(function () {
$(item).toggleClass('div-clicked');
}, i*100);
});
}
Here's a working fiddle: http://jsfiddle.net/5sUr4/
I have the following fiddle:
http://jsfiddle.net/XpAk5/63/
The IDs increment appropriately. For the first instance. The issue is when I try to add a sport, while it duplicates, it doesn't duplicate correctly. The buttons to add are not creating themselves correctly. For instance, if I choose a sport, then fill in a position, and add another position, that's all fine (for the first instance). But when I click to add another sport, it shows 2 positions right away, and the buttons aren't duplicating correctly. I think the error is in my HTML, but not sure. Here is the JS I am using to duplicate the sport:
$('#addSport').click(function(){
//increment the value of our counter
$('#kpSport').val(Number($('#kpSport').val()) + 1);
//clone the first .item element
var newItem = $('div.kpSports').first().clone();
//recursively set our id, name, and for attributes properly
childRecursive(newItem,
// Remember, the recursive function expects to be able to pass in
// one parameter, the element.
function(e){
setCloneAttr(e, $('#kpSport').val());
});
// Clear the values recursively
childRecursive(newItem,
function(e){
clearCloneValues(e);
});
Hoping someone has an idea, perhaps I've just got my HTML elements in the wrong order? Thank you for your help! I'm hoping the fiddle is more helpful than just pasting a bunch of code here in the message.
The problem is in your clearCloneValues function. It doesn't differentiate between buttons and other for elements that you do want to clear.
Change it to:
// Sets an element's value to ''
function clearCloneValues(element){
if (element.attr('value') !== undefined && element.attr('type') !== 'button'){
element.val('');
}
}
As #PHPglue pointed out in the comments above, when new positions are added, they are incorrectly replicated (I'm assuming here) to the newly cloned for
There is a similar problem with the add years functionality.
A quick fix would be to initialize a variable with a clone of the original form fields:
var $template = $('div.kpSports').first().clone();
Then change your addSport handler to:
$('#addSport').click(function () {
//increment the value of our counter
$('#kpSport').val(Number($('#kpSport').val()) + 1);
//clone the first .item element
var newItem = $template.clone();
…
});
However, there are no event bindings for the new buttons, so that functionality is still missing for any new set of form elements.
Demo fiddle
Using even a simple, naive string based templates the code can be simplified greatly. Linked is an untested fiddle that shows how it might be done using this approach.
Demo fiddle
The code was simplified to the following:
function getClone(idx) {
var $retVal = $(templates.sport.replace(/\{\{1\}\}/g, idx));
$retVal.find('.jsPositions').append(getItemClone(idx, 0));
$retVal.find('.advtrain').append(getTrainingClone(idx, 0));
return $retVal;
}
function getItemClone(setIdx, itemIdx) {
var retVal = itemTemplate.replace(/\{\{1\}\}/g, setIdx).replace(/\{\{2\}\}/g, itemIdx);
return $(retVal);
}
function getTrainingClone(setIdx, trainingIdx) {
var retVal = trainingTemplate.replace(/\{\{1\}\}/g, setIdx).replace(/\{\{2\}\}/g, trainingIdx);
return $(retVal);
}
$('#kpSportPlayed').on('click', '.jsAddPosition', function() {
var $container = $(this).closest('.kpSports');
var containerIdx = $container.attr('data_idx');
var itemIdx = $container.find('.item').length;
$container.find('.jsPositions').append(getItemClone(containerIdx, itemIdx));
});
$('#kpSportPlayed').on('click', '.jsAddTraining', function() {
var $container = $(this).closest('.kpSports');
var containerIdx = $container.attr('data_idx');
var trainIdx = $container.find('.advtrain > div').length;
$container.find('.advtrain').append(getTrainingClone(containerIdx, trainIdx));
});
$('#addSport').click(function () {
var idx = $('.kpSports').length;
var newItem = getClone(idx);
newItem.appendTo($('#kpSportPlayed'));
});
I have a complex structure of many nested, absolutely positioned elements.
These elements may or may not have their z-index set.
They also may or may not have the same parent element.
I am wondering what is the best / simplest way to return which element is on 'top'. Something like the following...
$(".panel").topMost()
Thanks (in advance) for your help
Do you mean the element with highest z-index:
$(document).ready(function() {
var array = [];
$("*").each(function() {
array.push($(this).css("z-index"));
});
var highest = Math.max.apply(Math, array);
console.log(highest);
});
A plugin is there ..topZindex
$.topZIndex("div");
Try this:
var z = [];
$('.panel').each(function(i, el) {
var $panel = $(el),
zindex = $panel.css('z-index');
z[zindex] = $panel;
});
var topMost = z.slice(-1);
See if you don't specify z-index to absolute elems then last of the element will be on top of other elems, means last element will have a greater highest default z-index calculated by browser itself.
Try this fiddle: http://jsfiddle.net/fLB2W/
var array = [];
$("*").each(function () {
if ($(this).css('position') == 'absolute') {
array.push($(this).css("position")+'<--pos & class -->'+$(this).attr('class'));
}
});
console.log(array[array.length-1]);
I faced similar issue with Modal dialog while displaying jQuery UI datepicker and using event parameters to figure out the clicked icon. Modal dialog overlay was preventing the new datepicker from showing on top of the modal dialog.
The best solution worked in three browsers (IE, Firefox, and Chrome) is:
function topZIndex(target, selector) {
var objs = $(target).parents(selector + '[z-index > 0]');
var a1 = -1;
$.each(objs, function (index, z1) {a1=(z1.style.zIndex > a1)? z1.style.zIndex : a1; });
return a1;
};
using event parameters as follows:
var zIndex = topZIndex(event.currentTarget, 'div')+1;
or
var zIndex = topZIndex(event.currentTarget, '*')+1;
Both combinations will generate same result, however it is more efficient to be specific by specifying 'div' instead of '*'
Then assuming my date picker id is datepickerpanel to set the new zIndex for datepicker
$('#datepickerpanel').css('z-index', zIndex);
This solution provides proper z-index value to place the new object on top of modal dialog.
I recently encountered an issue dealing with the Telerik Treeview component and handling recursive selection with checkboxes when handling larger data sets (500-1000+ nodes).
With smaller data sets (100-300 nodes) the Treeview and its selection methods work as follows (as they should):
Initially - all nodes are selected.
Clicking a parent node toggles selection of all of the child nodes.
Unselecting a single child node will unselect the parent (and any grandparent / top-level nodes)
Most of these things I believe are fairly commonplace when dealing with Treeviews and selection. The current method that is being used isn't the cleanest and calls an unnecessary amount of additional events to be fired during the selection process.
I was just curious if anyone had handled an issue similar to this before I began tearing apart the current code (available below).
Existing Selection Code:
$('#TreeView').find("li").find('> div > .t-checkbox :checkbox').bind('click', function (e) {
var isChecked = $(e.target).is(':checked');
var treeView = $($(e.target).closest('.t-treeview')).data('tTreeView');
var item = $(e.target).closest('.t-item');
var checkboxes = item.find('.t-checkbox :checkbox');
$.each(checkboxes, function (index, checkbox) { $(checkbox).attr('checked', isChecked ? true : false); treeView.checkboxClick(e, checkbox); });
var siblings = item.parent().find('> li .t-checkbox');
var siblingsLength = siblings.length;
var checkedLength = siblings.find(':checked').length;
if (siblingsLength == checkedLength) {
var parentCheckBox = item.parent().closest('.t-item').find('> div .t-checkbox :checkbox');
var grandparentCheckBox = item.parent().parent().parent().closest('.t-item').find('> div .t-checkbox :checkbox');
parentCheckBox.attr('checked', true)
grandparentCheckBox.attr('checked', true)
treeView.checkboxClick(e, parentCheckBox)
treeView.checkboxClick(e, grandparentCheckBox)
}
else {
var parentCheckBox = item.parent().closest('.t-item').find('> div .t-checkbox :checkbox');
var grandparentCheckBox = item.parent().parent().parent().closest('.t-item').find('> div .t-checkbox :checkbox');
parentCheckBox.attr('checked', false)
grandparentCheckBox.attr('checked', false)
treeView.checkboxClick(e, parentCheckBox)
treeView.checkboxClick(e, grandparentCheckBox)
}
});
I found a solution that I feel will function at least effectively as of right now and works a great deal faster than the existing solution, even when dealing with very large data sets.
I created a simple function that fires upon the Checked event within the TreeView and handles all the necessary child selection:
function TreeView_Checked(e) {
var current = $(e.item).find(':checkbox:eq(0)');
//Check or uncheck all child nodes from this one
var children = $(e.item).find(':checkbox');
current.is(':checked') ? children.attr('checked', 'checked') : children.removeAttr('checked');
}
which is implemented by adding the following within the TreeView declaration:
TreeView().Name("TreeView").ClientEvents(e => e.OnChecked("TreeView_Checked"))
This code will select or deselect the parent along with all of its children. If you want the grand-parent, you can probably do .closest(".t-item")
$(document).ready(function () {
$("#btnSelectChildren").click(function () {
nodeCheck(true);
});
$("#btnDeselectChildren").click(function () {
nodeCheck(false);
});
});
function nodeCheck(isChecked) {
var treeView = $('#TreeView').data('tTreeView');
var selected = $('#TreeView .t-state-selected');
treeView.nodeCheck(selected, isChecked);
selected.closest('li').find(".t-item").each(function () {
treeView.nodeCheck($(this), isChecked);
});
}