Removing elements from one to/until another - javascript

Let's have an example:
<table>
<tr class="need"></tr>
<tr class="no-need"></tr> // This is ourElement, needs to be removed
<tr></tr> // This element needs to be removed
<tr class="no-need"></tr> // This element needs to be removed
<tr class="no-need"></tr> // This element needs to be removed
<tr class="need"></tr> // Elements removed until this
</table>
I want to remove those four elements at once.
This is what I've done:
function remove(ourElement) {
var body = ourElement.parentNode,
bodyRows = body.getElementsByTagName('tr');
for (var i = 0; i < bodyRows.length; i++) {
if (bodyRows[i] == ourElement) {
if (!bodyRows[i+1].className) {
body.removeChild(bodyRows[i+1]);
}
}
if (bodyRows[i] > ourElement) {
if (bodyRows[i].className == 'no-need') {
body.removeChild(bodyRows[i]);
}
if (bodyRows[i].className == 'need') {
break;
}
}
}
body.removeChild(ourElement);
}
The function removes only the first empy row after ourElement and the ourElement itself.
As i wrote above, I need to remove those four elements at first run of our function.
Pure Javascript needed.

I just realised you may be looking for a function to delete items inside boundaries lets say:
items between class"need" and class"need" and delete all items inside them. if thats your question the answer is as follows:
function remove( tagElement, boundClass ) {
var tr = document.getElementsByTagName(tagElement),
re = new RegExp("(^|\\s)"+ boundClass +"(\\s|$)"),
bound = false,
r = [];
for( var i=0, len=tr.length; i<len; i++ ) {
if( re.test(tr[i].className) ) {
bound = ( bound === true ) ? false : true;
if(bound) continue;
}
if( bound ) r.push( tr[i] );
}
while( r.length )
r[ r.length - 1 ].parentNode.removeChild( r.pop() );
}
remove( "tr", "need" ); // use it like this

you need something like this:
function remove(ourElement) {
var body = ourElement.parentNode;
var childRows = body.childNodes;
var found = false;
for (var i = 0; i < childRows.length; i++) {
var row = childRows[i];
if(found) {
if(!row.className || row.className == "no-need") {
body.removeChild(row);
i--; // as the number of element is changed
} else if(row.className == "need") {
break;
}
}
if(row == ourElement) {
body.removeChild(ourElement);
found = true;
i--; // as the number of element is changed
}
}
}

You cannot use the < or > operators with DOM elements.
function remove(ourElement) {
var body = ourElement.parentNode,
bodyRows = body.getElementsByTagName('tr'),
lb = false;
for (var i = 0; i < bodyRows.length; i++) {
lb = (lb)?(bodyRows[i] == ourElement):lb;
if(lb){
if (!bodyRows[i].className) {
body.removeChild(bodyRows[i]);
}else if (bodyRows[i].className == 'no-need') {
body.removeChild(bodyRows[i]);
}else if (bodyRows[i].className == 'need') {
break;
}
}
}
}

Try this, every time it removes a child it decreases i to compensate:
function remove(ourElement) {
var body = ourElement.parentNode,
bodyRows = body.getElementsByTagName('tr'),
lb = false;
for (var i = 0; i < bodyRows.length; i++) {
if (!lb && bodyRows[i] != ourElement) {
continue;
} else if(bodyRows[i] == ourElement){
lb = true;
}
if (bodyRows[i].className == 'no-need' || !bodyRows[i].className) {
body.removeChild(bodyRows[i]);
i--;
}
}
}

Related

how to prevent filter to make empty array?

