JQuery selector, partial DOM searching for performances - javascript

I'm actually working with Jquery and at some point I use Jquery selectors to make my page work. The issue here is that the HTML I work with can get very long depending on the data I work with and it looks like this.
HTML
<div class="mailing"></div>
<input type="text" class="mail_subject"/>
<input type="text" class="mail_body"/> <!-- I can have 1 to n number of these -->
<!-- Preview tags -->
<p class='main_subject'></p>
<p class='main_body'></p>
<!--
And a few more things we don't use here
-->
</div>
<div id="table1">
<table id="ranking">
<tbody>
<!-- Data, can have 0 to ~3500 rows -->
</tbody>
</table>
</div>
As you can see, my page is more or less divided in two parts, the <div class="mailing">, which contains a few forms, and the <div id="table1"> that is about displaying lots of data.
In my mailing div I have a few inputs and an auto-updated preview that takes the data from the inputs. What I have here is a kind of "mail builder" with the preview giving me the result with html formatting.
The problem here is about performance, my JQuery is slowed by the table and I got lag when I type in a form and I don't want it to search the whole document as I already know my data will be in the mailing div.
JS
$('.mailing').on('change click keyup keydown', function () {
// Here I append the mail_subject input to the preview
var text = $(this).val();
$('.main_subject').text($('.subject_select').val());
// Here I append each mail_body input to the preview
$('.bodies_select').each(function () {
text = $(this).val();
/*
* Some computation for the text
*/
jQuery('<span/>', {text: text}).appendTo('.main_body');
});
});
I have a few more functions like theses and a few more computation, but I think we got the idea of what my code looks like.
My question is, is there a way, when I use JQuery selectors like $('.main_subject') or $('.bodies_select') to not search the whole DOM document but only in my mailing div for example? The problem is that I can store my elements in variable since it as multiple occasion to be updated.

You can use context with jQuery to improve performances :
$('.bodies_select', '.mailing')
http://api.jquery.com/jquery/#jQuery1
You can even optimize the selectors with some technics :
https://learn.jquery.com/performance/optimize-selectors/

Sure, you just need to place the parent elemenent before
$('.mailing .main_subject')
You should probably read a bit about selectors
https://api.jquery.com/category/selectors/

Related

how to store content that is displayed dynamically

