Remove everything except child - javascript

<div class="parent">
<span>sometext</span>
plain text
<input class="child">
</div>
<div class="parent">
<span>sometext</span>
plain text
<input class="child">
</div>
<div class="parent">
<span>sometext</span>
plain text
<input class="child">
</div>
How do I safely remove everything in .parent except .child?
I'm using this code (where items is a stack of .child and each is a .child)
items.each(function(){
$(this).parent().children().each(function(){
if ($(this).hasClass('child'))
// do something
else
$(this).remove();
});
$(this).unwrap(); // remove parent, keep only .child
});
But it doesn't handle plain text.

You've said
There can be more than one .child inside .parent, we keep only a first one. So If there are three, second and third should be removed.
and that
items is a stack of .child and each is a .child
Okay, then this is what I would do:
items.parent('.parent').each(function() {
var parent = $(this),
child = parent.children('.child').first();
child.detach();
parent.empty().append(child);
});
What that does:
Moves from the set of .child elements up to the set of .parent elements. The resulting set will only have unique parents.
Loops through the parents.
Gets the first .child in each parent and detaches it.
Empties the .parent.
Re-attaches the .child.
End result is that each .parent will have only one .child (and no other children, whether .child or not).

Here you'd have a solution with pure JS: fiddle
So within your loop you can use the following:
this.parentNode.innerHTML = this.outerHTML;
If you'd have to keep attached event handlers:
var _this = this.cloneNode(true),
parent = this.parentNode;
parent.innerHTML = '';
parent.appendChild(_this);

Something like this will do it;
$('.parent').on('click', '.child', function(e) {
var $this = $(this),
$parent = $this.parent(),
$children = $parent.find('.child'); // or: $this.siblings('.child');
$parent
.empty() // Empty the "parent" element
.append($children); // Re-append all "child" element to "parent"
$this.focus(); // Focus the (input) element
});

Here's an alternative using a regex:
$('.parent').each(function() {
$(this).html($(this).html().match("<input class=.child.>"));
});

Related

how to set the index of an element