function addSelected(id) {
var feedFound = false;
var selList = [] ;
for (var i = 0; i < vm.feeds.length; i++) {
if (vm.feeds[i].id == id) {
if (vm.rationList.length > 0) {
for (var j = 0; j < vm.rationList.length; j++) {
if (vm.feeds[i].id == vm.rationList[j].id) {
feedFound = true;
break;
}
}
}
if (!feedFound) {
selList.push(vm.feeds[i]);
vm.feeds[i] = vm.feeds.filter(function(item) {
return item.id === vm.feeds[i].id;
});
}
feedFound = false;
}
}
var li = [];
angular.copy(selList, li);
for (var i = 0; i < li.length; i++) {
vm.rationList.push(li[i]);
}
vm.rationListSafe = vm.rationList;
}
This is how i add elements from one list to another with filtering. The problem is, for each filtered element, I get back an empty array. Is there anyway I can solve this?
If you are looking for only one item in your array, use find() instead of filter()
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
PS: find may return null so you should always null-check
Example:
const foundItem = vm.feeds.find(function(item) {
return item.id === vm.feeds[i].id;
});
if (foundItem) {
vm.feeds[i] = foundItem
}

Return DOM elements based on array

As a challenge, I am trying to create a JavaScript selection engine i.e. a JavaScript function that will return DOM elements given a CSS selector.
I cant use document.querySelector/document.querySelectorAll.
I am currently creating a object of the parameter, but am now stuck. I now need to loop through every element on the page, and if it matches my tag, or class/id, push that element to an array.
$("div") //Should return 2 DIVs
$("img.some_class") //Should return 1 IMG
$("#some_id") //Should return 1 DIV
$(".some_class") //Should return 1 DIV and 1 IMG
function $ (selector) {
var elements =[];
var pageTags =[];
var all = document.getElementsByTagName("*");
//splits selector
var arg = parse(selector);
function parse(subselector) {
var obj = {tags:[], classes:[], ids:[], attrs:[]};
subselector.split(/(?=\.)|(?=#)|(?=\[)/).forEach(function(token){
switch (token[0]) {
case '#':
obj.ids.push(token.slice(1));
break;
case '.':
obj.classes.push(token.slice(1));
break;
case '[':
obj.attrs.push(token.slice(1,-1).split('='));
break;
default :
obj.tags.push(token);
break;
}
});
return obj;
}
console.log(arg);
for (var item of all) {
//gets tagname of all page elements
var element = item.tagName.toLowerCase();
console.log(element);
//if argument contains DOM element
if (arg.indexOf(element) !== -1) {
var x = document.getElementsByTagName(element);
for (var test of x) {
elements.push(test);
}
}
}
return elements;
}
<html>
<head>
<script src="Answer.js"></script>
<script src="Test.js"></script>
</head>
<body onload="test$()">
<div></div>
<div id="some_id" class="some_class some_other_class"></div>
<img id="some_other_id" class="some_class some_other_class"></img>
<input type="text">
</body>
</html>
Please any help on how to do this will be appreciated.
check this jsfiddle.
There would be many many more combinations of course ...
I limited the test cases to the html example you provided.
function _select(attrValues, tagFilter, cssSel) {
var results = [];
//var value = selector.slice(1);
var all = document.getElementsByTagName(tagFilter);
//look for an id attribute
if (cssSel === '#') {
for (var i = 0; i < all.length; i++) {
if (all[i].id === attrValues) {
results.push(all[i]);
}
}
} else {
if (typeof attrValues === 'string') {
for (var i = 0; i < all.length; i++) {
if (all[i].classList.contains(attrValues)) {
results.push(all[i]);
}
}
} else {
//multiple selector classes
var found = 0;
for (var i = 0; i < all.length; i++) {
for (var j = 0; j < attrValues.length; j++) {
if (all[i].classList.contains(attrValues[j])) {
found += 1;
if (found === attrValues.length) {
results.push(all[i]);
}
}
}
}
}
}
return results;
}
function $(selector) {
var cssSel = selector.charAt(0);
var cssSelectors = ['.', '#'];
if (cssSel === cssSelectors[0] || cssSel === cssSelectors[1]) {
//direct selector
var attrValue = selector.slice(1),
tagFilter = '*';
return _select(attrValue, tagFilter, cssSel)
} else {
for (var i = 0; i < cssSelectors.length; i++) {
var tokens = selector.split(cssSelectors[i]);
if (tokens.length > 1 && tokens[0] !== "") {
//nested selector
var tagFilter = tokens[0], //the first of the array should be the tagname ,because the case of the cssSelector at charAt(0) should have been caught in the if at the beginning.
attrValue = tokens.slice(1); //the rest of the array are selector values
return _select(attrValue, tagFilter, cssSel)
}
}
}
return document.getElementsByTagName(selector);
}
//TEST cases
var results = $("div")
console.log('Should return 2 DIVs')
for ( var e of results){
console.log(e)
}
var results = $(".some_class")
console.log('Should return 1 DIV and 1 IMG')
for ( var e of results){
console.log(e)
}
var results = $("#some_id")
console.log('Should return 1 DIV ')
for ( var e of results){
console.log(e)
}
var results = $("img.some_class")
console.log('Should return 1 IMG')
for ( var e of results){
console.log(e)
}
var results = $("div.some_class.some_other_class")
console.log('Should return 1 div')
for ( var e of results){
console.log(e)
}

