Wrap elements depending on their siblings - javascript

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

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>

How to simultaneously hide and show content and vice versa?

I have a problem and I need your help. I have several links (in <aside>) leading to several different menus (in <section>). On click over the link, only the relevant div in <section> is shown, the rest are hidden. This part is ok and working. What is not working is when I click over an image:
the current div (.menu) in <section> should be hidden;
the same picture (with bigger size) should be shown;
when you click once again over the big image, the big image should disappear and the current div in .menu (the one that was hidden on the first step) should appear one more time. Sort of toggling between content.
So if I click on a picture on the "second div" content, the same picture with bigger size should be show (the "second div" content should be hidden) and when I click once again over the big picture it should disappear and the "second div" content to be returned.
I tried with toggle() but had no success. Either I did not use it correctly, or it is not suitable for my case. This is where I managed to reach to.
I will really appreaciate your support - how to show only the hidden div, not all hidden div's. Right now, when you click on the big image it did not show the hidden div.
$(window).on("load", function() {
$("div.menu:first-child").show();
});
$(".nav a").on("click", function() {
$("div.menu").fadeOut(30);
var targetDiv = $(this).attr("data-rel");
setTimeout(function() {
$("#" + targetDiv).fadeIn(30);
}, 30);
});
var pictures = $(".img-1, .img-2").on("click", function() {
$("div.menu:active").addClass("hidden");
//how to reach out only the current, active div (not all div's in .menu)?
$(".menu").hide();
var par = $("section")
.prepend("<div></div>")
.append("<img id='pic' src='" + this.src + "'>");
var removePictures = $("#pic").on("click", function() {
$(this).hide();
$(".hidden").show();
});
});
.menu {
width: 100%;
display: none;
}
.menu:first-child {
display: block;
}
.row {
display: inline-block;
width: 100%;
}
.img-1,
.img-2 {
width: 120px;
height: auto;
}
<!doctype html>
<html>
<head>
</head>
<body>
<aside>
<ul class="nav">
<li>To first div
</li>
<li>To second div
</li>
<li>To third div
</li>
</ul>
</aside>
<section>
<div class="menu" id="content1">
<h3>First Div</h3>
<div class="present">
<div class="row">
<div>
<p>Blah-blah-blah. This is the first div.</p>
<img class="img-1" src="http://www.newyorker.com/wp-content/uploads/2014/08/Stokes-Hello-Kitty2-1200.jpg">
</div>
</div>
<div class="row">
<div>
<img class="img-2" src="https://jspwiki-wiki.apache.org/attach/Slimbox/doggy.bmp">
<p>Blah-blah-blah. This is the first div.</p>
</div>
</div>
</div>
</div>
<div class="menu" id="content2">
<h3>Second Div</h3>
<div class="present">
<div class="row">
<div>
<p>
Blah-blah-blah. This is the second div.
</p>
<img class="img-1" src="http://www.newyorker.com/wp-content/uploads/2014/08/Stokes-Hello-Kitty2-1200.jpg">
</div>
</div>
<div class="row">
<div>
<img class="img-2" src="https://jspwiki-wiki.apache.org/attach/Slimbox/doggy.bmp">
<p>
Blah-blah-blah. Yjis is the second div.
</p>
</div>
</div>
</div>
</div>
<div class="menu" id="content3">
<h3>Third Div</h3>
<div class="present">
<div class="row">
<div>
<p>
Blah-blah-blah. This is the third div.
</p>
<img class="img-1" src="http://www.newyorker.com/wp-content/uploads/2014/08/Stokes-Hello-Kitty2-1200.jpg">
</div>
</div>
<div class="row">
<div>
<img class="img-2" src="https://jspwiki-wiki.apache.org/attach/Slimbox/doggy.bmp">
<p>
Blah-blah-blah. This is the third div.
</p>
</div>
</div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
</body>
</html>
Sorry for the ugly sketch and pictures - it is only to get an idea what it should look like....
In general, it's poor form to ask on Stack Overflow how to code for a specific behavior. However, that takes some understanding of the libraries you're using, and what you are trying to achieve. Hopefully, my answer will help you better articulate and form your questions in the future.
Here's a fiddle for you: https://jsfiddle.net/hwd4b0ag/
In particular, I've modified your last click listener:
var pictures = $(".img-1, .img-2").on("click", function() {
var parentDiv = $(this).closest('div.menu').hide();
var blownUpPic = $("<img>").attr({
id: 'pic',
src: this.src,
'data-parent': parentDiv.attr('id')
})
.appendTo("section")
.on('click', function() {
$('#' + $(this).attr('data-parent')).show();
$(this).remove();
});
});
Now, let's review it!
First,
var parentDiv = $(this).closest('div.menu').hide();
In a jQuery listener, the this variable stores the current javascript DOM element that is the recipient of the event listener. In your case, it refers to an element that matches ".img-1, .img-2".
.closest(selector) will traverse up the DOM (including the current element) and find the first matching element for the provided selector. In this case, it finds your container div with class menu. Then we hide that div and save a reference to it in a variable.
Next, we create a full-sized version of the picture and assign it some attributes:
var blownUpPic = $("<img>").attr({
id: 'pic',
src: this.src,
'data-parent': parentDiv.attr('id')
})
We set the data-parent attribute to the id of our container div, so we have a reference back to it later.
We then add our image to the DOM:
.appendTo("section")
And declare a new click listener for it:
.on('click', function() {
$('#' + $(this).attr('data-parent')).show();
$(this).remove();
});
With $(this).attr('data-parent') we use the reference to our container div that we assigned earlier, and then retrieve that element by its id. We unhide the container div and remove the full-sized image.
All done!
There are better ways to code this, but I think this is a good next step for you that's analogous to your current code.

