I have the following code:
<span class='myClass'>My Text</span><button class='myBtn'>OK</button>
<span class='myClass'>My Text</span><button class='myBtn'>OK</button>
<span class='myClass'>My Text</span><button class='myBtn'>OK</button>
I'm looking to click on any of myClass and remove it's contents PLUS remove button right after that. I don't want to remove others - just the one I click on.
Thanks for any help.
If you are using jQuery:
$('.myClass').click(function() {
$(this).next().remove();
$(this).remove();
});
If plain javascript:
var spans = document.getElementsByClassName('myClass');
for(var i = 0; i < spans.length; i++) {
spans[i].onclick = function() {
this.parentNode.removeChild(this.nextSibling);
this.parentNode.removeChild(this);
};
}
Related
I have some HTML elements which are hidden by default until user try to search for it. I am thinking to change the class of the element to show when the DataTable search completed (if found).
Refer to the jQuery code below where I implement the search, what is the next step to show the parent or the container of it?
E.g. if the search key is c or cc or ccc, how to show its parent (in this case is <li class="show">)?
jQuery
$("#txtSearch").on('keyup keypress blur change', function(e) {
var val = $(this).val();
$('#mytable').DataTable().search(val).draw();
});
HTML
<li class="hide">aaa</li>
<li class="hide">bbb</li>
<li class="hide">ccc</li>
Take a look at this code Snipp,Iterate over the Hide classes and extract the inner text then compare your search results.
$("#txtSearch").on('keyup keypress blur change', function(e) {
var val = $(this).val();
var numItems = $('.hide').length ;
for(var count = 1; count<=numItems;count++){
var index = count-1;
var searchResult = $(".hide:eq("+index+")").text();
if(val.indexOf(searchResult) > -1){
$(".hide:eq("+index+")").removeClass("hide").addClass("show");
}
else{
$(".hide:eq(" + index + ")").removeClass("show").addClass("hide");
}
}
$('#mytable').DataTable().search(val).draw();
});
I have a MathJax demo that can be viewed at Online Demo.
In this demo, I have some Tex markup within a div that gets rendered perfectly by MathJax.
But, if I programatically add some Tex markup to the above div by clicking Add Math Markup button followed by clicking Rerender Math Markup button, then it results in repeated rendering of previously rendered Math markup. This can be seen in following video: Math being rendered repeatedly
All I am doing when Rerender Math Markup button is clicked is calling the following method MathJax.Hub.Typset(divElement). The divElement is the div to which Tex markup was added programatically.
Demo code for my situation
<script>
function reRenderMath() {
var div = document.getElementById("mathMarkup");
//render Math for newly added Tex markup
MathJax.Hub.Typeset(div);
}
function addMath() {
var div = document.getElementById("mathMarkup");
div.innerHTML = div.innerHTML + "$$\sin^{-1}.6$$";
document.getElementById("btnRenderMath").disabled = false;
}
</script>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({tex2jax: {inlineMath: [['$','$'], ['\\(','\\)']]}});
</script>
<script type="text/javascript"
src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<button type="button" onclick="addMath();return false;" id="btnAddMath" >Add Math Markup</button>
<button type="button" onclick="reRenderMath();return false;" id="btnRenderMath" disabled>Rerender Math Markup</button>
<div id="mathMarkup">
$$x^2 = x +2$$
</div>
Screen shot of repeated rendering
#Sunil thanks for the answer
Summarizing:
Required script:
var MathJaxUtils = (function () {
let obj = {};
let scripts = null;
obj.render = function (element) {
scripts = new Array();
$(element).find("script[id^='MathJax-Element']").each(function () {
scripts.push({
displayElement: $(this).prev("div")[0],
scriptElement: this
});
});
//remove all html within MathJax script tags so it doesn't get typset again when Typeset method is called
$(element).find("script[id^='MathJax-Element']").remove();
//render Math using original MathJax API and in callback re-insert the MathJax script elements in their original positions
MathJax.Hub.Queue(["Typeset", MathJax.Hub, element, typeSetDone]);
};
//callback for Typeset method
function typeSetDone() {
for (var i = 0; i < scripts.length; i++) {
$(scripts[i].displayElement).after(scripts[i].scriptElement);
}
//reset scripts variable
scripts = [];
};
return obj;
}());
Basic use:
let elem = document.getElementById("mathContainer");
MathJaxUtils.render(elem);
Demo:
math-jax-test
How can I add fields dynamically in Jtable. I want to have multiple values for Cities
Please Refer the image attached
Thanks
Yes this is not built-in with jQuery jTable. To deal with this I've created a script for the same purpose. This handles (a) adding more controls OR group of controls and (b) remove control(s).
Here is the script:
//add '.add_more' class to
$(".add_more").on('click', function () {
// creates unique id for each element
var _uniqueid_ = +Math.floor(Math.random() * 1000000);
var new_ele_id = $(this).attr("data-clone-target") + _uniqueid_;
var cloneObj = $("#" + $(this).attr("data-clone-target"))
.clone()
.val('')
.attr("id", new_ele_id);
// if the control is grouped control
if ($(this).hasClass('group_control') == true) {
$($(cloneObj).children()).each(function () {
$(this).attr("id", $(this).attr("id") + _uniqueid_).val("");
});
}
$(cloneObj).insertBefore($(this));
//creates a 'remove' link for each created element or grouped element
$("<a href='javascript:void(0);' class='remove_this' data-target-id='" + new_ele_id + "'></a>")
.on('click', function () {
if ($(this).is(":visible") == true) {
if (confirm("Are you sure?")) {
$("#" + $(this).attr("data-target-id")).remove();
$(this).remove();
}
}
else {
$("#" + $(this).attr("data-target-id")).remove();
$(this).remove();
}
}).insertBefore($(this));
$("#" + new_ele_id).focus();
});
//remove element script
$(".remove_this").on('click', function () {
if ($(this).is(":visible") == true) {
if (confirm("Are you sure?")) {
$("#" + $(this).attr("data-target-id")).remove();
$(this).remove();
}
}
else {
$("#" + $(this).attr("data-target-id")).remove();
$(this).remove();
}
});
Usage: Single Element http://jsfiddle.net/vkscorpion1986/ktbn4qLg/2/
<input class="" id="<ELEMENT-ID>" type="text" name="input1">
Add More
Usage: Grouped Elements http://jsfiddle.net/vkscorpion1986/ktbn4qLg/4/
<div id="<ELEMENT-ID>">
<input class="" id="input1" type="text" name="input1">
<input class="" id="input2" type="text" name="input2">
</div>
Add More
attributes
href = javascript:void(0); // just to disable the anchor tag default behaviour
data-clone-target = id of the target element
css classes
.add_more = to implement the add more/remove controls functionality
.group_control = for indicating that this is group of elements which have to be repeted
Hope this works for you.
No, it's not made with jTable. You can use input option (http://jtable.org/ApiReference#fopt-input) and this: http://jqueryui.com/autocomplete/#multiple Or you can create your own dialog.
I have this fiddle, and can not make this work. I believe that the reason resides in that two li elements with a custom directive edit-in-place share scope.
The solution would be to say to the directive to create a copy of the scope that binds on the parent - can transclude help?
angular.module('bla', [])
.directive('editInPlace', ['$parse','$compile', function($parse, $compile) {
return {
restrict: 'A',
scope: true,
link: function (scope, element, attribs) {
var inputStart = '<input style="border: 2 solid black" name="inPlaceInput" style="display:none" value="';
var inputEnd = '">';
scope.editModeAccessor = $parse(attribs.editInPlace);
scope.modelAccessor = $parse(attribs.ngBind);
scope.$watch(attribs.editInPlace, function(newValue, oldValue){
if (newValue){
console.debug("click");
console.debug("value: " + scope.modelAccessor(scope));
var inputHtml = inputStart + scope.modelAccessor(scope) + inputEnd;
element.after(inputHtml);
jQuery(element).hide();
scope.inputElement = jQuery("input[name=inPlaceInput]");
scope.inputElement.show();
scope.inputElement.focus();
scope.inputElement.bind("blur", function() {
blur();
});
} else {
blur();
}
});
function blur(){
console.debug("blur secondary");
if (scope.inputElement){
console.debug("blur secondary inputElement found");
var value = scope.inputElement.val();
console.debug("input value: "+ value);
scope.inputElement.remove();
jQuery(element).show();
scope.editModeAccessor.assign(scope, false);
scope.modelAccessor.assign(scope, value);
}
}
}
}
}]);
function ContactsCtrl($scope, $timeout){
$scope.contacts = [{number:'+25480989333', name:'sharon'},{number:'+42079872232', name:''}];
$scope.editMode = false;
var editedId;
$scope.edit = function(id){
$scope.editMode = true;
jQuery("#"+id).hide();
editedId = id;
//TODO show delete button
}
$scope.$watch('editMode', function(newValue, oldValue){
if (!newValue && editedId){
jQuery("#"+editedId).show();
}
});
}
<div ng-app="bla">
<div ng-controller="ContactsCtrl">
<h4>Contacts</h4>
<ul>
<li ng-repeat="contact in contacts">
<span edit-in-place="editMode" ng-bind="contact.number"></span>
<span edit-in-place="editMode" ng-bind="contact.name"></span>
<span id="{{$index}}" ng-click="edit($index)"><i class="icon-edit">CLICKtoEDIT</i></span>
</li>
</ul>
</div></div>
I think cloning the scope is not the best solution.
When creating a directive in angular, you should encapsulate all the functionality within the directive. You should also avoid mixing jQuery in when you don't have to. Most of the time (as in this case) you're just introducing unnecessary complexity. Lastly, classes are the best way of controlling display, rather than the style attribute on an element.
I took the liberty of rewriting your directive in a more "angular" way - with no jQuery. As you can see from the updated jsFiddle, it is simpler and cleaner. Also, it works!
This directive can be easily modified to add lots of additional awesome functionality.
app.directive( 'editInPlace', function() {
return {
restrict: 'E',
scope: { value: '=' },
template: '<span ng-click="edit()" ng-bind="value"></span><input ng-model="value"></input>',
link: function ( $scope, element, attrs ) {
// Let's get a reference to the input element, as we'll want to reference it.
var inputElement = angular.element( element.children()[1] );
// This directive should have a set class so we can style it.
element.addClass( 'edit-in-place' );
// Initially, we're not editing.
$scope.editing = false;
// ng-click handler to activate edit-in-place
$scope.edit = function () {
$scope.editing = true;
// We control display through a class on the directive itself. See the CSS.
element.addClass( 'active' );
// And we must focus the element.
// `angular.element()` provides a chainable array, like jQuery so to access a native DOM function,
// we have to reference the first element in the array.
inputElement[0].focus();
};
// When we leave the input, we're done editing.
inputElement.prop( 'onblur', function() {
$scope.editing = false;
element.removeClass( 'active' );
});
}
};
});
I've got an issue where I'm using template.render to render an array of items based on a html template. Each item in the array also contains another array, that I want to bind to another template, within the parent element for the area. I know I can use a grid layout for groups, but I'm trying to accomplish this another way, so please, no suggestions to use a different control, I'm just curious as to why the following doesn't work correctly.
//html templates
<div id="area-template" data-win-control="WinJS.Binding.Template">
<h1 class="area-title" data-win-bind="innerHTML:title"></h1>
<div class="items">
</div>
</div>
<div id="item-template" data-win-control="WinJS.Binding.Template">
<h2 class="item-title" data-win-bind="innerHTML:title"></h2>
</div>
// JS in ready event
var renderer = document.getElementsByTagName('section')[0];
var area_template = document.getElementById('area-template').winControl;
var item_template = document.getElementById('item-template').winControl;
for (var i = 0; i < areas.length; i++) {
var area = areas.getAt(i);
area_template.render(area, renderer).done(function (el) {
var item_renderer = el.querySelector('.items');
for (var j = 0; j < area.items.length; j++) {
var item = area.items[j];
item_template.render(item, item_renderer).done(function (item_el) {
});
}
});
}
So what should happen, is that after it renders the area, in the "done" function the newly created element (el) gets returned, I'm then finding it's ".items" div to append the items to. However, this appends all the items to the first div created. If it was the last div, it might make more sense due to closures, but the fact it happens on the first one is really throwing me off!
What's interesting, is that if I replace my template render function using document.createElement and el.appendChild, it does display correctly e.g: (in the done of area render)
area_template.render(area, renderer).done(function (el) {
var item = area.items[j];
var h2 = document.createElement('h2');
h2.innerText = item.title;
el.appendChild(h2);
}
although I've realised this is el it is appending it to, not the actual .items div of the el
I'm not quite sure what could be going on here. It appears the value of el is getting updated correctly, but el.querySelector is either always returning the wrong ".items" div or it's getting retained somewhere, however debugging does show that el is changing during the loop. Any insight would be greatly appreciated.
thanks
I've worked out what is going on here. The "el" returned in the render promise is not the newly created element as I thought. It's the renderer and the newly created html together. Therefore el.querySelector('.items') is always bringing back the first '.items' it finds. I must have misread the docs, but hopefully someone else will find this information useful in case they have the same error.
I guess one way around this would be to do item_rendered = el.querySelectorAll('.items')[i] and return the numbered '.items' based on the position in the loop
e.g
for (var i = 0; i < areas.length; i++) {
var area = areas.getAt(i);
area_template.render(area, renderer).done(function (el) {
var item_renderer = el.querySelectorAll('.items')[i];
for (var j = 0; j < area.items.length; j++) {
var item = area.items[j];
var h2 = document.createElement('h2');
h2.innerText = item.title;
item_renderer.appendChild(h2);
}
});
}