I want to remove redundant code and move in to a separate Jquery function

Here is the code I am trying to remove the redundant code and move the code to separate function.
//Adding Infotypes to filter and checks whether any infotype option is selected
if(this.$infoOptions.val() != null){
var infotypelength = this.$infoOptions.val().length;
var l=0,j;
//Condition to check empty input type
if( infotypelength > 0){
var infotypeList = this.$infoOptions.val();
for(j = 0; j < infotypelength; j++) {
//It checks whether already option is selected and prevents adding to filter if its duplicate.
if(($.inArray( $('#infoOptions').select2('data')[j].text, filterList)) == -1 ){
this.filter.push($('#infoOptions').select2('data')[j].text);
if(infotypeList[j].contains('_subgroup')){
var res = infotypeList[j].split("_");
this.aSubinfotype[l]=res[0];
l=l+1;
}
else
this.aInfotypes.push(infotypeList[j]);
}
}
}
}
//Adding Countries to filter
if(this.$countryOptions.val() != null){
var geoLength = this.$countryOptions.val().length;
//Condition to check empty input type
if( geoLength > 0){
var geoList = this.$countryOptions.val();
var l=0;
for(var j = 0; j < geoLength; j++) {
if(($.inArray( $('#countryOptions').select2('data')[j].text, filterList)) == -1 ){
this.filter.push($('#countryOptions').select2('data')[j].text);
if(geoList[j].contains('_subgroup')){
var res = geoList[j].split("_");
this.aSubgeotype[l]=res[0];
l=l+1;
}
else
this.aGeography.push(geoList[j]);
}
}
}
}
But I am facing problem in passing the variable and cached selectors in to other function. Can anyone help me with this?
I don't know how is done your implementation but I really think that you can improve it, by the way, you can reduce your code in two way bit different :
var myFunction = function(option, filter, array, selector, subType) {
if(option && option.val()){
var optList = option.val();
var optLength = optList.length;
//Condition to check empty input type
if( optLength > 0) {
var l = 0;
for(var j = 0; j < optLength; j++) {
if( ($.inArray( selector.select2('data')[j].text, filterList)) == -1 ){
filter.push(selector.select2('data')[j].text);
if(optList[j].contains('_subgroup')){
var res = optList[j].split("_");
subType[l]=res[0];
l=l+1;
} else {
array.push(optList[j]);
}
}
}
}
}
}
call : myFunction(this.$countryOptions, this.filter, this.aGeography, $('#countryOptions'), this.aSubgeotype)
// data = {option, filter, array, selector, subType}
var myFunction = function(data) {
if(data.option && data.option.val()){
var optList = data.option.val();
var optLength = optList.length;
//Condition to check empty input type
if( optLength > 0) {
var l = 0;
for(var j = 0; j < optLength; j++) {
if( ($.inArray( data.selector.select2('data')[j].text, filterList)) == -1 ){
data.filter.push(data.selector.select2('data')[j].text);
if(optList[j].contains('_subgroup')){
var res = optList[j].split("_");
data.subType[l]=res[0];
l=l+1;
} else {
data.array.push(optList[j]);
}
}
}
}
}
}
call :
myFunction({
option: this.$countryOptions,
filter: this.filter,
array: this.aGeography,
selector: $('#countryOptions'),
subType: this.aSubgeotype
});
or
var data = {
option: this.$countryOptions,
filter: this.filter,
array: this.aGeography,
selector: $('#countryOptions'),
subType: this.aSubgeotype
}
myFunction(data);
The first way is to pass your data one by one, the second you pass your data into an json object.

