I am building some custom dropdown controls and the z-index isn't working properly.
// Add the empty class to the container div if no check boxes are checked.
$('.qx-container').each(function ()
{
var container = $(this);
if (!container.find('input[type="checkbox"]').is(':checked'))
{
container.find('.qx-content').text($('.qx-content').attr('empty-message'));
container.find('.qx-content').addClass('qx-empty-content');
}
else
{
handleCheckBoxToggle(container.find('input[type="checkbox"]'));
}
});
// Wire a mouse enter event to the container div. Turns the drop-down list's colors to gray if the slider isn't visible.
$('.qx-container').mouseenter(function ()
{
var container = $(this);
if (!container.find('.qx-slider').is(':visible'))
{
container.find('.qx-container-border-outer').addClass('qx-container-border-outer-hover');
container.find('.qx-container-border-inner').addClass('qx-container-border-inner-hover');
container.find('.qx-container-border-background').addClass('qx-container-border-background-hover');
}
container.data('hoverState', true);
});
// Wire a mouse leave event to the container div. Turns the drop-down list's colors to white if the slider isn't visible and
// sets the container div's empty class if no check boxes are checked.
$('.qx-container').mouseleave(function ()
{
var container = $(this);
if (!container.find('.qx-slider').is(':visible'))
{
container.find('.qx-container-border-outer').removeClass('qx-container-border-outer-hover');
container.find('.qx-container-border-inner').removeClass('qx-container-border-inner-hover');
container.find('.qx-container-border-background').removeClass('qx-container-border-background-hover');
}
if (container.text() == '')
{
container.text($(this).attr('empty-message'));
container.addClass('qx-empty-content');
}
container.data('hoverState', false);
});
// Wire a click event to the content div. Shows or hides the slider and changes the drop-down list's colors based on the slider's visibility.
$('.qx-container-border-outer').click(function ()
{
var outer = $(this);
var inner = $(this).find('.qx-container-border-inner');
var background = $(this).find('.qx-container-border-background');
var container = outer.closest('.qx-container');
var slider = container.find('.qx-slider');
var sliders = $('.qx-container').find('.qx-slider').not(slider);
// Close any other open sliders.
sliders.each(function ()
{
$(this).hide();
var containerDiv = $(this).closest('.qx-container');
var outerBorder = containerDiv.find('.qx-container-border-outer');
var innerBorder = containerDiv.find('.qx-container-border-inner');
var backgroundDiv = containerDiv.find('.qx-container-border-background');
outerBorder.removeClass('qx-container-border-outer-selected');
outerBorder.removeClass('qx-container-border-outer-hover');
innerBorder.removeClass('qx-container-border-inner-selected');
inner.removeClass('qx-container-border-inner-hover');
backgroundDiv.removeClass('qx-container-border-background-selected');
background.removeClass('qx-container-border-background-hover');
});
// Toggle the slider.
slider.slideToggle(50, function ()
{
if (!container.data('hoverState'))
{
outer.removeClass('qx-container-border-outer-hover');
inner.removeClass('qx-container-border-inner-hover');
background.removeClass('qx-container-border-background-hover');
}
if (slider.is(':visible'))
{
outer.addClass('qx-container-border-outer-selected');
inner.addClass('qx-container-border-inner-selected');
background.addClass('qx-container-border-background-selected');
}
else
{
outer.removeClass('qx-container-border-outer-selected');
inner.removeClass('qx-container-border-inner-selected');
background.removeClass('qx-container-border-background-selected');
}
});
});
// Wire a change event to the check boxes. Stores the user's selection in the content element & displays the text of which check box is checked.
$('.qx-slider').find($('input[type="checkbox"]')).click(function (event)
{
event.stopPropagation();
handleCheckBoxToggle($(this));
});
// Wire a mouse enter event to the slider row so the background color changes to gray.
$('.qx-slider-row').mouseenter(function ()
{
$(this).find('td').addClass('qx-slider-cell-hover');
});
// Wire a mouse leave event to the slider row so the background color changes to white.
$('.qx-slider-row').mouseleave(function ()
{
$(this).find('td').removeClass('qx-slider-cell-hover');
});
// Wire a mouse click event to the slider row to toggle the check box's checked attribute.
$('.qx-slider-row').click(function ()
{
var checkBox = $(this).find('input[type="checkbox"]');
checkBox.attr('checked', !checkBox.is(':checked'));
handleCheckBoxToggle(checkBox);
});
// Handles the checked event for each check box.
function handleCheckBoxToggle(checkBox)
{
// Reference to the containing content div.
var content = checkBox.closest('.qx-container').find('.qx-content')
// Holds the checked values (data is associated with the content div).
var checkBoxData = content.data('checkBoxData');
// Reference to all the check boxes in the slider.
var checkBoxes = checkBox.closest('table').find('input[type="checkbox"]');
// Create an array of check box values (associated with the content div) if it doesn't exist.
if (checkBoxData == undefined)
{
checkBoxData = new Array();
checkBoxes.each(function ()
{
checkBoxData[$(this).attr('interest-level-description')] = 0;
});
}
// Store the checked values of each check box.
checkBoxes.each(function ()
{
checkBoxData[$(this).attr('interest-level-description')] = $(this).is(':checked') ? 1 : 0;
});
// Create a commo-delimited string from the checked values.
content.data('checkBoxData', checkBoxData);
var output = '';
for (var property in checkBoxData)
{
if (checkBoxData[property] == 1)
{
output += property + ", ";
}
}
// Remove the trailing comma.
if (output.match(",") != null)
{
output = output.substr(0, output.length - 2);
}
// Set the content text and class based on the checked values.
if (output == '')
{
content.text(content.attr('empty-message'));
content.addClass('qx-empty-content');
}
else
{
content.text(output);
content.removeClass('qx-empty-content');
}
}
http://jsfiddle.net/heray/1/
If you click the items you'll notice the dropdown menu appears behind subsequent dropdowns. I've added z-indexes and position relative to every element I can think of.
Just so you understand how to use z-index, never assign a z-index to something unless you want it to be displayed over top of another element. The best practice is not to define a z-index (especially not assigning a value of 0) until you need to. In your example, the class you have after clicking the button (the actual dropdown) should have a z-index of 1 or greater, and nothing else in your document should have any z-index definition. if you have an element with z-index of 1, and then you put another element in the same physical spot with a z-index of 2 -- the container with the higher z-index will overlap the one's with the lower.
Remove the z-indexes from the dropdowns. Also, what makes you think that setting a z-index of 0 on them will make things better?
Updated fiddle.
Related
I am writing Javascript code that shows pictures on a category based on the ones the user clicked on but will show all categories when clicked outside the filtered pictures. Yet, I need the same code to work on separate divs independently, not the whole dom.
Attaching the event listener to the document works except, as you may have guessed, it doesn't work on two divs independently. When I attach it to a reference dom, let's day the dom id, it works but it doesn't know when the user clicked outside
document.addEventListener("click", (evt) => {
//get an array of all the div with "column" class
var imgElements = imgGrid.getElementsByClassName("column");
var w, x;
var y, z;
let targetElement = evt.target; // clicked element
do {
//itirate through all the divs we got the reference for
for (var i = 0; i < imgElements.length; i++) {
//check if we click on any of those divs
if (targetElement.className == imgElements[i].className) {
//we clicked on a div
//let's get the class name we want to filter by
w = imgElements[i].className;
x = w.split(' ');
console.log("You clicked on a: " + x[1]);
//we're not done //let's go itirate those divs once more
//but this time for everyone that don't have a class that
//matched our filter class we hide it, else we show it
for (var i = 0; i < imgElements.length; i++) {
y = imgElements[i].className;
z = y.split(' ');
if (z[1] != x[1]) {
addClass(imgElements[i], "hidden");
} else {
removeClass(imgElements[i], "hidden");
}
}
return;
}
}
// Go up the DOM.
targetElement = targetElement.parentNode;
} while (targetElement);
console.log("You clicked outside");
//other useful things being done here
});
Instead of "document.addEventListener" I will say DomId.addEventlistner and expect it to know to when I clicked inside and outside of each dom reference.
You can give DIVs that should be 'clickable' an unique class name. That way your logic doesn't have to know each 'inside' DIVs id.
function handleClick(e) {
if (e.target.className != "clickableDiv") {
console.log("clicked outside");
} else {
var element = e.target.id;
console.log(element + " clicked");
}
}
document.addEventListener("click", handleClick);
<div id="container1" class="clickableDiv">
DIV1
</div>
<div id="container2" class="clickableDiv">
DIV2
</div>
<div id="container3">
I'm not clickable
</div>
so I'm using this code, to slideToggle a box on my webpage.
// OPEN CERTAIN BOX
$(function() {
var sliding = false;
var mq = window.matchMedia( "(min-width: 700px)" );
if (mq.matches) {
var time = 500;
} else {
var time = 0;
}
var id = ('1');
var div = ('#toggle-content-' + id);
var img = ('#toggle-img-' + id);
var toggler = ('toggler-' + id);
$(div).hide()
$(toggler).click(function() {
if (sliding == false) {
sliding = true;
// Open / Close
$( div ).slideToggle(time,"swing");
// ...
As you can see, I'm using the var id, to use the toggle function for a certain box, which has its own css and html code.
I have 7 more boxes. Until now, i copied the code 7 times and changed the id at each copy from 2 - 8. Is there a way to make it with one code?
I tried a for loop, that goes from 1 - 8 but this obviously didnt work.
Has someone an idea? Or do I have to make that 8 copies and changed the id.
Edit:
My approach with the for-loop:
$(function() {
var sliding = false;
var mq = window.matchMedia( "(min-width: 700px)" );
if (mq.matches) {
var time = 500;
} else {
var time = 0;
}
for(i = 1; i <= 8; i++){
var id = (i.toString());
var div = ('#toggle-content-'+id);
var img = ('#toggle-img-'+id);
var toggler = ('toggler-'+id);
$( div ).hide()
$( toggler ).click(function(){
if (sliding == false){
sliding = true;
// Open / Close
$( div ).slideToggle(time,"swing");
...
And this is my html code for one box:
<tr><td cellspacing="0" cellpadding="0" height="50px" class="upper">
<toggler-1><area-head-text><img id="toggle-img-1" src="images/box_opener.png"/>Starterpaket</area-head-text></toggler-1>
</td></tr>
<tr><td>
<div id="toggle-content-1">
<area-head-text>
<img class="text-image" src="images/arrow.png"/>3 individuelle Entwürfe<br>
<img class="text-image" src="images/arrow.png"/>3 Korrekturzeichnungen<br>
<img class="text-image" src="images/arrow.png"/>Internationale Nutzungsrechte<br>
<img class="text-image" src="images/arrow.png"/>400€<br><br>
</area-head-text>
</div>
</td></tr>
I'm not sure why you put "Obviously" a loop doesn't work, because that's pretty much exactly what you should do. Something like this:
for(var i = 1; i <= 8; i++)
{
var div = $('#toggle-content-' + i);
var img = $('#toggle-img-' + i);
var toggler = $('toggler-' + i);
$(div).hide()
$(toggler).click(function() {
if (sliding == false) {
sliding = true;
// Open / Close
$( div ).slideToggle(time,"swing");
// ...
}
This is 2 options.
(and my preference) -
Instead of using an ID to add the click event onto each individual toggle button, use the same class on each, and add the click event on that class. When the user clicks a toggle button traverse the DOM from the clicked toggle button to perform your toggle on the relevant <div>.
This would look something like:
$(function() {
$('.toggleBtn').click(function() {
var sliding = $(this).data('sliding'); //use data attr to store sliding status
if (sliding == false) {
$(this).data('sliding') = true;
}else {
return; //don't toggle we're sliding
}
// navigate to element and toggle
$(this).parent('.someParentElement').children('.theDiv').slideToggle(time,"swing");
//clear sliding status
$(this).data('sliding', false);
}
}
The reason this is my preference, is because although it's faster to target an ID for a click event than a class for a single event, using 7 click events on 7 different IDS in my opinion (I don't know for sure) is less efficient than using a single click event on 1 class. That's my perceived purpose of using events on classes rather than IDS.
Also this way, when you want to add another box in, or remove a box, you don't need to modify any Javascript, the only thing you would need to maintain this code for is if you decide to change the structure of the HTML, and therefore the navigation of the DOM to perform your toggle.
using your method:
var ids = ["id1","id2","id3"];
for(var id in ids) {
var $div = $('#toggle-content-' + id);
var $img = $('#toggle-img-' + id);
var $toggler = $('toggler-' + id);
$div.hide()
$toggler.click(function() {
if (sliding == false) {
sliding = true;
// Open / Close
$div.slideToggle(time,"swing");
// ...
}
I'll try to keep it at simple as I can.
I have a JSON object that is pulled via AJAX. I am displaying a list of icons in a main div from the data dynamically which can be toggled on or off.
I have a secondary div where the selected items are appearing, while the main div icon receives a class of active.
I want the end user to be able to remove any of them by clicking on them on either the main div or secondary div.
Most of this is working, but I'm having trouble figuring out the best way to map them together so that I have 2 separate click events which can control the same outcome.
I think it may have something to do with the fact that I'm dynamically creating elements... which create more elements... which have to alter the initial elements.
My structure so far is to map the current selection inside of an array. This gives me control over keeping a code-based list of everything that is selected (there is much more data than in the example I'll be providing).
So, here is how I have it so far:
HTML:
<div id="options"></div>
<div id="selectedOptions"></div>
Javascript/jQuery:
// Simple simulated AJAX data
var ourData = {
"color1": "yellow",
"color2": "blue"
};
var $options = $('#options');
var $selectedOptions = $('#selectedOptions');
// Array to keep track of which objects are selected
var selectedOptions = [];
// Makes the initial option dynamic list
makeOptions(ourData, $options);
// If an option from the main div is clicked, handle class changing
$('button').on('click', function(){
pickOption($(this));
});
/* This function is the problem. The option gets removed from the array, and from the secondary div, but the class of active still occurs on the main div. */
$selectedOptions.on('click', '.optionActive', function(){
var option = $(this).data('color');
var optionPosition = jQuery.inArray(option, selectedOptions);
selectedOptions.splice(optionPosition, 1);
displayOptions(selectedOptions, $selectedOptions);
});
// Creates initial icons (buttons in this case) to the DOM and applies a data-attribute for the color
function makeOptions(options, $container){
var $results = $('<div id="results">');
$.each(options, function(key, value){
var $optionButton = $('<button>' + key + ':' + value + '</button>');
$optionButton.data('color', value);
$results.append($optionButton);
});
$container.append($results);
}
/* Handler for selecting an option from the Main Div. Handling the class active.
I am not using a simple classToggle because there are many situations where a click is not allowed */
function pickOption($option){
var selectedOption = $option.data('color');
// If the option returns true, or that it doesn't exist yet
if(modifyOptions(selectedOption, selectedOptions)){
$option.addClass('active');
} else {
$option.removeClass('active');
}
// Recreate our current selected options
displayOptions(selectedOptions, $selectedOptions);
}
/* Searches array to see if the option exists. Returns true or false and either pushes or splices the option from the array */
function modifyOptions(option){
var optionPosition = jQuery.inArray(option, selectedOptions);
if(optionPosition == -1){
selectedOptions.push(option);
return true;
} else {
selectedOptions.splice(optionPosition, 1);
return false;
}
}
/* Displays all currently selected options that exist in our array */
function displayOptions(selectedOptions, $container){
$container.empty();
$.each(selectedOptions, function(option, value){
var $optionTile = $('<div class="optionActive">');
$optionTile.data('color', value)
.text(option + ":" + value)
.css('background', value);
$container.append($optionTile);
});
}
So, to summarize, I want some some way to remove the .active class from the main div equivalent element when the item from the second div is clicked.
I tried removing the class active by searching for any elements with the data-color=data-color of the selected item, but I couldn't get that to work.
ex:
$('*[data-color="' + $(this).data('color') + '"]').removeClass('active');
I would really like some data approach to this, such as removing the class active if it had data-color="yellow" for instance.
Playground:
https://jsfiddle.net/c75xcLha/
EDIT:
Both Are Selected, working as designed:
Clicked Yellow Div. Yellow Button is still active:
Should remove the active class from the button when the yellow div OR the button is pressed, as shown here:
You are assigning data-* property using .data(PROP), not attribute hence element having data-* property could not be accessed/selected using attribute-selector, assign attribute using .attr('data-color') instead of .data(property)
Attribute Equals Selector [name=”value”], Selects elements that have the specified attribute with a value exactly equal to a certain value.
.data( key, value ), Store arbitrary data associated with the matched elements.
When you use .data() to update a data value, it is updating internal object managed by jQuery, so it will not be updated in the data-* attribute[Ref]
// Simple simulated AJAX data
var ourData = {
"color1": "yellow",
"color2": "blue"
};
var $options = $('#options');
var $selectedOptions = $('#selectedOptions');
// Array to keep track of which objects are selected
var selectedOptions = [];
// Makes the initial option dynamic list
makeOptions(ourData, $options);
// If an option from the main div is clicked, handle class changing
$('button').on('click', function() {
pickOption($(this));
});
/* This function is the problem. The option gets removed from the array, and from the secondary div, but the class of active still occurs on the main div. */
$selectedOptions.on('click', '.optionActive', function() {
var option = $(this).data('color');
var optionPosition = jQuery.inArray(option, selectedOptions);
selectedOptions.splice(optionPosition, 1);
$('[data-color="' + $(this).data('color') + '"]').removeClass('active');
displayOptions(selectedOptions, $selectedOptions);
});
// Creates initial icons (buttons in this case) to the DOM and applies a data-attribute for the color
function makeOptions(options, $container) {
var $results = $('<div id="results">');
$.each(options, function(key, value) {
var $optionButton = $('<button>' + key + ':' + value + '</button>');
$optionButton.attr('data-color', value);
//___________^^^^^^^^^^^^^^^^^^Little trick here!
$results.append($optionButton);
});
$container.append($results);
}
/* Handler for selecting an option from the Main Div. Handling the class active.
I am not using a simple classToggle because there are many situations where a click is not allowed */
function pickOption($option) {
var selectedOption = $option.data('color');
// If the option returns true, or that it doesn't exist yet
if (modifyOptions(selectedOption, selectedOptions)) {
$option.addClass('active');
} else {
$option.removeClass('active');
}
// Recreate our current selected options
displayOptions(selectedOptions, $selectedOptions);
}
/* Searches array to see if the option exists. Returns true or false and either pushes or splices the option from the array */
function modifyOptions(option) {
var optionPosition = jQuery.inArray(option, selectedOptions);
if (optionPosition == -1) {
selectedOptions.push(option);
return true;
} else {
selectedOptions.splice(optionPosition, 1);
return false;
}
}
/* Displays all currently selected options that exist in our array */
function displayOptions(selectedOptions, $container) {
$container.empty();
$.each(selectedOptions, function(option, value) {
var $optionTile = $('<div class="optionActive">');
$optionTile.data('color', value)
.text(option + ":" + value)
.css('background', value);
$container.append($optionTile);
});
}
.active {
background: #CCF;
}
.optionActive {
min-width: 100px;
min-height: 100px;
display: inline-block;
margin: 1em;
background: #eee;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div id="options"></div>
<div id="selectedOptions"></div>
Updated Fiddle
Edit: If you still want to stick with .data method, use .filter to select target element.
$('button').filter(function(){
return $(this).data('color')=='yellow';
}).removeClass('active');
I have a range of divs (projects) which have a display:none-ed overlay container inside of them, containing additional info.
If the mouse enters the outer div, that overlay container should receive another class making it visible. On mouse leaving the class should be removed.
I solved it using onmouseover="setactive('DIV ID')", but it made the code look pretty messed up so I tried to switch to Eventlisteners. It won't work though and I can't figure out why.
This is my script so far:
// Init Eventlisteners for each container
window.addEventListener("load", start, false);
function start() {
var project_containers = document.getElementsByClassName('content-project')
for (var i = 0; i < project_containers.length; i++) {
project_containers[i].addEventListener("mouseover", setactive(), false)
project_containers[i].addEventListener("mouseout", setinactive(), false)
}
}
// If mouse is over container, add overlay_active class
function setactive() {
var container = document.getElementById(event.currentTarget);
var overlay_class = container.getElementsByClassName("element-overlay")[0];
if (!(overlay_class.className.match(/(?:^|\s)overlay_active(?!\S)/))) {
overlay_class.className += " overlay_active";
}
}
// If mouse is outside the container again, remove overlay_active class
function setinactive() {
var container = document.getElementById(event.currentTarget);
var overlay_class= container.getElementsByClassName("element-overlay")[0];
if (overlay_class.className.match(/(?:^|\s)overlay_active(?!\S)/)) {
overlay_class.className = overlay_class.className.replace(/(?:^|\s)overlay_active(?!\S)/g, '')
}
}
You don't need the id to set container, your functions could be like this:
function setinactive(e) {
var container = e.currentTarget;
//your code
}
}
And then the call:
project_containers[i].addEventListener("mouseout", setinactive, false);
I am trying to create a Jquery Tree plugin for my current project. In the plugin, there are 3 compomnents: a text box containing the result selected from the tree , a div containing the tree and a button to show the div. It works fine, except that i cannot make it auto lose the popup div if the tree lose its focus.
Here is the code to create the div
createTree = function() {
$.getJSON(_options.jsonSrc, function(data) {
nDiv = document.createElement("div");
nDiv.id = "divRootAd";
$(nDiv).css('display', 'none');
jsonObj = data["treeJson"];
nUl = document.createElement("ul");
nUl.appendChild(createNode(jsonObj));
nDiv.appendChild(nUl);
$("body").append(nDiv);
//$(nDiv).focus();
repositionDiv();
});
};
repositionDiv = function() {
if ($('#divRootAd').is(':hidden')) {
// get the field position
var sf_pos = $("#txtAdVal").offset();
var sf_top = sf_pos.top;
var sf_left = sf_pos.left;
// get the field size
var sf_height = $("#txtAdVal").height();
// apply the css styles - optimized for Firefox
$("#divRootAd").css("position","absolute");
$("#divRootAd").css("left", sf_left);
$("#divRootAd").css("top", sf_top + sf_height + 5);
$('#divRootAd').show();
$('#divRootAd').blur(function(event){
alert("lose focus");
clearDiv();
});
} else {
clearDiv();
}
};
The line alert("lose focus") does not work when i move the mouse outside the div. Can anyone suggest a solution for this ?
Instead of blur you could use mouseout
$('#divRootAd').mouseout(function(event){
alert("lose focus");
clearDiv();
});
Hope it helps