I was wondering what is the proper way of store in html content that is displayed dynamically.
My case is that depending on what is clicked some sort of text is displayed in another part of a website. My first idea was to create a variable in js/jquery script to store this content so the script can access it whenever it is necessary.
this is an example:
var someContent=" Content to be displayed when something is clicked";
var a=$('#myid');
a.click(function(){
$('#myOtherId').text(someContent);
});
But after a while it came to my mind that the content should be stored in the html with a display value set to 'none' and js script should simple toggle its visibility depending wether it has been clicked or not.
Storing the content in js script seems much easier - but something tells me that there is better way to do it...
Indeed, you can do it by having the text in an HTML element, and use either jQuery's .toggle or .show depending on how you want it to respond to a second click:
$("#toggle").click(function () {
$('#msg').toggle(); // or .show() to have it in one way only
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span id="msg" style="display:none">Content to be displayed when something is clicked</span>
<br>
<button id="toggle">click me</button>
When the content is static, it seems more natural to have it embedded in your HTML, while when the content is dynamic (i.e. it depends on the actual state of the application), you would inject the dynamic part of the text with JavaScript.
For a non-JS-programmer in the development team (but with knowledge of HTML) it would in theory be easier to manipulate the messages when they are embedded in the HTML part of the page.
But this is a matter of opinion really.
Here is a mix of the two:
$("#enter").click(function () {
// Copy the number into the error message
$('#num').text($("#inp").val());
// Show the error message when the number is not even
$('#msg').toggle($('#inp').val() % 2 !== 0);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Enter an even number: <input id="inp" type="number" value=1>
<button id="enter">Verify</button>
<div id="msg" style="display:none; color:red">
The number <span id="num"></span> is not even
</div>

jQuery Within a PHP While Loop

I am using jQuery to reveal an extra area of a page when a button is clicked.
The script is
$(document).ready(function() {
$("#prices").on('click', 'a.click', function() {
$(".hiddenstuff").slideToggle(1000),
$("a.click").toggleClass("faded");
});
});
Then the button is
Enquire or Book
and the newly revealed area is
<div class="hiddenstuff" style="display:none">
<!-- HTML form in here -->
</div>
The problem I have is that the button and "hiddenstuff" div are wrapped in a PHP while loop so they repeat anything between one and six times. When the user clicks on one of the buttons, all the hidden divs are revealed. I would like just the hidden div related to the clicked button to reveal.
I presume that I have to create a javascript variable that increments in the while loop and somehow build that into the script. But I just can't see how to get it working.
EDIT, in response to the comments
The while loop is actually a do-while loop. The code inside the loop is about 200 lines of PHP and HTML. That's why I didn't show it all in my question. In a shortened version, but not as shortened as before, it is
do {
<!-- HTML table in here -->
Enquire or Book
<!-- HTML table in here -->
<div class="hiddenstuff" style="display:none">
<!-- HTML form and table in here -->
</div>
<!-- More HTML in here -->
} while ($row_season = mysql_fetch_assoc($season));
EDIT 2
The final solution was exactly as in UPDATE2 in the reply below.
The easiest thing for you to do is to keep your onclick binding but change your hiddenstuff select. Rather than grabbing all the hiddenstuffs which you are doing now, you can search for the next one [the element directly after the specific button that was clicked].
$(this).next('div.hiddenstuff').slideToggle(1000);
UPDATE
i created a fiddle for you with what I would assume would be similar to the output from your php loop. one change from my early answer was rather than using next(), i put a div around each group as I would assume you would have and used .parent().find()
http://jsfiddle.net/wnewby/B25TE/
UPDATE 2: using IDs
Seeing your PHP loop and your nested tables and potentially complex html structure, I no longer thing jquery select by proximity is a good idea [be it by big parent() chains or sibling finds].
So I think this is a case for injecting your ids. I assume your table structure has an id that you can get from $row_season ( $row_season["id"] )
you can then place it in the anchor:
Enquire or Book
and the same for your hiddenstuff
<div class="hiddenstuff" data-rowid=" . $row_season['id'] . " style="display:none">
and then your js can find it easily
$(document).ready(function() {
$("#prices").on('click', 'a.click', function() {
var rowid = $(this).attr("data-rowid");
$(".hiddenstuff[data-rowid='" + rowid + "']").slideToggle(1000),
$(this).toggleClass("faded");
});
});
updated fiddle
If your structure is something like this:
<div class="container">
Enquire or Book
<div class="hiddenstuff" style="display:none">
<!-- HTML form in here -->
</div>
</div>
You can do your js like this:
$(document).ready(function() {
$("#prices").on('click', 'a.click', function() {
$(this).siblings(".hiddenstuff").slideToggle(1000),
$(this).toggleClass("faded");
});
});
which is similar to William Newby answer, but a close look at your while loop, I'd think you could do this:
$(document).ready(function() {
$("#prices").on('click', 'a.click', function() {
var index = $(this).index();
$(".hiddenstuff")[index].slideToggle(1000),
$(this).toggleClass("faded");
});
});
There are several ways of do it, I hope I was useful.

search html div and display results on different page

I want to search multiple HTML files from a separate page, where I search for text from all the divs which has a specific id for each, whole id containing matched search term will be displayed on the search page in list.
The div list looks like this :
<body>
<div class='vs'>
<div id='header 1'>content 1 here </div>
<div id='header 2'>another text </div>
<div id='header 3'>whatever </div>
</div>
</body>
Please note that I want to perform search from different page and want to display results there with links to the searchable page.
For now I was searching like this :
HTML
<body>
<input type="text" id='search' />
<div class='vs'>
<div id='header 1'>content 1 here </div>
<div id='header 2'>another text </div>
<div id='header 3'>whatever </div>
</div>
</body>
JavaScript
$('#search').on('input', function () {
var text = $(this).val();
$('.vs div').show();
$('.vs div:not(:contains(' + text + '))').hide();
});
It is working on the fiddle here, but I don't want it to work like this, I want to do the search from a separate page remotely and display results there with link to this page.
Solution with jQuery and AJAX:
<form id="searchForm">
<input type="text" id="search"/>
<input type="submit" name="Search!" />
</form>
<div id="resultContainer">
</div>
<script type="text/javascript">
$("#searchForm").submit(function(e) {
e.preventDefault();
var results = $("#resultContainer");
var text = $("#search").val();
results.empty();
$.get("http://example.com/", function(data) {
results.append($(data).find("div:contains(" + text + ")"));
});
});
</script>
Fiddle (This fiddle enables you to search for content on the jsfiddle page, try for example JSFiddle as search term.)
Note however that this does not work cross-domain, because browsers will prevent cross-site scripting. You didn't describe your use-case clear enough for me to know whether you're okay with that.
You'll want to look at using PHP file_get_contents to retrieve the HTML contents of the external page, and from there analyze the data in the <div>s that you are interested in. Ultimately, you'll want to store each individual search term in a JavaScript array (you can create JavaScript arrays dynamically using PHP), and then create search functionality similar to example you posted to search all the elements in your array.
So on page load, you'll want to have a <div> in which you are going to list all the elements from the array. You can list these by looping through the array and displaying each individual element. From there, you will want to call a function every time the user enters or deletes a character in the <input> box. This function will update the <div> with an updated list of elements that match the string in the <input> box.
This is the theory behind what you are trying to accomplish. Hopefully it will give you some direction as to how to write your code.
Update:
If you're looking for a JavaScript only solution, check out a JavaScript equivalent of PHP's file_get_contents: http://phpjs.org/functions/file_get_contents/
From here, you can maybe look at using .split to break up the list. Ultimately, you're still trying to store each individual search term as an element in an array, it's just the method that you retrieve these terms is different (JavaScript as opposed to PHP).
Perhaps I was emphasizing too much on PHP, perhaps it's because it's the web development language I'm most familiar with. Hope this JavaScript-only solution is helpful.

Is it possible to position a div on top of another starting from the bottom using CSS?

I have a chat window made using HTML, CSS & JS. I want to position the message from bottom to top.
Example:
first message div at the bottom, 2nd message div on top of first an so on. Is it possible?
I can't imagine a pure CSS solution. But Using jQuery, if you already have this library for your project, you could write something this:
$(':button').click(function() {
var message = $('input[type=text]').val();
$('#chat').prepend('<div class="line">'+message);
});
Demo: http://jsfiddle.net/Vbd67/1/
**I changed the append to prepend according to the comment
I know that this is not an answer to your question, rather this is a better option that you should consider implementing in the chat window.
Newest comment should be at the bottom, that is how most basic chat windows work.
Next thing, you can do this all using css:
because such a design requires either use of table rows or list elements
Obviously you have to use javascript for using ajax, so that you can asynchronously fetch user records like messages and profile pic etc
CSS:
<style type="text/css">
table#chat_window {
}
tr#message_row {
}
td#profile_pic {
}
td#message {
}
</style>
HTML STRUCTURE:
<table id="chat_window">
<tr id="message_row">
<td id="profile_pic"></td>
<td id="message"></td>
</tr>
</table>
OR USING LISTS:
<ul id="chat_window">
<li id="message_row">
<div id="profile_pic"></div>
<div id="message"></div>
</li>
</ul>
Now you have to just using javascript:ajax to fetch values and add a child:
If you are using table based chat-window, then you have to fetch the table id using javascript and add row element <tr> per value/message that you fetch.
If you are using list based chat-window, then you have to fetch the list id using javascript and add list element <li> per value/message that you fetch.
I am sorry if there are any typos I have less time.
You can do this using jQuery like;
var first = $('div > div:first-child');
first.appendTo(first.parent());
To deal with several elements, you can do this:
$('div > div').each(function() {
$(this).prependTo(this.parentNode);
});
Here is a working Live Demo.
You should use a combination of display:table-cell and vertical-align:bottom.
I use Sotiris's fiddle and fixed its CSS.
The HTML is
<div style="display:table; height:200px;">
<div style="display:table-row">
<div id="chat" style="width:400px; border:1px solid black;display:table-cell; vertical-align:bottom">
</div>
</div>
</div>
<input type="text"><input type="button" value="post">
​
And this is the JavaScript code
$(':button').click(function() {
var message = $('input[type=text]').val();
$('#chat').append( $('<div/>').text(message) );
});
​
Please let me know if there is a problem.
This is the fiddle
I kept searching for a better solution because table layout is always suspicious to me, but I found css-tricks article about it and they do the same as I do.. so I guess this solution is the right one.
ADDING - keep scroll to bottom code
Since new messages are coming at the bottom, we need to keep scroll to bottom.
I updated the fiddle link