$('.title').on('click', function(){
console.log($(this).index('.title'));
});
.title{
cursor:pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='title'>lorema</div>
<div class='title'>loremb</div>
<div class='title'>loremc</div>
<div class='title'>loremd</div>
Now, how to SET the index of a clicked element, i.e. change its position?
Is it possible something like:
$('.clicked').setIndex('.title', 3);
One option would be to .remove() the clicked element, then find the third .title element currently in the DOM, and use insertAfter to insert the clicked element after it:
$(document).on('click', '.title', function(){
const $this = $(this);
$this.remove();
$this.insertAfter($('.title').eq(2));
});
.title{
cursor:pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='title'>lorema</div>
<div class='title'>loremb</div>
<div class='title'>loremc</div>
<div class='title'>loremd</div>
Note the event delegation there - that's needed because otherwise, the listener will only work once for each .title.
To illustrate why .remove is necessary, check the following snippet - although it's using insertAfter($('.title').eq(2));, the .eq(2) refers to the 3rd element before the clicked one is removed, resulting in inconsistent behavior; if you click the first, second, or third element, it'll get put in the third position, instead of the fourth, as desired.
$(document).on('click', '.title', function(){
const $this = $(this);
$this.insertAfter($('.title').eq(2));
});
.title{
cursor:pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='title'>lorema</div>
<div class='title'>loremb</div>
<div class='title'>loremc</div>
<div class='title'>loremd</div>
<div class='title'>loreme</div>
<div class='title'>loremf</div>

Hide all elements except one, And show all elements again

<body>
<div id="e1">Element X1</div>
<div id="e2">Element 2X</div>
<div id="e3">Element X3</div>
Hide
</body>
How can i hide the entire body And only show #e2 when i click on #hide, But if i clicked anywhere else out of #e2 again, the hide effect will stop and return to normal.
Something like this? NB: make sure to give your hide link a unique ID.
Showing/hiding works well with jQuery show and hide methods, but since you wanted the elements to stay in their place, it is more suitable to use the visibility style attribute:
$('#hide').click(function () {
// hide all in body except #e2, and #e2's parents.
$('body *').not($('#e2').parents().addBack()).css({visibility: 'hidden'});
return false; // cancel bubbling and default hyperlink effect.
});
$('#e2').click(function () { // click on #e2
return false; // cancel bubbling -- ignore click.
})
$(document).click(function (e) { // click on document
$('body *').css({visibility: 'visible'}); // show all in body.
});
div { border: 1px solid}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="e1">Element X1</div>
<div id="e2">Element 2X</div>
<div id="e3">Element X3</div>
Hide
Be aware that these div elements stretch across horizontally, so a click to the right of the text "Element 2X" will still be on #e2.
Something like this:
// Get reference to the hyperlink
var hideElem = document.getElementById("e4");
// Set up click event handler for link
hideElem.addEventListener("click", function(e){
var elems = document.querySelectorAll("body *:not(#e2)");
Array.prototype.slice.call(elems).forEach(function(value){
value.classList.add("hide");
});
e.stopPropagation();
});
// Set up click event handler for document
document.addEventListener("click", function(){
var elems = document.querySelectorAll("body *");
Array.prototype.slice.call(elems).forEach(function(value){
value.classList.remove("hide");
});
});
.hide { display:none; }
<div id="e1">Element X1</div>
<div id="e2">Element 2X</div>
<div id="e3">Element X3</div>
Hide
$('body').click(function(evt){
if(!$(evt.target).is('#e2')) {
//If not e2 is clicked then restore the state back of page by removing a specific class
}
});
You will need help of css class .hide {display:none;} and add and remove this class when e2 is clicked and remove this class when body is clicked but not e2 as provided above

Select a div if it contains all specified elements

I'm attempting to select the .item div which contains both tagOne and tagTwo span elements.
My div structure is as follows:
<div id="items">
<div id="block1" class="item">
<span class="tagOne tag">One</span>
<span class="tagTwo tag">Two</span>
</div>
<div id="block2" class="item">
<span class="tagOne tag">Java</span>
</div>
</div>
Using the following jQuery I'm able to locate the tags (with their parent div's) separately.
var blocks = $('#items .item');
blocks.filter('.item').find('[class*="tagOne"]').parent();
blocks.filter('.item').find('[class*="tagTwo"]').parent();
However, once I try to combine them to narrow it down to the one div that contains them both, I get no results and I can't seem to work out why!
blocks.filter('.item').find('[class*="tagOne"][class*="tagTwo"]');
My understanding is that the comma syntax will create an OR expression, and without creates an AND expression. I'm after the AND expression as I only want to return the div that contains all the criteria, or nothing at all.
Note: I'm doing it this way because I'm creating a toggle-filter based on the tags, and the criteria (i.e. tagOne, tagTwo) is a concatenation of the tags selected by the user (not shown) so it is preferable to try to do it in one operation.
EDIT: Moved duplicate id's to class names instead to make it valid and tweaked JavaScript code accordingly.
First of all, the ID should be unique. Now, the markup contains two elements with ID tagOne which is invalid markup.
You can use class instead of ID.
Select any of the element from the two(.tagOne or .tagTwo in this case)
Use siblings() to select the sibling element having the other class
Use closest() to select closest ancestor matching the selector.
The step #1, #2 and #3 above will select only those .item elements having both .tagOne and .tagTwo as descendent.
Code:
$('.tagOne') // Select one of the element
.siblings('.tagTwo') // Get second element if it is sibling
.closest('.item') // Get the closest ancestor
$('.tagOne') // Select one of the element
.siblings('.tagTwo') // Get second element if it is sibling
.closest('.item') // Get the closest ancestor
.addClass('selected'); // For Demo purpose
.item {
color: red;
}
div.selected {
color: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="items">
<div id="block1" class="item">
<span class="tagOne tag">One</span>
<span class="tagTwo tag">Two</span>
</div>
<div id="block2" class="item">
<span class="tagOne tag">Java</span>
</div>
<div id="block3" class="item">
<span class="tagTwo tag">I Love JavaScript</span>
</div>
</div>
You can also use filter as follow.
Iterate over all the .item elements using filter()
Use context selector to check if the current .item has descendent .tagOne and .tagTwo.
Use length property on the jQuery object to get the number of elements selected by the selector.
Code:
$('.item').filter(function() {
return $('.tagOne', this).length && $('.tagTwo', this).length;
})
// Fiddle: https://jsfiddle.net/tusharj/8tuu1wxs/1/
// Iterate over all elements having item class
$('.item').filter(function() {
return $('.tagOne', this).length && $('.tagTwo', this).length;
}).addClass('selected');
.item {
color: red;
}
.selected {
color: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="items">
<div id="block1" class="item">
<span class="tagOne tag">One</span>
<span class="tagTwo tag">Two</span>
</div>
<div id="block2" class="item">
<span class="tagOne tag">Java</span>
</div>
<div id="block3" class="item">
<span class="tagTwo tag">I Love JavaScript</span>
</div>
</div>
If the sequence/order of the elements is fixed, CSS general sibling selector ~ or adjacent sibling selector + can be used.
$('.tag1 ~ .tag2').closest('.item')
OR
$('.tag1 + .tag2').closest('.item')
// Fiddle: https://jsfiddle.net/tusharj/amdoLfou/1/
$('.tag1 ~ .tag2') // + can also be used instead of ~
.closest('.item') // Get closest ancestor
.css('color', 'blue'); // For Demo purpose
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="items">
<div id="block1" class="item">
<span class="tag1 tag">One</span>
<span class="tag2 tag">Two</span>
</div>
<div id="block2" class="item">
<span class="tag1 tag">Java</span>
</div>
</div>
While you've already accepted an answer, I felt that this is a question worthy of a plain JavaScript, rather than simply a jQuery, solution. So, with that in mind, I'd like to offer the following approach (which does use some ECMAScript 6 feastures, so does require a fairly modern browser):
// using an Immediately-Invoked Function Expression syntax,
// so that the enclosed function will be executed when
// encountered, rather than requiring the user to call it
// explicitly (this would need to run in a DOMReady callback
// or once the DOM has been constructed, however):
(function hasAll(opts) {
// setting the default settings for the function:
var settings = {
// a CSS Selector string to identify the ancestor
// element that you wish to identify:
'ancestorSelector': 'div',
// an array of CSS Selectors to identify the
// descendants by which the ancestor should
// be found:
'descendantSelectors': []
}
// looking at the named (not inherited) properties
// of the opts Object supplied by the user:
for (var property in opts) {
// if the opts Object has a given property
// name we set the corresponding property
// of the settings Object to be equal to that
// property-value:
if (opts.hasOwnProperty(property)) {
settings[property] = opts[property];
}
}
// finding all the elements represented by the first selector
// of the user-supplied selectors contained within an element
// matching the ancestor selector:
var firstElements = document.querySelectorAll(
settings.ancestorSelector + ' ' + settings.descendantSelectors[0]
),
// converting the NodeList returned by document.querySelectorAll()
// into an Array, using Array.from:
arrayOfFirsts = Array.from(firstElements),
// here we iterate over that Array, using Array.prototype.filter():
hasSiblings = arrayOfFirsts.filter(function(n) {
// we look for the parentNode of the current node (n):
var p = n.parentNode;
// we use Array.prototype.every() to ensure that every
// selector in the descendantSelectors Array returns
// a Node (document.querySelector() returns only the
// first node matching the given selector, or null if
// there is no element matching that selector).
// if Array.prototype.every() returns true (all elements
// of the Array match the supplied test) then the current
// node (n) is retained in the array returned by
// Array.prototype.filter():
return settings.descendantSelectors.every(function(selector) {
// Array.prototype.every() returns a Boolean,
// true : if all elements of the Array match
// the supplied test/assessment,
// false: if *any* of the elements of the Array
// fail to match.
// this is the test that we're matching against:
return p.querySelector(selector) !== null;
});
});
// here we iterate over the hasSiblings Array, and use
// Array.prototype.map() to form a new Array, using
// an Arrow function to take the current node (n)
// and find, and return, the closest element to that
// node which matches the supplied settings.ancestorSelector:
var found = hasSiblings.map(n => n.closest(settings.ancestorSelector));
// returning that array to the calling context:
return found;
})({
// this is the 'opts' Object that we're passing to the
// IIFE-contained function:
'ancestorSelector': '.item',
'descendantSelectors': ['.tagOne', '[data-demo]']
// using Array.prototype.forEach() to iterate over the
// returned elements, to add the class 'hasAll' to the
// the classList (the list of class-names) of the given
// node (n):
}).forEach(n => n.classList.add('hasAll'));
(function hasAll(opts) {
var settings = {
'ancestorSelector': 'div',
'descendantSelectors': []
}
for (var property in opts) {
if (opts.hasOwnProperty(property)) {
settings[property] = opts[property];
}
}
var firstElements = document.querySelectorAll(
settings.ancestorSelector + ' ' + settings.descendantSelectors[0]
),
arrayOfFirsts = Array.from(firstElements),
hasSiblings = arrayOfFirsts.filter(function(n) {
var p = n.parentNode;
return settings.descendantSelectors.every(function(selector) {
return p.querySelector(selector) !== null;
});
});
var found = Array.from( hasSiblings.map(n => n.closest(settings.ancestorSelector)) );
return found;
})({
'ancestorSelector': '.item',
'descendantSelectors': ['.tagOne ~ .tagTwo']
}).forEach(n => n.classList.add('hasAll'));
div {
width: 50%;
margin: 0.5em auto;
border: 2px solid #000;
border-radius: 1em;
padding: 0.5em;
box-sizing: border-box;
}
.hasAll {
border-color: #f90;
}
.hasAll span {
color: #f90;
font-weight: bold;
}
<div id="items">
<div id="block1" class="item">
<span class="tag tagOne">One</span>
<span class="tag tagTwo">Two</span>
</div>
<div id="block2" class="item">
<span class="tag tagOne">Java</span>
</div>
<div id="block3" class="item">
<span class="tag tagOne" data-demo="false">tag-one</span>
<span class="tag tagTwo">tag-two</span>
<span class="tag" data-demo="true">tag-three</span>
</div>
</div>
JS Fiddle demo.
Note that, with the above function, an ancestor element will be matched if any of its descendants', or its descendants' siblings, matches multiple selectors.
References:
Array.from().
Array.prototype.every().
Array.prototype.filter().
Array.prototype.forEach().
Array.prototype.map().
Arrow functions.
document.querySelector().
document.querySelectorAll().
Element.classList.
Element.closest().
for...in statement.
Object.hasOwnProperty().
Node.parentNode.
Try this
blocks.filter('.item').find('[id="tagOne"],[id="tagTwo"]');
Try using has jquery has selector it searches if selected nodes have certain children:
https://jsfiddle.net/96gbf7xg/

Wrap elements depending on their siblings

I am trying to wrap p elements within a single div if they come before or after an .image class. At the moment, I can only wrap around each individual element.
Here is my CSS:
<ul>
<p>this is the first paragraph</p>
<p>this is the second paragraph</p>
<div class="image"></div>
<div class="image"></div>
<p>this is the third paragraph</p>
<p>this is the fourth paragraph</p>
</ul>
and my jQuery:
$('p').wrap('<div class="new" />');
$('.image').wrap('<li />');
Here is my JSFIDDLE: http://jsfiddle.net/BDqGe/
Would anyone know how to wrap elements depending on their siblings?
You HTML is invalid first of all because if you look here
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul
Permitted content for ul - zero or more <li> elements, eventually mixed with <ol> and <ul> elements.
But you can wrap all your elements by using prevAll() to get all previous siblings and .nextAll() to get all siblings after the element - then .wrapAll() will wrap them all with a single element
$('.image') // start at .image
.prevAll('p') // <-- get all p's before .image
.wrapAll('<div class="new" />') // wrap all in a div
.end() // go back to .image
.nextAll('p') // get all p's after .image
.wrapAll('<div class="new" />') // wrap all in div
.end() // go back to .image
.wrap('<li />'); // wrap .image in li
FIDDLE
My approach will be
var $ul = $('ul');
$ul.find('.image').wrap('<li />');
$ul.children('p').filter(function(){
var $prev = $(this).prev();
return $prev.length == 0 || $(this).prev().is(':not(p)')
}).each(function(){
$(this).nextUntil(':not(p)').addBack().wrapAll('<li class="new"/>')
})
Demo: Fiddle

How to add slide animation when showing elements

I would like to add animation effect to following code when showing tree items.
I know that jquery has slide functions, and css has "transition", but not sure how to apply these to my code. Any ideas?
<head>
<script language="JavaScript">
function show(){
var elements = document.getElementsByClassName("label");
for(var i = 0, length = elements.length; i < length; i++) {
elements[i].style.display = 'block';
}
}
</script>
<style>
.label {
-webkit-padding-start: 20px;
display: none;
}
</style>
</head>
<body>
<div>
<div onclick="show()">1st Row</div>
<div>
<div class="label">First</div>
<div class="label">Second</div>
<div class="label">Third</div>
</div>
<div>2nd Row</div>
</div>
</body>
If you are planning to use jQuery then you can use slideDown and slideUp method to show/hide elements with animation. There is slideToggle method which alternatively show/hides the element with animcation. You can modify your show method as below
Working demo
function show(obj){
var $this = $(obj);//Here obj points to the element clicked
//Now you have to show/hide the next sibling of the element clicked
//We will use next() method which gives the next sibling of element
//And then call slideToggle on it to show/hide alternatively
$this.next().slideToggle();
}
Change in the markup
<div onclick="show(this)">1st Row</div>
function show() {
$('.label').slideDown();
}
This selects all elements with the .label class and slides them into view. There is also a .fadeIn() function.
Also, you can attach click handlers by selectors (like an id or class):
<div>
<div class="row">1st Row</div>
<div>
<div class="label">First</div>
<div class="label">Second</div>
<div class="label">Third</div>
</div>
<div class="row">2nd Row</div>
</div>
Notice I removed the onClick="" statement and added a class to the row div. Then you can select the element you want to attach the click event to and keep all the code in one place:
$('.row').bind('click', function () {
$(this).next().find('.label').slideToggle();
});
This JavaScript above adds a click handler to all elements with the row class and toggles the display of all of the elements with the label class in the next element.
Here is a jsfiddle: http://jsfiddle.net/L34g3/.

Categories