Convert jquery into Angularjs

I have the following jquery method:
$('.niGridTable table tr').addClass('selected').end().click(function (event) {
event = event || window.event;
var isClassExist = false;
var closesedTable = $(event.target).closest('tr').find('.selected_row');
if (closesedTable.length > 0) {
isClassExist = true;
if (event.ctrlKey) {
for (var i = 0; i < closesedTable.length; i++) {
if ($(closesedTable[i]).hasClass('selected_row')) {
$(closesedTable[i]).removeClass('selected_row');
}
}
}
}
if (!event.ctrlKey) {
if ($('td').hasClass('selected_row')) {
$('td').removeClass('selected_row');
}
}
if (!isClassExist) {
$('.table-striped > tbody > tr:hover > td').addClass('selected_row');
}
});
I want to write such code as angular way.like...
element.on('click', function (event) {
}
In my directive I have changed my question said jquery to Angularjs. In this case I have use angular.element.
$timeout(function () {
//get all row for set selected row class
var trs = iElement.find('tr');
for (var index = 0; index < trs.length; index++) {
var tableTr = angular.element(trs[index]);
//remove prvious click event
tableTr.unbind('click');
tableTr.bind('click', function (event) {
event = event || window.event;
event.stopPropagation();
event.preventDefault();
//if target row contain previos selection then remove such selection
var isClassExist = false;
var targetTd = angular.element(event.target);
var targetTr = targetTd.parent();
if (targetTr.hasClass('selected_row')) {
targetTr.removeClass('selected_row');
isClassExist = true;
}
//if another row contain selection then remove their selection but if control button pressed then it will not work
var closesedTable = iElement.find('tr');
if (closesedTable.length > 0) {
if (!event.ctrlKey) {
for (var i = 0; i < closesedTable.length; i++) {
var eachRow = angular.element(closesedTable[i]);
if (eachRow.hasClass('selected_row')) {
eachRow.removeClass('selected_row');
}
}
}
}
//set selection
if (!isClassExist) {
targetTr.addClass('selected_row');
}
////get selected rows
//for (var j = 0; j < trs.length; j++) {
// gridOption['selectedRow'].push();
//}
});
}
}, 0);

Set value jquery work in trace but doesn't work without trace

I have set filter in Kendo grid but i have a problem when filter applied to grid, i missed value of my filter row.
After filter i missed my filter :
Now for this reason, i set my filter row again so bellow code :
function updateSearchFilters(grid, field, operator, value)
{
var newFilter = { field: field, operator: operator, value: value };
var dataSource = grid.dataSource;
var filters = null;
if ( dataSource.filter() != null)
{
filters = dataSource.filter().filters;
}
if ( filters == null )
{
filters = [newFilter];
}
else
{
var isNew = true;
var index = 0;
for(index=0; index < filters.length; index++)
{
if (filters[index].field == field)
{
isNew = false;
break;
}
}
if ( isNew)
{
filters.push(newFilter);
}
else
{
//alert(value);
if(value == '')
filters.splice(index,1);
//delete filters[index];
else
filters[index] = newFilter;
}
}
dataSource.filter(filters);
for (var i = 0; i < filters.length; i++) {
$('#gridId-filter-column-' + filters[i].field.toString()).val(filters[i].value.toString());
}
}
When i set the break point in this line $('#gridId-filter-column-' + filters[i].field.toString()).val(filters[i].value.toString()); it worked correct but
when i remove break point this line doesn't work.
you can set delay before run this line :
for (var i = 0; i < filters.length; i++) { $('#gridId-filter-column-' +filters[i].field.toString()).val(filters[i].value.toString()); }

Categories