Why are edit in place forms rendered together with the display version instead of being rendered on the fly?

Is there a specific reason that most everyone implements edit-in-place as a shown 'display' div and a hidden 'edit' div that are toggled on and off when somebody clicks on the associated 'edit' button like so?
<div id="title">
<div class="display">
<h1>
My Title
</h1>
</div>
<div class="edit">
<input type="text" value="My Title" />
<span class="save_edit_button"></span>
Cancel
</div>
</div>
Everywhere I look, I see edit-in-place basically handled like this. This approach certainly makes sense when you are rendering all views on the server side and delivering them to the client. However, with pure AJAX apps and frameworks like backbone.js, it seems that we could make our code much more DRY by rendering edit-in-place form elements on the fly as necessary, possibly even making a factory method that determines which form element to render. e.g.
an H1 element with class "title" is replaced by <input type="text" />
a span with class "year_founded" is replaced by <input type="number" min="1900" max="2050" />
a span with class "price" is replaced by an input with the appropriate mask to only allow prices to be input.
Is this practice of rendering all edit-in-place form elements a historical legacy leftover from when pages were rendered on the server-side?
Given the flexibility and power we have with client-side MVC frameworks like Backbone.js, is there a reason for not creating and inserting the form elements on the fly when necessary using a factory method? Something like this:
HTML
<div id="description">
Lorem ipsum dolar set amit...
</div>
<span class="edit_button"></span>
Backbone.js View
events: {
"click .edit_button": "renderEditInPlaceForm",
},
renderEditInPlaceForm: function:(e) {
var el = $(e.currentTarget).previous();
var id = el.attr('id');
var value = el.text();
var tagName = el.tagName();
var view = new editInPlaceForm({
id: id,
type: tagName,
value: value
});
$("#id").html(view.render().el)
},
Where editInPlaceForm is a factory that returns the appropriate edit-in-place form element type based on tagName. This factory view also controls all its own logic for saving an edit, canceling an edit, making requests to the server and rerendering the appropriate original element that was replaced with the .html() function?
It seems to me that if we use this approach then we could also render the <span class="edit_button"></span> buttons on the fly based on a user's editing rights like so:
<h1 id="title">
<%= document.get("title") %>
</h1>
<% if (user.allowedToEdit( document, title )) { %>
<span class="edit_glyph"></span>
<% } %>
where the allowedToEdit function on the user model accepts a model and attribute as its arguments.
It's an interesting idea. The devil is in the detail.
While your simple example is easily rendered as an editable form on the fly, things quickly get trickier when dealing with other data types.
For example - suppose my edit form requires the user to choose a value from a select list. On the display form I can simply display the user's choice, but for the edit form I am going to need those other available choices. Where do I hide them on the display? Similar issues exist for checkboxes, radio lists...
So, perhaps we should consider rendering the edit form, and then deriving our display-view from that?
After 5 Backbone apps I came to same thoughts.
When things are complicated you have forms to show relations between user data,
but in simple cases you just need input, select, checkbox over h1, div or span
Now I am searching for jQuery plugin to make simple in place editing without ajax.
jQuery but not Backbone becuase I don't want to be tight coupled with Backbone for such small thing.
Likely to wright my own jQuery + Synapse plugin http://bruth.github.com/synapse/docs/.
Synapse for binding with model and jQuery for input placing

Categories