Add multiple items to text-area with duplicate items.
I have one text-area which store data after clicked add data link.
How can i prevent add duplicate items to text-area?
JavaScript call DOM event:
var Dom = {
get: function(el) {
if (typeof el === 'string') {
return document.getElementById(el);
} else {
return el;
}
},
add: function(el, dest) {
var el = this.get(el);
var dest = this.get(dest);
dest.appendChild(el);
},
remove: function(el) {
var el = this.get(el);
el.parentNode.removeChild(el);
}
};
var Event = {
add: function() {
if (window.addEventListener) {
return function(el, type, fn) {
Dom.get(el).addEventListener(type, fn, false);
};
} else if (window.attachEvent) {
return function(el, type, fn) {
var f = function() {
fn.call(Dom.get(el), window.event);
};
Dom.get(el).attachEvent('on' + type, f);
};
}
}()
};
JQuery add data to textarea:
$("#lkaddlanguage").click(function(){
var totalstring;
var checkconstring = $("#contentlng").text();
var strLen = checkconstring.length;
myStr = checkconstring.slice(0,strLen-1);
//alert(myStr);
var checkedItemsArray = myStr.split(";");
var j = 0;
var checkdup=0;
totalstring=escape($("#textval").val()) ;
var i = 0;
var el = document.createElement('b');
el.innerHTML = totalstring +";";
Dom.add(el, 'txtdisplayval');
Event.add(el, 'click', function(e) {
Dom.remove(this);
});
});
HTML Display data
<input type="textbox" id="textval">
<a href="#lnk" id="lkaddlanguage" >Add Data</a>
<textarea readonly id="txtdisplayval" ></textarea>
This seems a very straightforward requirement to me, so I'm not quite clear where you're getting stuck. I have not tried too hard to figure out your existing code given that you are referencing elements not shown in your html ("contentlng"). Also, mixing your own DOM code with jQuery seems a bit pointless. You don't need jQuery at all, but having chosen to include it why then deliberate not use it?
Anyway, the following short function will keep a list of current items (using a JS object) and check each new item against that list. Double-clicking an item will remove it. I've put this in a document ready, but you can manage that as you see fit:
<script>
$(document).ready(function() {
var items = {};
$("#lkaddlanguage").click(function(){
var currentItem = $("#textval").val();
if (currentItem === "") {
alert("Please enter a value.");
} else if (items[currentItem]) {
alert("Value already exists.");
} else {
items[currentItem] = true;
$("#txtdisplayval").append("<span>" + currentItem + "; </span>");
}
// optionally set up for entry of next value:
$("#textval").val("").focus();
return false;
});
$("#txtdisplayval").on("dblclick", "span", function() {
delete items[this.innerHTML.split(";")[0]];
$(this).remove();
});
});
</script>
<input type="textbox" id="textval">
<a href="#lnk" id="lkaddlanguage" >Add Data</a><br>
<div id="txtdisplayval" ></div>
<style>
#txtdisplayval {
margin-top: 5px;
width : 200px;
height : 100px;
overflow-y : auto;
border : 1px solid black;
}
</style>
Note I'm using a div (styled to have a border and allow vertical scrolling) instead of a textarea.
As you can see I've coded it to display an alert for duplicate or empty items, but obviously you could remove that and just ignore duplicates (or substitute your own error handling). Also I thought it might be handy to clear the entry field and set focus back to it ready for entry of the next value, but of course you can remove that too.
Working demo: http://jsfiddle.net/LTsBR/1/
I'm confused.
The only variable that might have duplicates comes from:
var checkedItemsArray = myStr.split(";");
However, checkedItemsArray is not used for anything.
Incidentally, the escape method is deprecated in favour of encodeURIComopnent.
When setting the value of the textarea, do just that: assign to its value property, not to its innerHTML (it can't have markup inside it or any elements, only text nodes).
If you want to check that the members of checkedItemsArray are unique, and you don't mind if they are sorted, you can use a simple function like:
function unique(arr) {
arr.sort();
var i = arr.length;
while (i--) {
if (arr[i] == arr[i - 1]) {
arr.splice(i, 1);
}
}
return arr;
}
Orignal order can be maintained, but it's a bit more code.
Related
I have a very basic search function that sorts through a list of names. There are about 4,000 names so this loop is a bit to slow to keep up as users type. Here is the loop for each new search value:
elements.each(function(i, el){
var name = $(el).find('button').text();
name = name.toLowerCase();
if(name.indexOf(value) >= 0) {
$(el).show().addClass('visible');
}
else {
$(el).hide().removeClass('visible');
}
});
How can I optimize this loop so it can keep up with a user typing?
Follow up:
Based on user suggestions, I mapped and stored the data in an array when the text input receives focus. Then, I changed the active search to the below:
Array.prototype.forEach.call(this.cache, function(el, i){
var name = el.name;
if(name.indexOf(value) >= 0) {
el.element.style.display = '';
el.element.classList.add('visible');
}
else {
el.element.style.display = 'none';
el.element.classList.remove('visible');
}
});
I tried to cut off jQuery where I could to better optimize. It seems to be working without any noticeable lag now! Thanks for the great suggestions and discussion.
As mentioned #Taplar, you could delay the error-check.
Not everything has to be real-time, as it could harm the usability.
It's common to check something in a timespan after the the system stops receiving the action, and while user is typing, you could just grey out the status text and once 500ms after user stops typing passes, you execute the check and assign necessary class to the status text.
Here's an example implementation. It won't throw a "error" unless you type "BAD" in the text box.
var timeout;
$("#txt").keydown(function(e){
$("#status").removeClass("bad").addClass("wait");
clearInterval(timeout);
timeout = setTimeout(checkStuff, 500);
});
function checkStuff(){
var t = $("#txt").val();
var s = $("#status");
s.removeClass("wait").html("No errors");
if(t.indexOf("BAD") > -1) {
s.addClass("bad").html("Errors detected!");
}
}
#status{
color:green;
}
#status.wait{
color:rgba(150,150,150,1);
}
#status.bad{
color:red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="txt" placeholder="type plz"><br>
<span id="status">No errors</span>
jQuery is sloooooooooooooooow, if you can do your looping and most of your heavily repeated functionality in vanilla JS, you will save a lot of processing time. Also pre-mapping text is huge, looking up the button element and getting the text value every time is a big waste.
/*
elements.each(function(i, el){
var name = $(el).find('button').text();
name = name.toLowerCase();
if(name.indexOf(value) >= 0) {
$(el).show().addClass('visible');
}
else {
$(el).hide().removeClass('visible');
}
});
*/
let elemQueryArr = elements.map(elem => {
return {
elem: elem,
text: elem.querySelector('button').text.toLowerCase()
}
}), len = elemQueryArr.length
function filterElems(val){
let i = -1
while(++i < len){
let elem = elemQueryArr[i]
if(elem.text.indexOf(val) > -1) elem.elem.classList.add('visible')
else elem.elem.classList.remove('visible')
}
}
My question is little bit confuse. I created comment box to post comments in javascript. I am posting every comment using javascript by creating dynamically created elements, attributes and event handlers.
Javascript:
var commentSubmit = document.getElementById("commentSubmit"),
commentBox = document.getElementById("commentBox"),
deleteComment = document.getElementById("deleteComment"),
commentList = document.getElementById("commentList");
function getDataFromLocalStorage(){
var i = 0,
cArr = [],
cKey;
for (; cKey = window.localStorage.key(i); i++) {
var cObj = {};
cObj.cId = cKey;
cObj.cText = window.localStorage.getItem(cKey);
cArr.push(cObj);
}
return cArr;
}
function createElement(name,attr,events,html){
debugger;
var elem = document.createElement(name);
var elemText;
if(html){
for(var i=0;i<html.length;i++){
if(html[i].name == "text"){
elemText = document.createTextNode(html[i].value)
elem.appendChild(elemText);
}else{
elem.appendChild(html[i].value);
}
}
}
if(events){
for(var i=0;i<events.length;i++){
if(events[i]["customFunction"]){
elem.addEventListener(events[i].eventName,events[i]["customFunction"].bind(elem,events[i].eventParams.join(" ")),false);
}
}
}
for(var i=0;i<attr.length;i++){
elem.setAttribute(attr[i].name, attr[i].value);
}
return elem;
}
function deleteComment(cId){
localStorage.removeItem(cId);
loadComments();
}
function loadComments(){
var cComments = getDataFromLocalStorage(),
cTotal = cComments.length;
commentList.innerHTML = "";
if(cTotal){
for(var i=0;i<cTotal;i++){
var deleteCommentButton = createElement("BUTTON",
[
{name:"class",value:"deleteComment"},
{name:"id",value:"deleteComment"},
{name:"value",value:"X"}
],
[
{eventName:"onclick",eventParams:[cComments[i].cId],customFunction:"deleteComment"}
]
);
var commentWrapper = createElement("DIV",
[
{name:"class",value:"commentWrapper"},
{name:"id",value:cComments[i].cId}
],
[
{name:"text",value:cComments[i].cText},
{name:"html",value:deleteCommentButton}
]
);
/*var commentText = document.createTextNode(cComments[i].cText);
commentWrapper.setAttribute("class", "commentWrapper");
commentWrapper.setAttribute("id", cComments[i].cId);
commentWrapper.appendChild(commentText);*/
commentList.appendChild(commentWrapper);
}
}
}
loadComments();
commentSubmit.addEventListener("click", function(e){
var cKey = Date.now();
if(commentBox.value != ""){
localStorage.setItem(cKey,commentBox.value);
loadComments();
}
e.preventDefault();
return false;
}, false);
html:
<div class="commentForm">
<form>
<textarea rows=5 id=commentBox></textarea>
<button id=commentSubmit>Post</button>
</form>
</div>
<div id="commentList">
</div>
my question is
i want to attach deleteComment function to the dynamically created
element. here, i am sending the function name also dynamically. i was
unable to bind function.
here is jsfiddle
thanks in advance.
Don't set the listener to the function name. That's not how it works. Send the function itself:
{eventName:"onclick",eventParams:[cComments[i].cId],customFunction:deleteComment}
Also, depending on what exactly you want, joining the params may also not be what you REALLY want to do.
Additional answer:
Found a bug in your code:
{name:"id",value:"deleteComment"}
You're creating an HTML element (in this case a button) with the same name as a function. This causes the function to be deleted. This is a very weird feature that was introduced by IE that was copied by other browsers for compatibility with IE specific sites. Now it's part of the HTML5 specification :(
To fix this either rename your function or rename the element. See this edit for an example fix: http://jsfiddle.net/pcmyzhqu/10
I have a function that adds div when I press a button.
I have a counter re-setting logic written in the removal which should re-count all my names.
my problem is that when I add say 3 items, and remove the 2nd Item, my 3rd item does not rename itself.
what am I doing wrong with this?
$(function () {
var rowItem = $(".row", $(".formitems")); //select all rows from class formitems
$(".formitems").on("click", ".addRow", function () {
var newItem = rowItem.clone(),
rowIndex = $(".row", $(".formitems")).length;
$(":input", newItem).each(function (c, obj) {
$(obj).attr("name", $(obj).attr("crap") + rowIndex);
});
$(".formitems").append(newItem); // adds At the end of the container
}).on("click", ".removeRow", function () {
if ($(".row", $(".formitems")).length > 1) {
var target = $(this).parent().parent().parent().parent(".row");
target.slideUp(function () {
target.remove();
});
}
for (i = 0; i < $(".row", $(".formitems")).length; i++) //select our div called //formitems and then count rows in it
{
rowobj = $(".row", $(".formitems"))[i];
$(":input", rowobj).each(function (c, obj) {
$(obj).attr("name", $(obj).attr("crap") + i);
})
}
});
});
EDIT : Ok, So I put up alerts just before and after using remove(). Does jquery takes time to re-calulate all the properties? If yes, are they cached? If yes, can I force refresh them.?
Consider simplifying your life.
<input type="text" name="blah[]" />
You can have as many of these elements as you want. The server will receive an array of values, meaning you don't have to manually count them.
As far as i understand, you want to update the 'name's after the removal. But the code does the remove() asynchronously when slideUp() completed, while the renaming is invoked immediately after invoking the slideUp(), before remove() is called.
You can solve this by placing the renaming code after the call to remove(), inside the slideUp() handler:
}).on("click", ".removeRow", function () {
if ($(".row", $(".formitems")).length > 1) {
var target = $(this).parent().parent().parent().parent(".row");
target.slideUp(function () {
target.remove();
// rename now!
for (i = 0; i < $(".row", $(".formitems")).length; i++) {
rowobj = $(".row", $(".formitems"))[i];
$(":input", rowobj).each(function (c, obj) {
$(obj).attr("name", $(obj).attr("crap") + i);
})
}
});
}
});
How do I fire one event before the previous function completed its function?
I have the following AJAX code :
var BrainyFilter = {
//...
init: function (opts) {},
changeTotalNumbers: function (data) {
jQuery(BrainyFilter.filterFormId).find('.bf-count').remove();
jQuery(BrainyFilter.filterFormId).find('option span').remove();
jQuery(BrainyFilter.filterFormId).find('select').removeAttr('disabled');
jQuery('.bf-attr-filter').not('#bf-price-container').find('input, option')
.attr('disabled', 'disabled')
.parents('.bf-attr-filter')
.addClass('bf-disabled');
if (data && data.length) {
for (var i = 0; i < data.length; i++) {
jQuery('.bf-attr-' + data[i].id + ' .bf-attr-val').each(function (ii, v) {
if (jQuery(v).text() == data[i].val) {
var parent = jQuery(v).parents('.bf-attr-filter').eq(0);
var isOption = jQuery(v).prop('tagName') == 'OPTION';
var selected = false;
if (isOption) {
jQuery(v).removeAttr('disabled');
selected = jQuery(v)[0].selected;
} else {
parent.find('input').removeAttr('disabled');
selected = parent.find('input')[0].checked;
}
parent.removeClass('bf-disabled');
if (!selected) {
if (!isOption) {
parent.find('.bf-cell').last().append('<span class="bf-count">' + data[i].c + '</span>');
} else {
jQuery(v).append('<span> (' + data[i].c + ')</span>');
}
}
}
});
}
jQuery('.bf-attr-filter input[type=checkbox]').filter(':checked')
.parents('.bf-attr-block').find('.bf-count').each(function (i, v) {
var t = '+' + jQuery(v).text();
jQuery(v).text(t);
});
// since opencart standard filters use logical OR, all the filter groups
// should have '+' if any filter was selected
if (jQuery('.bf-opencart-filters input[type=checkbox]:checked').size()) {
jQuery('.bf-opencart-filters .bf-count').each(function (i, v) {
var t = '+' + jQuery(v).text().replace('+', '');
jQuery(v).text(t);
});
}
}
// disable select box if it hasn't any active option
jQuery(BrainyFilter.filterFormId).find('select').each(function (i, v) {
if (jQuery(v).find('option').not('.bf-default,[disabled]').size() == 0) {
jQuery(v).attr('disabled', 'true');
}
});
},
//...
} // close the BrainyFilter
I also have another jQuery file running to get the bf-count value using $('.bf-count').text().
When the page load, the bf-count value is empty. Since the code above inject the bf-count, I will need to wait until it finishes the for loop in order to get the bf-count value.
What is the best way to approach this?
without knowing how the second js file is loaded, I can only give you a guesstimate suggestion.
If you want to run the second js file code after the page is fully loaded, you can wrap the code in:
jQuery(window).load(function(){
//your code here. runs after the page is fully loaded
});
jQuery documentation: http://api.jquery.com/load-event/
"The load event is sent to an element when it and all sub-elements have been completely loaded. This event can be sent to any element associated with a URL: images, scripts, frames, iframes, and the window object."
I currently have the following code on my website:
$(document).ready(function() {
$("#contact").on("click", function(e)
{
e.preventDefault();
$("#contactform").toggle('fast');
});
});
I would like to have an if(isset($_GET['email')); trigger this function as well, so have it open on page load if the $_GET variable is set.
I'm rather new with Jquery and not sure if this is possible, I also have another somewhat related question, I'm not sure if I should make a new question for this as I'm fairly new to stackoverflow as well, but here it is.
Say I have two of these:
$(document).ready(function() {
$("#contact").on("click", function(e)
{
e.preventDefault();
$("#contactform").toggle('fast');
});
});
$(document).ready(function() {
$("#archivestop").on("click", function(e)
{
e.preventDefault();
$("#archives").toggle('fast');
});
});
I want one to close if the other one is opened, how would I go about this?
Thanks!
Here's the Javascript-solution:
function getParam(key) {
var paramsStr = window.location.search.substr(1, window.location.search.length),
paramsArr = paramsStr.split("&"),
items = [];
for (var i = 0; i < paramsArr.length; i++) {
items[paramsArr[i].split("=")[0]] = paramsArr[i].split("=")[1];
}
if (key != "" && key != undefined) {
// return single
if (items[key] != undefined) {
return items[key];
} else {
return null;
}
} else {
// return all (array)
return items;
}
};
if (getParam("email")) {
// ...
}
Regarding your second question you can use the following to determine if an element is visible:
var bool = $('.foo').is(":visible");
So to hide an element if it is visible you would do something like this:
if ($('.foo').is(":visible")) {
$('.foo').hide();
}
I'm silly and have answered my first question. I still have yet to have my coffee.
The following works, just insert it into the div that is to be displayed:
<div id="contactform" style="<?php if(isset($_POST['email'])) echo "display:block;" ?>">
content
</div>