jQuery - different behaviour of before() and after() - javascript

I have a sortable list (not jQuery UI sortable, just by a simple click on buttons).
Sorting to the right via after() works just fine, but sorting to the left with before() does not work.
First of all the code:
Html
PhotoList:
<ul class="PhotoList">
<li>
<div class="ThumbWrapper">
<img src="images/img_testphoto.jpg" />
</div>
</li>
[...more Elements...]
</ul>
Selectable List:
(positioned absolutely over the photos)
<ul class="SelectablePolaroids">
<li>
<div class="Selectable">
<div class="Tools">
<div class="ArrowLeft Tool">
</div>
<div class="Delete Tool">
</div>
<div class="ArrowRight Tool">
</div>
</div>
</div>
</li>
[...more Elements...]
</ul>
JS
Bind functions
jQuery('.SelectablePolaroids .Selectable .ArrowLeft').live('click', function(){sortPolaroid(jQuery(this),0)});
jQuery('.SelectablePolaroids .Selectable .ArrowRight').live('click', function(){sortPolaroid(jQuery(this),1)});
Function
function sortPolaroid(elm, right)
{
var selectitem = elm.parents('li');
var index = jQuery('.SelectablePolaroids').children().index(selectitem);
var photo = jQuery('.PhotoList').children().eq(index);
selectitem.remove();
photo.remove();
if(right)
{
jQuery('.SelectablePolaroids').children().eq(index).after(selectitem);
jQuery('.PhotoList').children().eq(index).after(photo);
}
else
{
console.log(index);
jQuery('.SelectablePolaroids').children().eq(index).before(selectitem);
jQuery('.PhotoList').children().eq(index).before(photo);
}
}
If right is true or .ArrowRight was clicked, it works just as expected. The item is removed and inserted one position further to the right.
As you can see I logged index to check if the else statement is executed at all and whether the index is correct. And yeah the else statement is executed and the index is correct as well. Is before() working in another way than after() or am I just blind to another mistake?
EDIT:
I have also logged the index after before().
//...
else {
console.log(index);
jQuery('.SelectablePolaroids').children().eq(index).before(selectitem);
console.log(jQuery('.SelectablePolaroids').children().index(selectitem));
jQuery('.PhotoList').children().eq(index).before(photo);
}
And it stays the same, nothing changes at all... but it was remove, which means it is inserted at exactly the same position, therfore it works if I am doing the following - my Solution:
//...
else {
jQuery('.SelectablePolaroids').children().eq(index-1).before(selectitem);
jQuery('.PhotoList').children().eq(index-1).before(photo);
}
But I don't really know why... the new element should be at the index, and before should insert it before index...
If someone knows please answer I will gladly accept an answer :-)
...
Okay, figured it out by myself and answered, sorry about the ruckus, but maybe some other people don't know as well and are thinking in the same way as me :-)

photo.remove(); actually removes the dom element from the tree so I think there is mismatching of indexes. Try using before/after without removing, that just moves the element.

By removing the element the next and not the previous element gets the index, so it will be inserted into the same position.
It works with after() because the element getting the new index is in the "right direction", therefore it works with the following:
//...
else
{
jQuery('.SelectablePolaroids').children().eq(index-1).before(selectitem);
jQuery('.PhotoList').children().eq(index-1).before(photo);
}
Just a logical mistake... solved after appropiate thinking :-)

Related

Remove any specific html code using javascript

