I want to use appendChild to bring div elements to front in a simple web application. I listen for click or mousedown (I have tried both) and then reappend whatever has been clicked to the main div container. Like this:
JavaScript
document.querySelector("#container").addEventListener("click", function(event) {
for (var i = 0; i < this.children.length; i += 1) {
if (this.children[i].contains(event.target)) {
this.appendChild(this.children[i]);
}
}
});
However, when I try this in Firefox the text fields in the div elements becomes unselectable. You can't put the marker in the text fields. It works in Chrome but not in Firefox. What could be the cause of this and how can I fix it?
Currently, because of this problem, I am using a different method with z index. But I would prefer to use appendChild as it has other advantages.
Thanks in advance.
This is a direct consequence of the appendChild which temporarily removes from DOM the child that contains the clicked Element (including when clicking to select text or to put caret / focus in a text input field), to re-append it at the end of the div children (but the text selection is now gone).
A simple workaround would be to leave the clicked child in DOM, but move its below siblings up until the clicked one is the last child.
document.querySelector("#container").addEventListener("click", function(event) {
var found,
children = this.children;
for (var i = 0; i < children.length; i += 1) {
if (children[i].contains(event.target)) {
//this.appendChild(children[i]); // Do not detach from DOM.
found = children[i]; // Leave it in place.
} else if (found) {
this.insertBefore(children[i], found); // Swap with found sibling.
}
}
});
Demo: https://jsfiddle.net/x53eh5b3/1/
Related
So, the situation is the following.
I have a div element which serves as a container for all kind of other elements (especially <a hre0="..."><img ...></img></a> and <iFrame> tags):
<div id="myDiv">
So basically, what i wanna do is to get the click target (if applicable) for all kind of elements within this div tag, under the assumption that there is always only one click with target specified.
Speaking UI wise: I want to see the target link that the user sees when he hovers with the cursor over the element.
Is there a generic approach to achieve this?
It sounds like what you want to do is find all the child elements of your wrapper div, then loop through them getting the 'href' property. Something like:
var children = document.getElementById('myDiv').children;
for (var i = 0; i < children.length; i++) {
var childElement = children[i];
var destination = childElement.href;
// do whatever you want with destination here. You can also get the associated IDs etc.
}
Untested but should work.
Answer in short: insertAfter() is to be used on the element that you are inserting after another element, not on the element that you want to insert something after. For full code, scroll down.
I have a situation where when the user clicks a button, certain elements get moved to a hidden container, and when the user clicks another button, those elements need to get moved back to their original position.
I do it (in short) like this:
Moving to hidden container:
element.data('original_parent', original_parent);
element.data('original_index', original_parent.index());
element.appendTo(hidden_container);
Moving the items back to their original container:
element.data('original_parent').children().eq(element.data('original_index')).prev().insertAfter(element);
But somehow this isn't working. When I output the children of the original parent to the console, it also lists the elements that are currently in the hidden container as children. Anyone have an idea of how I could fix this?
Your logic may not be not correct as the order in which it is removed and added might deffer – Arun P Johny 1 min ago
You are right. The elements are output to the console first, then I move them, which is why it seemd like an uncorrect parent was being listed as their parent.
Are you sure you're getting any element with doing element.data('original_parent')? – Dhaval Marthak 5 mins ago
An element is being returned for sure.
I have already found out what is happening here. I use the insertAfter function on the original element that I want to insert the element after instead of on the element that I want to insert after the original element. Got my jQuery functions mixed up a bit.
The rest of the code works, though! Full code for those that want to use the idea and come across this post:
function hideNonMatchingLevelElements(jquery_selector) {
var elements = $(jquery_selector);
if (!elements.length)
return false;
var target = $('#js-hidden-level-elements');
if (!target.length) {
console.error('Cannot hide non matching level elements because target cannot be found.');
return false;
}
elements.each(function() {
$(this).data('original_parent', $(this).parent());
$(this).data('original_index', $(this).index());
$(this).appendTo(target);
});
}
function showMatchingLevelElements(jquery_selector) {
var elements = $(jquery_selector);
if (!elements.length)
return false;
elements.each(function() {
// Only show elements that are in the "hidden elements" container.
if ($(this).parent().attr('id') != 'js-hidden-level-elements')
return true; // Continue;
if (!$(this).data('original_parent'))
return true; // Continue.
$(this).insertAfter($(this).data('original_parent').children().eq($(this).data('original_index')).prev());
});
}
/***************************************************** *
* Function: renderTodos
* Builds a list of todo items from an array
* rebuilds list each time new item is added to array
****************************************************** */
function renderTodos(arr) {
// erases all previous content
uList.innerHTML = '';
for ( var i = 0; i < tdArray.length; i++ ) {
// create a link element
var remex = document.createElement('span');
//link element didn't work so try a button
//var remex = document.createElement('input');
//remex.setAttribute('type','button');
remex.setAttribute('class', 'deletex');
remex.innerHTML="X";
// set attribute index and value
remex.setAttribute('index', i);
remex.addEventListener('click',removeTodo);
console.dir(remex);
// create <li> element
var li_element = document.createElement('li');
li_element.style.lineHeight = "20pt";
li_element.appendChild(remex);
li_element.innerHTML += tdArray[i];
// add item to list
uList.appendChild(li_element);
inField.value = '';
}
} // /renderTodos
This function builds a list based on text field inputs. Each time the the "add item" button is clicked, the event calls this function to add the item to the list. Everything works beautifully UNTIL I try to add the eventListener to the "x" that is appended to the li element prior to the list item text. The idea is that the "x" is clickable, and onClick it removes the list item entry from the list. But I have tried 6 ways to Sunday to attach an eventListener to the "x" object and nothing works. I have tried attaching the event listener to a span object, and a button object; I have moved "remex.addEventListener..." all around in the function, after it has been rendered, before it gets rendered, etc.; I have eliminated the CSS; I have tried changing the addEventListener to onClick; I have tried this code on our own Apache server, I have moved it to jsbin.com in hopes that some server setting was getting in my way; and probably a few more things I can't remember in the long list of things I have tried. As you see, I have tried it as a button and as a span, hence the commented code.
In short, no matter what I try, the eventListener will NOT attach to the "x". Any ideas? Do you need to see more of the code?
This line overrides the attached eventlistener:
li_element.innerHTML += tdArray[i];
Setting innerHTML replaces all the original elements within li_element. += is just a shortcut to li_element.innerHTML = li_element.innerHTML + tdArray[i];
If tdArray[i] contains just some text, you can add its content like this:
li_element.appendChild(document.createTextNode(tdArray[i]));
If tdArray[i] contains elements, you could append a wrapper element, and then set the innerHTML of the wrapper.
I was wanting to have a javascript (jQuery) function that removed everything that didn't have the safe class.
The problem is, if the parent element is hidden, it cannot show the 'safe' part of it.
Is there a simple way to get around this? I'd rather not go in and span all of the elements that need removed.
trimmer = function(element){
x = $(element+' *:not(.safe)');
x.hide();
}
trimmer('section');
Fiddle
var element = 'section';
//finds all non `.safe` elements in `section`s and hides them
$(':not(.safe)', element).hide();
//finds all `.safe` elements in `section`s and shows the `section`s
$('.safe', element).parents(element).show();
Horen was right, it is indeed impossible to show parts of a hidden element.
To make only parts of the text disappear, the non-safe content must be labeled for removal.
$(element).contents().each(function() {
if (this.nodeType == 3)
$(this).wrap('<span class="disappear" />');
});
You can read more about this answer here:
How to add spans to all areas of a node that isn't restricted
I want to toggle a div element (expand/collapse) when clicked.
I have many div elements, on click to new element, I want to collapse the previous one and expand the current clicked one.
I tried using static type variable to save the instance of previous div tag and compared with the current selection, but I don't know why is it not working.
Searching about this, I got similar code idea to collapse all div and then expand the current selected only, but I want to just toggle the previous one with new one, not collapse all div and expand the selected (though I would be using it if other way is not possible)
Can it be done using static variables of js?
At its simplest, you can simply do something like this:
var divs = document.getElementsByTagName('div'),
collapseClass = 'collapsed',
current = divs[0];
// Hide all elements except first, add click event hander
for(var i = 0, len = divs.length; i < len; i++){
divs[i].onclick = function(){
if(this !== current){
toggle(this, current);
current = this;
}
};
if(i > 0) toggle(divs[i]);
}
This will store the current element in a variable, then toggle it when another element is clicked. It also uses an if statement to check if the currently clicked element is the one currently visible element, and only toggles if its not.
See a working demo of this here: http://jsfiddle.net/GaxvM/
You can assign a unique ID to each of the elements and use document.getElementById to identify both elements, and then collapse one/expand the other.
If you number them sequentially (like div1, div2, div3, etc) you could do something like:
function colexp(div_id){
div_2_collapse = document.getElementById(div_id);
next_div = div_id.substr(0,3) + parseInt(div_id.substr(3))+1;
div_2_expand = document.getElementById(next_div);
hide(div_2_collapse);
show(div_2_expand);
}