jQuery each combined with replaceWith issue - javascript

I am trying to loop through each element with a class and then wrap it with a div, and then continue to the next one.
Currently using this...
$('.img-editable').each(function(i) {
if ($(this).prop("tagName") == "IMG") {
$(this).replaceWith('<span class="img-edit-container">'+this.outerHTML+'<button class="btn btn-default btn-sm img-edit popover-trigger"><i class="fa fa-file-image-o"></i></button></span>');
} else {
$(this).replaceWith('<span class="img-edit-container bg-img-edit-container">'+this.outerHTML+'<button class="btn btn-default bg-img-edit img-edit popover-trigger"><i class="fa fa-file-image-o"></i></button></span>');
}
});
It works in simple cases. But the challenge is when there is nested elements like this...
<div class="img-editable>
<div id="something else">
<div class="img-editable"></div>
</div>
</div>
Then it falls apart and the inner element doesn't get wrapped correctly.
My belief is the each() function is looping the outermost elements and when it matches the first one then it performs the replaceWith() and then continues. But by the time the loop gets to the next one (inner), the reference to second inner element that matches is no longer there (according to the browser).
I also tried this...
var setupImgWrapper = function(elem) {
if (elem.prop("tagName") == "IMG") {
elem.replaceWith('<span class="img-edit-container">'+this.outerHTML+'<button class="btn btn-default btn-sm img-edit popover-trigger"><i class="fa fa-file-image-o"></i></button></span>');
elem.children('.img-edtiable').each(function(i) {
setupImgWrapper($(this));
});
} else {
elem.replaceWith('<span class="img-edit-container bg-img-edit-container">'+this.outerHTML+'<button class="btn btn-default bg-img-edit img-edit popover-trigger"><i class="fa fa-file-image-o"></i></button></span>');
elem.children('.img-edtiable').each(function(i) {
setupImgWrapper($(this));
});
}
}
$('.img-editable').each(function(i) {
setupImgWrapper($(this));
});
This completely failed.
I would love to know how we can overcome this? Is there a way to maintain the DOM reference after replaceWith() is performed, or can we update it?
TL;DR After performing replaceWith in loop on selected elements, descendant element references seem to break and these elements aren't updated successfully.
Thanks!
Solution As Accepted Below
$('.img-editable').each(function(i) {
if ($(this).prop("tagName") == "IMG") {
$(this).wrap('<span class="img-edit-container"></span>');
$(this).after('<button class="btn btn-default btn-sm img-edit popover-trigger"><i class="fa fa-file-image-o"></i></button>');
} else {
$(this).wrap('<span class="img-edit-container bg-img-edit-container"></span>');
$(this).after('<button class="btn btn-default bg-img-edit img-edit popover-trigger"><i class="fa fa-file-image-o"></i></button>');
}
});