In the past I used Google Developer Console to delete some specific divs on a page. I could do it manually of course but in some cases where the divs where many I had to use the console. I had a single line code that did the job (I found it while searching the internet) but I lost my note.
So how can I delete using javascript any html code (by copy pasting the code).
Something like:
elements = $('<div ... </div>');
elements.remove();
OR
$('<div ... </div>').remove();
Any ideas? I am not an expert in javascript (obviously) and I've been searching stackoverflow for hours without finding anything that works.
UPDATE: I think some people might get confused with my question. Google developer console accepts javascript command lines. So even though I ask for javascript I will use the code on the google developer console.
UPDATE 2 :
Here is an example of a div I need to delete. Keep in mind I want to copy paste the entire code in the javascript code. Not just identify the div.
<div class="entry-status-overlay" data-entry-status="declined">
<div class="entry-status-overlay__inner">
<span class="entry-status-overlay__title">Declined</span>
</div>
</div>
It's the data-entry-status="declined" that makes that div unique so I can't just identify the div using an id selector or a class selector. I need to put the entrire thing there and remove it.
I tried:
$('<div class="entry-status-overlay" data-entry-status="declined"><div class="entry-status-overlay__inner"><span class="entry-status-overlay__title">Declined</span></div></div>').remove();
It didn't remove the div.
Try to search the dom by its outerHTML.
function deleteDomByHtml(html){
html=html.replace(/\s/g,'');
$("*").each(function(){
if(this.outerHTML.replace(/\s/g,'')===html){
$(this).remove();
}
});
}
And try this line on this page:
deleteDomByHtml(`<span class="-img _glyph">Stack Overflow</span>`);
You cannot do by simply pasting the code. That will remove all the div element.
You may need a specific selector like id,class or child to specific parent to remove the element from the dom.
Consider this case the divs have common class but the data-entry-status is different. So you can get the dom using a selector and then check the dataset property.
For demo I have put it inside setTimeout to show the difference. In application you can avoid it
setTimeout(function() {
document.querySelectorAll('.entry-status-overlay').forEach(function(item) {
let getStatus = item.dataset.entryStatus;
if (getStatus === 'declined') {
item.remove()
}
})
}, 2000)
<div class="entry-status-overlay" data-entry-status="declined">
<div class="entry-status-overlay__inner">
<span class="entry-status-overlay__title">Declined</span>
</div>
</div>
<div class="entry-status-overlay" data-entry-status="accepted">
<div class="entry-status-overlay__inner">
<span class="entry-status-overlay__title">accepted</span>
</div>
</div>
Just add any attribute with [] and it will remove the element.
$('[class="entry-status-overlay"]').remove();
/*OR*/
$('[data-entry-status="declined"]').remove();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div class="entry-status-overlay" data-entry-status="declined">
<div class="entry-status-overlay__inner">
<span class="entry-status-overlay__title">Declined</span>
</div>
</div>
function del(){
var h = document.body.outerHTML;
h = h.match('<div>...</div>');
h.length--;
return h;
}
I guess this will work just give it a try... i tried on browser console and it worked, this way you can match the exact you want.
I might as well add my take on this. Try running this in your console and see the question vanish.
// convert the whole page into string
let thePage = document.body.innerHTML,
string = [].map.call( thePage, function(node){
return node.textContent || node.innerText || "";
}).join("");
// I get some string. in this scenario the Question or you can set one yourself
let replacableCode = document.getElementsByClassName('post-layout')[0].innerHTML,
string2 = [].map.call( replacableCode, function(node){
return node.textContent || node.innerText || "";
}).join("");
// replace whole page with the removed innerHTML string with blank
document.body.innerHTML = thePage.replace(replacableCode,'');
If you want to identify divs with that particular data attribute, you can use a data-attribute selector. In the example below, I've used a button and click event to make the demo more visual, but in the console the only line you'd need would be:
$('div[data-entry-status="declined"]').remove();
$(function() {
$("#testbutton").click(function() {
$('div[data-entry-status="declined"]').remove();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="entry-status-overlay" data-entry-status="declined">
<div class="entry-status-overlay__inner">
<span class="entry-status-overlay__title">Declined</span>
</div>
</div>
<div id="x">Some other div</div>
<button type="button" id="testbutton">Click me to test removing the div</button>
See https://api.jquery.com/category/selectors/attribute-selectors/ for documentation of attribute selectors.
P.S. Your idea to paste some raw HTML into the jQuery constructor and then execute "remove" on it cannot work - you're telling jQuery to create an object based on a HTML string, which is, as far as it's concerned, a new set of HTML. It does not try to match that to something existing on the page, even if that exact HTML is in the DOM somewhere, it pays it no attention. It treats what you just gave it as being totally independent. So then when you run .remove() on that new HTML...that HTML was never added to the page, so it cannot be removed. Therefore .remove() has no effect in that situation.

Replacing an Element with it's Contents IE8

I am trying to replace some "nested" classes from some HTML with javascript/jquery. I need to get rid of the spans with class="foo" and leave behind only "stuff".
<ul>
<li>
<span class="foo">stuff
<ul>
<li><span class="foo">stuff</span>
</li>
</ul>
</span>
</li>
</ul>
This works in everything but IE8 (which I must unfortunately support):
$(".foo").each(function () {
$(this).replaceWith(this.innerHTML);
});
Before someone points out that there are other similar questions, please note that I've tried several methods outlined in those questions and I think my use case differs because of the "nesting". I've tried the solutions in topics on this site and others. Some of these completely crash IE8, others just don't work.
I am aware of trying to use .empty() before replaceWith, but this doesn't help... my problem isn't performance (yet) it is getting it to even work at all.
I've also tried this:
$(".foo").each(function () {
var HTMLshunt = this.innerHTML;
$(this).after(HTMLshunt).remove();
});
Why the "HTMLshunt" var? I was working on the premise that maybe it wasn't working in IE8 because the "after" wasn't really going "after" .foo, but inside it... because in IE8 something happens with this one: it eliminates every .foo but leaves no contents of foo behind.
This "nesting" isn't helping. I think something else cobbled together along the way would have worked if it weren't for the nesting, but it doesn't matter because there IS this nesting. If anyone can help, please respond.
You could try this, should be IE8 friendly (though I do believe jQuery 1.11.0 should also support IE8). If reflow is any worry then you could cloneNode first at the expense of some memory instead.
HTML
<ul>
<li>
<span class="foo">stuff
<ul>
<li>
<span class="foo">stuff</span>
</li>
</ul>
</span>
</li>
</ul>
Javascript
var fooClasses = document.querySelectorAll('.foo'),
fooIndex,
foo,
fragment;
for (fooIndex = fooClasses.length - 1; fooIndex >= 0; fooIndex -= 1) {
fragment = document.createDocumentFragment();
foo = fooClasses[fooIndex];
while (foo.firstChild) {
fragment.appendChild(foo.firstChild);
}
foo.parentNode.replaceChild(fragment, foo);
}
On jsFiddle
With a cloned node
var fooClasses = document.querySelectorAll('.foo'),
fooIndex,
foo,
fragment,
clone;
for (fooIndex = fooClasses.length - 1; fooIndex >= 0; fooIndex -= 1) {
fragment = document.createDocumentFragment();
foo = fooClasses[fooIndex];
clone = foo.cloneNode(true);
while (clone.firstChild) {
fragment.appendChild(clone.firstChild);
}
foo.parentNode.replaceChild(fragment, foo);
}
On jsFiddle
It seems as though you are using jQuery. In that case, you should just use the unwrap() method:
Description: Remove the parents of the set of matched elements from the DOM, leaving the matched elements in their place.
As an example:
$(".foo").each(function () {
$(this).children().unwrap();
});
jsFiddle demonstration
This fiddle uses jQuery 1.9.1, so it should function in IE8.
EDIT
Okay, so the problem is that jQuery.unwrap doesn't work when a node only contains text content.
In order to work with text content, you'll have to use a slightly different approach:
$(".foo").each(function() {
$(this).replaceWith(this.childNodes);
});
See this fiddle
Full disclosure: I used this answer for the technique.

JS - Shouldn't this be written better? One function vs multiples?

I have a tweet stream where new tweets are added at the top and the older ones pushed down. You can click on the entire tweet and a panel slides down to reveal, "reply", "retweet", "favorite" etc. The panel is added to each new tweet added in the stream.
The code below works. Shouldn't this be better written so that only one call is being made? Or, as a new tweet is added. would I just have to add to the code with div#tc4, ul#tb4 etc?
$(document).ready(function () {
$("div#tc1").click(function () {
$("ul#tb1").slideToggle("fast");
});
$("div#tc2").click(function () {
$('ul#tb2').slideToggle("fast");
});
$("div#tc3").click(function () {
$('ul#tb3').slideToggle("fast");
});
});
Added Markup:
<div id="tc1" class="tweetcontainer">
<div class="avatarcontainer">
<div class="avatar"></div>
</div>
<div class="content">
<div class="tweetheader">
<div class="name">
<h1>John Drake</h1>
</div>
<div class="tweethandle">
<h2>#Drakejon</h2>
</div>
<div class="tweettime">10m</div>
</div>
<div>
<p>Exceptional Buys Ranger To Give Monitoring Shot In The Arm To Its 'DevOps' Platform http://tcrn.ch/11m3BrO by #sohear </p>
</div>
</div>
</div>
<!-------------Tool Bar -------------------------------->
<ul id="tb1" class="toolbar">
<li><a class="reply" href="#"><span>reply</span></a></li>
<li><a class="retweet" href="#"><span>retweet</span></a></li>
<li><a class="favorite" href="#"><span>favorite</span></a></li>
<li><a class="track" href="#"><span>track</span></a></li>
<li><a class="details" href="#"><span>details</span></a></li>
</ul>
I highly recommend separating your javascript from your detailed page function. The best way to do this is to put the retweeting panel inside the tweet container, then you don't even need to give it an id at all or encode in the javascript information about your html structure and ids. You can then just do:
$('.tweetcontainer').on('click', function(event) {
if ($(event.target).is(':descendantof(.toolbar)')) {
//ignore all clicks within the toolbar itself
return;
}
$(this).find('.toolbar').slideToggle();
});​
It's that easy! See it in action in a jsFiddle.
Now you can add as many tweet containers as you want to your page--and your javascript doesn't have to change one bit. Other solutions that require knowledge of specific ids linking to specific ids are suboptimal.
Note the descendantof pseudo-selector is custom (see the fiddle to find out how it works). Also, since you didn't provide any css, I had to choose some--it was quick so don't expect much. (Aww heck I just saw you updated your question to provide a jsFiddle with css giving a far prettier result--but I won't change mine now.) I did have to add a class to the actual tweet itself, but there is probably a better way to style it.
And if you want a click on the displayed toolbar itself (outside of a link) to allow collapsing the toolbar, change the code above to :descendantof(a).
If you don't want to change your page layout, another way to it is to encode the information about the linkage between html parts in the html itself using a data attribute. Change your tweetcontainer div to add a data attribute with a jQuery style selector in it that will properly locate the target:
<div class="tweetcontainer" data-target="#tb1">
You don't really have to remove the id if you use it elsewhere, but I wanted you to see that you don't need it any more. Then on document.ready:
$('.tweetcontainer').click(function () {
$($(this).data('target')).slideToggle('fast');
});
Here is another jsFiddle demonstrating this alternate technique (though it less elegant, in my opinion).
Last, I would like to mention that it seems possible you have a little bit of "div-itis". (We have all been there.) The toolbar anchor elements have unnecessary spans inside of them. The tweet name h1 element is inside a div, but could just be an h1 with class="name" instead.
In general, if there is only a single item inside a div and you can change your stylesheet to eliminate the div, then the div isn't needed. There are an awful lot of nested divs in your html, and I encourage you to remove as many of them as you can. Apply style to the other block elements you use and at least some, if not many, won't be needed.
I'd suggest (though currently untested):
$('div[id^="tc"]').click(function(){
var num = parseInt(this.id.replace(/\D+/g,''),10);
$('#tb' + num).slideToggle("fast");
});
Though given that you don't need the num to be a number (it'd be fine as a string), you could safely omit the parseInt().
Yes, you can write this code much more compactly like this:
$(document).ready(function() {
for (var i = 1; i < 3; i++) {
$("div#tc" + i).click(function() { $("ul#tb" + i).slideToggle("fast"); } );
}
});

If statement involving this.indexof in order to animate an element not working

I have checked many of other questions involving issues like this, but none have helped.
I'm very new to this so bare with my obvious lack of knowledge.
I was trying to create a navigation where once mouseenter on the navigation link itself, the nav link will be animated to rise up by 5px and a box of the same width would drop down to meet it in the middle. I can think of a way to do it but it will involve a lot more code than if i could use an if statement instead.
<div id="header" class="wrapper">
<div id="header_logo" class="header"></div>
<div id="header_main" class="header">
<div id="nav_drop_btn">
<div id="nav_drop_box">
<span id="1" class="nav_drop"></span>
<span id="2" class="nav_drop"></span>
<span id="3" class="nav_drop"></span>
<span id="4" class="nav_drop"></span>
</div>
<span class="nav_btn">Home</span>
<span class="nav_btn">About</span>
<span class="nav_btn">Contact</span>
<span class="nav_btn">Portfolio</span>
</div>
$(".nav_btn").mouseenter(function(){
$(this).animate({height:"25px",bottom:"0px"},400);
if (this.indexOf("home") >=-1){$("#1").animate({height:"50px"})};
});
.nav_btn is the class for all of the navigation links. home is the navigation button in question.
I am pretty sure i have done this all wrong. so would very much appreciate if someone could help me to understand what is wrong with the If statement. the rest of my code works its just this.
home is text within the <span> for .nav_id, im trying to distinguish between which .nav_btn has been clicked to then activate another line of code to animate the corresponding top bar which comes down to meet it, this top bar containing an image for that particular navigation link
this does not represent a string value. If you're looking for a value to search with indexOf, and it's a link, you'll probably want to use this.href.indexOf instead.
Test
Test 2
$('a').mouseenter(function(){
console.log(this.href.indexOf('/home'));
if (this.href.indexOf('/home') !== -1) {
console.log('Mouse enter home');
}
});
http://jsfiddle.net/kwPm6/
And you can use jQuery's .text() if you want to search the link's visible text node value:
Test
Other
$('a').mouseenter(function(){
console.log($(this).text().indexOf('Test'));
if ($(this).text().indexOf('Test') !== -1) {
console.log('Mouse enter home');
}
});
http://jsfiddle.net/gJCp8/
And with SPAN's:
<span>Test</span>
<span>Other</span>
$('span').mouseenter(function(){
console.log($(this).text().indexOf('Test'));
if ($(this).text().indexOf('Test') !== -1) {
console.log('Mouse enter home');
}
});
http://jsfiddle.net/gJCp8/1/
And as the other answer from N.G. suggests, you need to test for something NOT equal to -1, and additionally you might consider using .toLowerCase().indexOf('home') to normalize the string test value and search value casing.
To wit, just looking at your code and making these adjustments, I come up with this:
$(".nav_btn").mouseenter(function(){
$(this).animate({height:"25px",bottom:"0px"},400);
if ($(this).text().toLowerCase().indexOf("home") !== -1){
$("#1").animate({height:"50px"});
}
});
indexOf is not the correct function to use for this. You could give the home button a special class/id to allow it to behave differently on mouseenter.
$(".nav_btn").mouseenter(function(){
$(this).animate({height:"25px",bottom:"0px"},400);
});
$("#home").mouseenter(function() {
$(this).animate({height:"50px"});
});
Your indexOf is checking against >= -1
This will always be true. -1 is the value returned if the search string is not found.
Perhaps you mean to check: if (this.indexOf("home") > -1)
instead?

Facing weird problem while adding and removing class

i have below function which is being used to initialize a widget.
jQuery.fn.initPortlet = function( parent_component ,header , component ){
var o = $(this[0])
this.addClass("ui-widget ui-widget-content ui-corner-all")
.find(header)
.addClass("headertitle")
.addClass("align_center")
.addClass("defaultheadercolor")
.prepend('<span class="ui-icon ui-icon-minusthick"></span>')
.end()
.find(component);
};
what it does is append a minus icon at the top left corner of the widget.
i have some ajax call because of that this function get called multiple time and append a minus icon multiple times.
i am tring to re-write this function in such a way, so that how many time it's get called, append only one minus icon into header.
i tried fallowing approach but it didn't work.
var $minusthick = $('span.ui-icon ui-icon-minusthick');
$('div.div_header').find($minusthick).remove().prepend('<span class="ui-icon ui-icon-minusthick"></span>').end();
what i am tring is remove all span with class name span.ui-icon ui-icon-minusthick and finally append a minus icon, but it's not worked for me.
Edit
i am calling this function in this way-
$('.div_portlet').initPortlet('.div_portlet','.div_header','.div_content')
$('.div_portlet_inner').initPortlet('.div_portlet_inner','.div_header_inner','.div_content_inner');
html corresponding to this is-
html:
<div class="div_portlet" id="LINEITEM_HEADER" >
<div class="div_header"><%=hu.getFrameURL(82,83)%> Line Item Header Information</div>
<div class="div_content" id="LINEITEM_HEADER_CONTENT">
</div>
</div>
for second call html will remain same just classes will get change from div_portlet to div_portlet_inner, in the same way for other class.
i have written this function in a js file.
any help or suggestion so that i can achieve my goal will be highly appreciated.
Please guys help me out i got stuck at this point.
Thanks in advance!!!!!
Not sure what variable o is being used for - but the general point of my alteration below is to check to see if the class has been applied already, using the jQuery hasClass() function.
jQuery.fn.initPortlet = function( parent_component ,header , component ){
var o = $(this[0])
if (!this.hasClass('ui-widget'))
{
this.addClass("ui-widget ui-widget-content ui-corner-all")
.find(header)
.addClass("headertitle")
.addClass("align_center")
.addClass("defaultheadercolor")
.prepend('<span class="ui-icon ui-icon-minusthick"></span>')
.end()
.find(component);
}
};
ʞɔɐɯɹoↃɔW sǝɯɐſ gave a good solution to this problem, but here is an explanation why your attempt didn't work:
The first part of the selector 'span.ui-icon ui-icon-minusthick' is looking for a span with class ui-icon, as you intended, but the second part looks for an element of type <ui-icon-minusthick> which obviously doesn't exist. To select an element with multiple class names, add them all to the same selector just like you would in CSS:
$('span.ui-icon.ui-icon-minusthick')
Of course, the rest of you code would be a no-op since find($minusthick) will do nothing and therefore the rest of the jQuery chain will have no context in which to operate. This would (I think) work as you expected:
$('div.div_header').find('span.ui-icon.ui-icon-minusthick').remove().end().prepend('<span class="ui-icon ui-icon-minusthick"></span>');
The extra end() call returns the jQuery object to the first selector, in this case div.div_header and there is no need for the final end().

Categories