Jquery find and append performance issue when building big hierarchy - javascript

I need to recursively read a hierarchic JSON object and build the HTML the same way as children objects will have to be inserted inside parents object.
Example:
<div class="level0">
<div class="children1 level1">
<div class="children1 level2">
.... N children
</div>
... N Children
</div>
</div>
... N Parents
Now, I'm building this with the following code:
function buildDivHtml(); // This generates all the parent/children code at that particular moment, meaning one by one.
object.find('selector of the row where the append goes').append('html from above function');
This causes browser to freeze. Is there a way to do this that works flawlessly without browser freezing?

Related

Vue - How to toggle an item's child across a list of DOM items?

I have the following DOM structure:
<item>
<div>
show details
</div>
<div class="details">...</div>
</item>
<item></item>
...
And I want to toggle the details div whenever a is clicked
What's the best way to achieve this in Vue.js ?
Given the fact that you have control over the rendering on the PHP side, I'd say utilizing a simple $index variable from your loop to hold access to a list of ID's would be simple enough. If you can pass $index from the php method to the component, then you can utilize the below:
<Item>
<div>
<a #click="$set(details, $index, !details[$index])">show details</a>
</div>
<div class="details" v-show="details[$index]">...</div>
</Item>
Then hold reference to details in your component's data definition:
data () {
return {
//...
details: {}
}
}
Note the usage of $set in the above allows us to creative a reactive property in our details object. Utilizing this approach means you are not required to define each index property on the details object.
Here's a simple jsFiddle which should help give you a more visual idea of the approach.

loading large array in oi-select takes too much of time in angularjs

I am using oi-select library, i have customized it according to my project need and i have written directive for it. The problem is its taking too much of time say 10secs to load around 3k array data. I want to minimize the time for it. Here I have created plunker for this.
I have directive which loads all factory data and provides it to oi-select, in my code here is html
<small><b>select {{MPhardwaresListDropDown.length}}</b></small>
<div style="padding-top: 7px">
<div title="" class="selected-multiple-items">
{{MPselectedHardwares.length}} selected
</div>
<grid-multi-select id="hardwareId" clean="clean" optionlist="MPhardwaresListDropDown" colval="name"></grid-multi-select>
</div>
HTML code in directive looks like
<div>
<div ng-repeat="optionVal in tempOptionList | orderBy : ['-originalcheck','+label']" prevent-close>
<div ng-if="optionVal.label">
<label class="checkbox" ng-if="!typeFilterOptions || (optionVal.label.toString().toLowerCase().indexOf(typeFilterOptions.toLowerCase()) > -1)">
<input type="checkbox" name="checkbox1" ng-checked="optionVal.check" ng-model="optionVal.check"/>
<span class="checkbox__input"></span>
<span class="checkbox__label" style="color:#A9A9A9;">{{optionVal.label}}</span>
</label>
</div>
<div ng-if="!optionVal.label">
<label class="checkbox" ng-if="!typeFilterOptions || (optionVal.val.toString().toLowerCase().indexOf(typeFilterOptions.toLowerCase()) > -1)">
<input type="checkbox" name="checkbox1" ng-checked="optionVal.check" ng-model="optionVal.check" ng-change="checking(typeFilterOptions)"/>
<span class="checkbox__input"></span>
<span class="checkbox__label" style="color:#A9A9A9;">{{optionVal.val}}</span>
</label>
</div>
</div>
angular code is too big to mention in this question please refer plunker, but this is how it loops
scope.selection = scope.selectionorg;
scope.checkedall=scope.checkedallorg;
scope.OptionList=parentScope[scope.parentListName].sort();
scope.tempOptionList=[];
var i=0;
for(i=0;i<scope.OptionList.length;i++) {
scope.tempOptionList[i] = {val: scope.OptionList[i], check: false, originalcheck: false};
}
if(scope.optionListSelectedList.length>0) {
angular.forEach(scope.optionListSelectedList, function(obj){
angular.forEach(scope.tempOptionList, function(obj1){
if(obj===obj1.val){
obj1.check=true;
obj1.originalcheck=true;
}
});
});
}
else{
scope.checkedall=false;
}
};
I want something like which will load partial data on scroll it loads more data, any help will be appreciated. Thank you so much.
EDIT
Now i have edited my plunker with limitTo in ng-repeat, for that i have written new directive which will trigger addmoreitems function when scroll will reach bottom. updatedPlunker
Now problem is when i am typing and searching something its searching in only available records with respect to limitTo its not searching in all data, say now the limitTo is 50 then search is happening only in 50 records not in 3k records.
Correct way of doing this kind of requirement is to do with pagination, since you are loading data from the server side, you should make your api to support pagination.
It should accept three parameters such as number of items, start, limit and you should initially get the number of items and repeat it until you get the whole result set.
There should be another request to get the total number of items. By doing this you can retrieve all the elements at once loaded in the client side. Until that you should have a loading indicator, or you could load this data when the login process/application starts.
limitTo will not be able to help with the search , because you are limiting the results.
The problem is not the array, the browser can easly handle that, the problem is that you're rendering all the 3k DOM elements, that's really heavy work even for an actual machine, also since there is bindings in each dom element {{}} they're being watching by AngularJs, I got the same problem and solved using Virtual Repeat from AngularJS Material, what this does is it doesn't render the whole 3k DOM elements generated by the ng-repeat, instead it just renders the ones that are visible, also I've found another library if you don't want to use Angular Material, this seems to work the same way: Angular VS-Repeat
You may try the limitTo filter in ng-repeat in angularjs which takes the additional argument to start the iteration.
https://docs.angularjs.org/api/ng/filter/limitTo
On scroll, you can then change that argument based on the number of items pending or left for rendering or the number of items already rendered. This should help you in approach of selective loading of data on scroll.