jquery .show() not working

I have this h1 element:
<h1><a id="toobin">Text here</a></h1>
Im using this to try and show it:
$("#nav_list").click(function() {
$("h1:gt(0)").hide();
$("#toobin h1").show();
});
The first line hides all of my h1 elements and trying to show just the one too bin h1 element won't work.
If I do this $("h1").show(); I can get all of the h1 elements to show. What am I doing wrong?
I think what you want is:
$("h1:has(#toobin)").show();
The :has(<selector>) modifier selects elements that contain an element matching the parenthesized selector.
#toobin h1 means all h1 that are inside #toobin, but #toobin is inside the h1.
$("#nav_list").click(function() {
$("h1:gt(0)").hide();
$("h1:has(#toobin)").show();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1>First, won't be hidden</h1>
<h1>Will be hidden</h1>
<h1>Will also be hidden</h1>
<h1><a id="toobin">Should stay</a></h1>
<h1>Another hidden one</h1>
<button id="nav_list">Click to hide</button>

Finding the index of elements of the same class but with different parents

Say I have elements of the same class but the're wrapped in different parent divs. How would I go about in finding the index of each .child div?
If I were to invoke the index in an iterator I'd get back 0 since its the one and only .child present within the parent div. What I'm looking to do is get back index of those divs based within the scope of the #container div.
$(function() {
$("#container .child").text(function() {
var i = $(this).index();
return "The index of this element is " + i
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
<div id="one">
<div class="child"></div>
<div>
<div id="two">
<div class="child"></div>
<div>
<div id="three">
<div class="child"></div>
<div>
</div>
The first argument to your callback function passed to the .text() method is the index within the current set.
$(function() {
$("#container .child").text(function(i) {
return "The index of this element is " + i
});
});
If you want to use index you can use the selector and then pass in the element to get the index in the collection
$("#container .child").index(this)
or even the other way around
$(this).index("#container .child")
but the easiest is to just use the iterator in the callback function for text()
$("#container .child").text(function(i) {
return "The index of this element is " + i
});

Remove everything except child

<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.>"));
});

Categories