There is a .wrap() method provided by jQuery that does exactly what you need.
Here's a demo
$('body > div').each(function() {
$(this).wrap('<div class="wrapper" />');
});
.wrapper {
border: 1px solid tomato;
margin-bottom: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>Normal Element</div>
<div>
<span>Simple inline element</span>
</div>
<div>
<div>
<h1>More Stuff</h1>
</div>
</div>
If .img-editable > ... > .img-editable breaks your code, you should prefer a better selector that fit your needs. I can assist you with that, just post a comment to this answer and we'll figure something out. :)

Try wrapall api along with each
$( ".img-editable" ).wrapAll( "<span class='img-edit-container'/>");
$(".img-edit-container .img-editable").each( function(){
if ( $( this ).prop("tagName") == "IMG" )
{
$(this).parent().append ( '<button class="btn btn-default btn-sm img-edit popover-trigger"><i class="fa fa-file-image-o"></i></button>' );
}
else
{
$(this).parent().append ( '<button class="btn btn-default bg-img-edit img-edit popover-trigger"><i class="fa fa-file-image-o"></i></button>' );
}
});
or even simpler
$( ".img-editable" ).wrapAll( "<span class='img-edit-container'/>");
$(".img-edit-container").append( '<button class="btn btn-default bg-img-edit img-edit popover-trigger"><i class="fa fa-file-image-o"></i></button>' );
$(".img-edit-container img.img-editable").siblings( "button" ).toggleClass( "btn-sm" ).toggleClass( "bg-img-edit" );

Related

Interaction between 3 buttons

This question is the following of that. At initialization of the page, the buttons are not "bridged", and becomes when the user clicks on the blue button with the icon inside.
Conditions :
If 2 buttons are bridged (action when clicked on blue button), the other must become bridged (join with numbers around) [this works]
After that, if the user clicks on any button, the 3 buttons have to separate from numbers around
I have this HTML code (using Bootstrap 3) :
<div class="col-sm-12 interfacesBridge">
<h3 class="text-info">Interfaces</h3>
<div class="col-sm-4">
<div class="input-group input-group-lg">
<div class="input-group-btn">
<button type="button" class="btn btn-default bRLeft" disabled="disabled" style="margin-right: 15px !important;">3</button>
<button type="button" class="btn btn-info linkInterfaces" data-interfaces="3-4"><i class="fa fa-chain-broken"></i></button>
<button type="button" class="btn btn-default bRRight" disabled="disabled" style="margin-left: 15px !important;">4</button>
</div>
</div>
</div>
<div class="col-sm-4">
<div class="input-group input-group-lg">
<div class="input-group-btn">
<button type="button" class="btn btn-default bRLeft" disabled="disabled" style="margin-right: 15px !important;">3</button>
<button type="button" class="btn btn-info linkInterfaces" data-interfaces="3-5"><i class="fa fa-chain-broken"></i></button>
<button type="button" class="btn btn-default bRRight" disabled="disabled" style="margin-left: 15px !important;">5</button>
</div>
</div>
</div>
<div class="col-sm-4">
<div class="input-group input-group-lg">
<div class="input-group-btn">
<button type="button" class="btn btn-default bRLeft" disabled="disabled" style="margin-right: 15px !important;">4</button>
<button type="button" class="btn btn-info linkInterfaces" data-interfaces="4-5"><i class="fa fa-chain-broken"></i></button>
<button type="button" class="btn btn-default bRRight" disabled="disabled" style="margin-left: 15px !important;">5</button>
</div>
</div>
</div>
</div>
And this jQuery code :
function setBridged(selector) {
selector.addClass('bridged');
selector.prev().animate({'marginRight': '0', 'marginLeft': '15px'}, 1000);
selector.next().animate({'marginLeft': '0'}, 1000);
selector.find('i').removeClass().addClass('fa fa-link');
}
function unsetBridged(selector) {
selector.removeClass('bridged');
selector.prev().animate({'marginRight': '15px', 'marginLeft': '0'}, 1000);
selector.next().animate({'marginLeft': '15px'}, 1000);
selector.find('i').removeClass().addClass('fa fa-chain-broken');
}
$(document).ready(function () {
$('.linkInterfaces').click(function () {
if ($(this).find('i').hasClass('fa-chain-broken')) {
setBridged($(this));
} else {
unsetBridged($(this));
}
if ($('.interfacesBridge .bridged').length >= 2) {
$('.linkInterfaces:not(.bridged)').trigger('click', function() {
setBridged($(this));
});
}
});
});
I tried many things to get there, but that's me stuck.
Please see the jsfiddle associated.
After observing the conditions, the click event's trigger timing is:
When something bridged, if there's more than 2 (include self), then bridge every one that is not bridge yet.
When something unbridged, if there's still more than 2 bridged, which means prev state is all bridged, unbridge all.
So you can change the logic to:
$('.linkInterfaces').click(function () {
if ($(this).find('i').hasClass('fa-chain-broken')) {
//targetFunc = setBridged;
setBridged($(this));
if ($('.interfacesBridge .bridged').length >= 2) {
$('.linkInterfaces:not(.bridged)').trigger('click');
}
} else {
unsetBridged($(this));
if ($('.interfacesBridge .bridged').length >= 2) {
$('.interfacesBridge .bridged').trigger('click');
}
}
});
Altered jsfiddle.
It's also possible to extract the logic out in this case, but note if the condition to bridge/unbridge all becomes different(e.g: >= 2 to bridge, <= 1 to unbridge), than the first one would be better to separate the logic.
$('.linkInterfaces').click(function () {
var relatedTargets;
if ($(this).find('i').hasClass('fa-chain-broken')) {
setBridged($(this));
relatedTargets = '.linkInterfaces:not(.bridged)';
} else {
unsetBridged($(this));
relatedTargets = '.linkInterfaces.bridged';
}
// When bridged element is more than 2 when clicked, we need to either link all, or unlink all.
if ($('.interfacesBridge .bridged').length >= 2) {
$(relatedTargets).trigger('click');
}
});
And note the .trigger's 2nd or later params would be passed to event handler, but won't trigger, so in your original code, that passed in function won't take effect unless you call it in your event handler.

Bootstrap data-toggle="button" not working with <span>

I'm using the 'data-toggle'-option from Bootstrap for a button. In the button I have a with glypicons and sometext. However, when you click on the text it works, but when you click on the glyphicon it doesn't trigger the data-toggle.
Since the data-toggle does not work in JSFiddle I cannot post a link to it.
HTML:
<button type="button" id="showHide" class="btn btn-default" data-toggle="button"><span class="glyphicon glyphicon-eye-open"></span> show</button>
JavaScript:
document.getElementById("showHide").onclick = function () {
if ($("#showHide").text() == " show") {
document.getElementById("showHide").innerHTML = "<span class=\"glyphicon glyphicon-eye-close\"></span> hide";
document.getElementById("showHide").blur();
} else {
document.getElementById("showHide").innerHTML = "<span class=\"glyphicon glyphicon-eye-open\"></span> show";
document.getElementById("showHide").blur();
}
}
I've looked online but haven't found a solution or someone with the same problem.
It appears the span is being replaced before the toggle event is propogated. You can use the button method to activate the toggle rather than the data attribute.
<button type="button" id="showHide" class="btn btn-default"><span class="glyphicon glyphicon-eye-open"></span> show</button>
document.getElementById("showHide").onclick = function () {
if ($("#showHide").text() == " show") {
document.getElementById("showHide").innerHTML = "<span class=\"glyphicon glyphicon-eye-close\"></span> hide";
jQuery("#showHide").button('toggle');
document.getElementById("showHide").blur();
} else {
document.getElementById("showHide").innerHTML = "<span class=\"glyphicon glyphicon-eye-open\"></span> show";
jQuery("#showHide").button('toggle');
document.getElementById("showHide").blur();
}
}
View Fiddle

how to add class using angular js

I want to add a class to the i tag after clicking of button. how can I achieve this using angular js.
<button type="button" title="Like" ng-click="countLikes(product.title)" class="btn btn-compare">
<i class="fa fa-thumbs-o-up"></i>
</button>
$scope.countLikes = function (e) {
console.log(e);
}
I wanted output like this. Added exampleClass to the i tag
<button type="button" title="Like" ng-click="countLikes(product.title)" class="btn btn-compare">
<i class="fa fa-thumbs-o-up exampleClass"></i>
</button>
use ng-class directive
<i class="fa fa-thumbs-o-up" ng-class="{'exampleClass' : buttonClicked}"></i>
$scope.countLikes = function (e) {
$scope.buttonClicked = true;
console.log(e);
}
when buttonClicked is true i element will get the exampleClass css class
<button type="button" title="Like" ng-click="countLikes(product.title)" class="btn btn-compare">
<i class="fa fa-thumbs-o-up" ng-class="{'exampleClass': isClick}"></i>
</button>
$scope.isClick = false;
$scope.countLikes = function (e) {
$scope.isClick = true;
}
Here is my answer
<button type="button" title="Like" class="btn btn-compare" ng-click="(myLike = myLike === 'fa-thumbs-o-up' ? 'fa-thumbs-up' : 'fa-thumbs-o-up'); countLikes(product.title)";>
<i class="fa" ng-class="myLike"></i>
</button>
$scope.myLike = "fa-thumbs-o-up";

Why is an on "click, "button" being handled by a span instead?

I'm experiencing something that seems quite wrong with events and jquery.
I have the following simple HTML list
<ul>
<li><button class="btn btn-danger btn-xs"><span class="glyphicon glyphicon-minus-sign"></span></button> Chocolate</li>
<li><button class="btn btn-danger btn-xs"><span class="glyphicon glyphicon-minus-sign"></span></button> Butter</li>
<li><button class="btn btn-danger btn-xs"><span class="glyphicon glyphicon-minus-sign"></span></button> Milk</li>
<li><button class="btn btn-danger btn-xs"><span class="glyphicon glyphicon-minus-sign"></span></button> ugar</li>
</ul>
and the following JavaScript:
$(document).on("click", "button", function(evt) {
console.debug("got a click in", evt.target);
});
When I click in a button I get
got a click in <span class="glyphicon glyphicon-minus-sign"></span>
and not in the button.
Why?
The target property of event objects points to the originator of the event; here, it’s the element that was the first target of the click, and that was apparently the <span>.
If you want the element selected by .on(), jQuery provides it as this.
$(document).on("click", "button", function(evt) {
console.debug("got a click in", this);
});
(Note that you don’t have to wait for $(document).ready when using delegation rooted in document.)
Any element can receive a click event. If it doesn't handle it the event will "bubble up" through parent elements.

why does my Bootstrap javascript not work?

I have looked at the other questons and double checked. My javascript resources are in order, there are no path problems, i even have jquery items working on my page. I thew this code right above the "Social Media Icons" heading for the sake of testing and still nothing.
<button type="button" class="btn btn-default" data-toggle="tooltip" data-placement="left" title="Tooltip on left">Tooltip on left</button>
<button type="button" class="btn btn-default" data-toggle="tooltip" data-placement="top" title="Tooltip on top">Tooltip on top</button>
<button type="button" class="btn btn-default" data-toggle="tooltip" data-placement="bottom" title="Tooltip on bottom">Tooltip on bottom</button>
<button type="button" class="btn btn-default" data-toggle="tooltip" data-placement="right" title="Tooltip on right">Tooltip on right</button>
$(document).ready(function () {
$('#section-nav a').tooltip();
$(".tab").tabs();
$(".tabs-bottom .ui-tabs-nav, .tabs-bottom .ui-tabs-nav > *")
.removeClass("ui-corner-all ui-corner-top")
.addClass("ui-corner-bottom");
// move the nav to the bottom
$(".tabs-bottom .ui-tabs-nav").appendTo(".tabs-bottom");
$('.navbar-toggle').click(function () {
var target = $('#navigation .shopbar');
if (target.hasClass('border-bottom')) {
target.removeClass('border-bottom');
} else {
target.addClass('border-bottom');
}
});
});
From the Bootstrap docs.. (http://getbootstrap.com/javascript/#tooltips)
"For performance reasons, the Tooltip and Popover data-apis are opt-in, meaning you must initialize them yourself."
So you need to add this jQuery..
$("[data-toggle=tooltip]").tooltip();
To initialize the Tooltips.
Change data-original-title to title, then:
$('#section-nav a').tooltip();

Categories

Resources