Create html elements in ngRepeat depending on object properties

I have an array of objects that have two fileds for example :
var array = [{type:"range", group:"group1"}, {type:"boolean", group:"group1"},
{type:"input", group:"group3"}... ]
Now in HTML by iterating through the array I want to create a div for every different GROUP and put the all elements with that group inside. But by putting I mean creating Inputs or Radiobuttons or Dropdowns depending on the TYPE of the element.
Now I have done this using AngularJS and mostly JS by appending objects to existing ones and so on. But I want to reduce as much as possible the usage of js because I am having issue with calling a function after the HTML is loaded (Call function after HTML is loaded AngularJS).
So I will be thankfull if someone give me an advice how this should look like mostly in html. I imagine something like this :
<div ng-repeat="object in array track by $index">
//if object.group div exists add to existing one (check maybe with js function ?)
// if object.type is ... add ...
// else if objec.type is ... add ...
...
//else object.group not exists create div with id object.group for example
// if object.type is ... add ...
// else if object.type is ... add ...
...
This should work for your case:
<div ng-repeat="(key, value) in array | groupBy: 'group'">
Group: {{key}}
<div ng-repeat="object in value">
<div ng-switch on="object.type">
<div ng-switch-when="range">Range block</div>
<div ng-switch-when="boolean">Boolean block</div>
<div ng-switch-when="input">Input block</div>
</div>
</div>
<br>
</div>
First looping groups elements, second will work with an elements in a group

JQuery accessing text deep below unique id

I'm trying to make an app that pulls our data from our existing website. As we have no known API, I have decided to use ajax with whateverorigin.org to pull the full HTML from our site, and parse it with jQuery from there.
Unfortunately, much of the data I need lies far below any unique ID's. With $(data.contents).find("#unique").text() I am able to retrieve data of the type:
<div id="unique">
<div>
Text I want
</div>
</div>
However, as the nesting gets deeper, like:
<span id="unique2">
<table>
<tbody>
<tr>
<td>
Text I want
that snippet always returns "".
I have tried chaining the levels together with .join, but that doesn't work either.
Code in context here, around line 40-50. The site I'm trying to access is wmbr.org. The id that works is #now_on_the_air, one of the id's that doesn't is #recent_plays. The log is empty.

JQuery: elegant way to replace set of elements with another set?

I have a bunch of DOM like
<div>
<div class="stuff"/>
<div class="stuff"/>
<div class="stuff"/>
</div>
and I want to replace it with a new set of stuff
<div>
<div class="stuff"/>
<p class="stuff"/>
<ul class="stuff"/>
<a class="stuff"/>
</div>
Which will be fetched via Ajax. My question is: what is the best way to do this?
$.replaceWith doesn't quite do what I want, because I then end up with multiple copies of the new stuff.
I can guarantee that all the stuff will be in one contiguous block, and so presumably I could put in some placeholder after the last element (or before the first element) of the old stuff, remove the old stuff, and replace the placeholder with the new stuff.
However, this seems rather roundabout and inelegant. Is there any clever way of, removing all the old stuff and putting in a single copy of the new stuff, all at one go?
EDIT: I would also like to do this without using any container divs. Using container divs would work in the above case, but would fail in some cases, like when the stuff is inside a <table>:
<table>
<head/>
<body>
<tr/>
<tr class="stuff"/>
<tr class="stuff"/>
<tr class="stuff"/>
<tr/>
</body>
</table>
If i want to replace the rows labelled stuff with another set of rows, possibly more, possibly fewer, there is no way I can nicely put them in a container thingy without breaking the HTML, since the <body> can only contain <tr>s (IIRC).
$('#outerdiv').empty().append(newContent);
Unlike .html(), this will work regardless of whether newContent is an HTML string, or an existing DOM structure.
If there are multiple elements to be replaced but where you need to retain their siblings, you can do this:
$('.stuff').first().before(newContent).end().remove();
i.e. take the first .stuff element, add the new content before it, and then remove all the .stuff elements.
Yes: $('#tagetDiv').html(newContent)
One way to do it would be with wrapAll:
$('.stuff').wrapAll('<div/>').parent().replaceWith('<div class="stuff"/>');
I'm not sure if that passes the "elegant" test, but it does work regardless of whether there is any other content in the containing element.
With that said, though, this seems to be a very complicated solution to a simple problem. The simple solution would be to wrap your elements in a containing element; this shouldn't be a problem if, as you say, you can guarantee that they will always be together.

Categories