With jQuery, I'm interested in creating the following interaction model.
When a user types in the page, anytime jQuery notices three !, "!!!" typed in a row, to be able to do something about it.
After noticing three !, "!!!", wrap the previous sentence (finding the last period from the location the user is currently typing and wrapping a <span class=Important>
How can I do this?
This should work. I've set it up as a live event handler (so that it works for elements dynamically added to the page), but you could use the exact same function as a normal keyup handler on any textarea or input element as well.
It checks to see whether the element's current value ends with !!! and if so performs the replacement, both using simple regular expressions.
$("input,textarea").live("keyup", function() {
var $this = $(this);
var value = $this.val();
if (/!!!$/.test(value)) {
$this.val(value.replace(/(\.?)([^.]+?\.?\s*)!!!$/, '$1<span class="important">$2</span>'));
}
});
As much as I am loathe to say that jQuery isn't good for something - perhaps this kind of logic is better handled on the server. Unless they are typing in an HTML editor where the newly inserted span tags are invisible to the user, it may be a little disconcerting to be typing in a textarea/textbox and suddenly see a bunch of HTML inserted into my comment.
Related
I have a function that dynamically creates div elements based upon whatever input is given, and lets them choose certain items by clicking on each div. I have it so that if the div is clicked, a function (named checkToggle) is called that makes it looks like it is selected and adjusts some related variables. There is a checkbox in the div element that is toggled by this function (hence its name). Long story short, I had to jump through some hoops to get it to work, most of which I don't even remember. Please don't ask me about that.
The point of this question is this. I initially used the following JavaScript code to run the function when the checkbox was clicked. It was assigned by the main function, which created these div elements using a for loop.
document.getElementById(`${itemID}-checkbox`).onclick = function() {
checkToggle(`${itemID}-checkbox`);
};
This works, but I wanted to try to convert all of my onClick functions to JQuery. Here is the JQuery alternative I created.
$(`${itemID}-checkbox`).on(`click`, function() {
checkToggle(`${itemID}-checkbox`);
});
While the code itself seems to be fine, it does not work. It seems as if JQuery functions cannot be created like this in a for loop or something. It is applied after the element is created and put in its place, so I don't think it has anything to do with the element not being ready. I am also having the same issue with 2 other similar cases. Any idea as of why this isn't working?
Let me know if more information is needed and if so, what kind of information is needed.
You need to update the selector to Target HTML id using the # character. Simply prepend the character to the query:
$(`#${itemID}-checkbox`).on(`click`, function() { checkToggle(`${itemID}-checkbox`); });
It would also apply to DOM methods querySelector or querySelectorAll as well.
Hopefully that helps!
I was wondering which, if any, framework would be the best to achieve capturing keystrokes and appending these to, say a "p" element. What I'm trying to achieve is having the client type something on the keyboard, and then have that sentence or whatever, appended to html, hereby displaying it in the "p" element.
Important notice; I'm not trying to call a function at a given keypress - ex. shift+alt, rather what I'm trying to do is, streaming the keyboardstrokes to an html element.
You don't necessarily need a framework for that task.
Another possbily viable option besides Kai Christensen's would be to create a textbox outside of the visible screen area, set the focus to this textbox automatically and create a change listener for the textbox.
You can then simply replace the content of the target element with the textbox's content whenever it changes.
This saves you the trouble of listening to keyboard events manually, distinguishing upper and lower case letters etc.
I would definitely go with jQuery to capture the keys and then constantly .replace() your div with a div with the same properties that contains the latest version of the updating string. Codecademy has a great set of lessons for this type of jQuery use.
Not that I'm an expert, though. I'm sure someone else has a better answer than my own.
Something like this?
Html
<p id="text"></p>
<input id="textinput"></input>
Js
//Register on keyup, append text from input-field to <p> element.
window.addEventListener("keyup", function () {
document.getElementById("text").innerHTML=document.getElementById('textinput').value;
});
jsfiddle: https://jsfiddle.net/vwobq9xf/1/
So, I have done some research, and it's pretty clear that id should be unique in the DOM. This is my issue, and I am curious what the best solution to it is:
I am using jQueryUI tabs as well as a custom menu and ajax to load specific pages into a content pane without re-rendering the browser. From some of these sub pages, a user can open a popup (done with a jQueryUI dialog) to edit customer information. Because these load a server side page, in each place that this form would be generated, it uses the same ids.
I have found that there are a number of ways to close a dialog without removing it from the DOM. This causes confusion later when it, or another form is opened elsewhere, and now there are conflicting ids present in the DOM. I am working on tracking down all the ways to close a dialog, and making sure to replace them with .dailog("destroy").remove() to make sure that they are erased from the DOM, but I want to be sure the solution here is fool proof in the event that someone one gets left on the page.
My two immediate thoughts:
1.) Generate a random string to append to each form element's id when the form is rendered, fully preserving uniqueness of the id.
2.) Use more specified selectors when getting the form data, i.e. scoping it to the popup that was created, the page that it was created from, and then the tab that it is under, and not worrying as much about id uniqueness.
The first feels ugly, and in theory you COULD randomly duplicate the string and still run into an issue. The later just feels bulky and ugly to me. Is there an option I am missing? What is best practice when it comes to dealing with IDs that can be duplicated in this way?
Thanks,
Eric
You may use classes if you need "similar" objects. Id's purpose is to identify object uniquely.
By the way, classes are widely used, for example, in Bootstrap.
UPDATE: I think your "second" approach is bad, as you eventually can change the layout, but, in this way, you should track every change, and remember WHERE to change your selectors (possibly, it will be multiple places).
Before inserting the new element into the list, you could check if there is already an element existing on the page with that id. If it does exist than delete it.
Like:
if($("#"+your_id).length!==0)
$("#"+your_id).remove();
//insert the new element
But if you need that element as well, i would suggest that you use classes to group elements used for same purposes.
Here is what you can do to distinguish between the different dialogs when you try to close them:
1) Change each dialog id into a class, so that your dialogs can share the same class. Using the same id is not recommended.
2) You can create a click listener for the button that closes the correct dialog by using the event callback parameter. See the working snippet below.
var closeButtons, i, closeButtonsLen;
closeButtons = document.getElementsByClassName('close');
for (i = 0, closeButtonsLen = closeButtons.length; i < closeButtonsLen; i += 1) {
closeButtons[i].addEventListener('click', function (e) {
e.target.parentNode.setAttribute('hidden', true); // if you want to hide the dialog
});
}
<div class="dialog"><button class="close">first x</button></div>
<div class="dialog"><button class="close">second x</button></div>
<div class="dialog"><button class="close">third x</button></div>
You can replace e.target.parentNode.setAttribute('hidden', true); with whatever you need to do. e.target.parentNode gets the dialog element.
EDIT #2:
Made a JS Fiddle... http://jsfiddle.net/N2p6G/ (I hardcoded some stuff that I'm certain works correctly, but the problem is still there)
Original:
So, I have written tens of thousands of lines of javascript, and used code that look like this a hundreds of times and I don't understand what's going on.
blacklistitembutton.onclick = function() {
console.log("clicked.");
}
The above code does not seem to be working... and I can't figure out why
In fact, I use the same method earlier in the same file... and it works fine!
settings.onclick = function() {
settings_popup.toggle();
}
EDIT:
Might it have something to do with the fact that it's being executed in a for loop?
Here is the code...
var blacklistButton = document.createElement('input');
blacklistButton.type = 'button';
blacklistButton.value = "Add Current Site to Blacklist";
blacklistButton.onclick = function() {
console.log('blacklistButton clicked');
}
for (var i=0;i<blacklist.length;i++) {
var blacklistitembutton = document.createElement('div');
blacklistitembutton.type = 'button';
blacklistitembutton.blacklistValue = blacklist[i];
blacklistitembutton.value = "X";
blacklistitembutton.onclick = function() {
console.log("clicked.");
}
}
Then both blacklistButton and all of the blacklistitembuttons are put into the document through element.appendChild (and they all show up successfully!)
The blacklistButton onclick fires just fine, and the blacklistitembutton onclick does not.
document.addEventListener('click', function(){
console.log('clicked');
}, false);
Edit:
Here is a re-write of your code in a fiddle: http://jsfiddle.net/N2p6G/1/
There are a lot of things in your code that worry me. Hopefully from my re-write you can see there are better ways to handle some things.
1) I'm not sure why you are using document.write() at the beginning. That has very little purpose.
2) You are modifying the DOM way too much. Some of the DOM elements you are creating in code are better-served as just being target locations in html. Only the dynamically-created input button elements need to be done in javascript. Remember, modifying the DOM should be done as little as possible.
3) Don't assign events using the onclick, onsubmit, onhover, etc syntax. Events should only be bound to DOM elements using addEventListener. The other benefit of doing it the proper way is that you can assign multiple events of the same type, if need be, to the same element. Also, with some extra state code that I haven't included, you can selectively remove particular events later if you need to.
4) There was a debate several years ago about whether using innerHTML and string templates was faster/better than using DOM creation methods. For a while, the best solution was to use documentFragments and a combination of the two methods. These days, it doesn't really matter anymore since all browsers are pretty damn fast, so for simplicity's sake is good to just go with innerHTML.
This also goes back to the rule of "don't touch the DOM too much". If you look at my code, you can see that I'm assembling the final html simply as an array of elements that gets joined as a single string at the end. Its then rendered to the DOM with a single innerHTML statement. I'm only touching the DOM one time, instead of multiple times.
5) The last bit goes into events again. At the beginning and end of the code you can see where and how I've added the events for the DOM elements. Indeed, the addEventListener at the beginning could be moved to the end to group all the event declarations together, but it doesn't really matter. I left it at the top to help you understand what's going on better.
Hope this helps.
For unlimited event bindings, either use addEventListener or attachEvent method. You cannot add more than one event of the same type using that traditional method.
I don't know if it's a typo in what you put here, but in the loop you are creating a "div" and then assigning it a type of "button". Does that work or is it throwing an error? If it is then that explains why the event handler is not getting the function. Try making it an "input" and see if it now works.
Fixed it!
blacklistitem.innerHTML += blacklist[i];
^ was messing it up, at this point in the code blacklistitem is still a javascript item, not yet appended to its to-be parent element in the document
So I just stuck blacklist[i] into a span tag and appended as a child and now it works fine :)
I seem unable to correctly attach the onchange event to a dijit.form.Select widget. However, I am new to web development, so I could be doing something completely idiotic (although, as best I can tell (and I've read all the docs I could find) I'm not). I made sure the body class matches the dojo theme, that I dojo.require() for all the widgets I use (and dojo.parser), and still, nada. The code I'm using is:
dojo.addOnLoad(function () {
var _query = dojo.query('.toggle');
for (var i in _query) {
dojo.connect(_query[i], 'onchange', function (ev) {
console.log(ev + ' fired onchange');
});
}
});
Any help at all would be appreciated.
Addition: after digging more into the internals of how dijit renders widgets, I discoverd that when I add the dojoType='dijit.form.Select' attribute-value pair to my html element (doing this declaratively), dijit actually renders a one-row two-col table. The table's first element is a span (with the class dijitSelectLabel) that I'm assuming just displays the selected (or default) element. It's second element appears to be a button rendered as a down arrow that toggles the display of the menu itmes in response to certain DOM events. Also (and I thought this was pretty nifty), dijit doesn't actually place the select options in the DOM tree until one of those events is triggered. I looked at the HTML in firebug right after a fresh pageload (before i clicked on anything), and the second option isn't anywhere to be found. Then, once I click on the arrow button, there's a dijit.Menu widget, dijit sticks a dijit.Menu to the end of the body node; after I click somewhere else, the Menu widget is still the body's lastChild, now its just hidden and not attached to the form.Select widget.
Should it really be this complicated if all I want to do is place a different dijit.form widget in the DOM tree depending on what item the user selects?
Conclusion:
Turns out it was a capitalization issue.
dojo.connect(widget_obj, 'onChange', function_obj);
works, whereas
dojo.connect(widget_obj, 'onchange', function_obj);
doesn't.
So I was right that I was being completely stupid. I assumed that because the all lowercase version works when putting placing in it an html tag as an attribute, that Dojo would treat it the same. It makes sense, because dijit.form.Select has no .onchange attribute, but does have a .onChange attribute. (I ended up sticking with a .Select over a .FilteringSelect because I don't my users to be given any impression that they can type in something.) So, which one of you guys do I give the answer to (because you both had onChange in your posts, I guess I was just too inexperienced to realize that the case mattered)?
For anyone else finding this page through a web search, you may have made the same mistake I did .. copy-pasting your markup such that each has the same 'value'.
e.g.
<select dojoType='dijit.form.Select' onChange="fn">
<option value='foo'>Foo 1</option>
<option value='foo'>Foo 2</option>
<option value='foo'>Foo 3</option>
</select>
fn() will never be called, because the change handler code checks the new value against the previously selected value and does not fire onChange unless it's changed.
Try the following when doing a dojo.connect:
var inputEvents = []; //Global var
inputEvents.push(dojo.connect(dijit.byId(inputFldStr), "onChange", eventFuncObj));
Store the connection in a global var.
In your code you connect a handler to 'onchange' event of dom nodes, not dojo widgets. dojo.query returns you a NodeList object - a collection of nodes that match the query.
In this case it's more reliable to connect to a widget's 'onChange' event, as GoinOff showed. Just a little addition to his answer to make sure you're doing this right.Assume this is your html (in later versions of Dojo dijit.form.Select has been replaced with dijit.form.FilteringSelect):
<input dojoType="dijit.form.FilteringSelect" id="stateInput" store="stateStore" searchAttr="name" name="state"/>
Then you would connect to 'onChange' this way (you also can store the connection in some array to be able to disconnect it later, as GoinOff suggested):
dojo.addOnLoad (function () {
dojo.connect(dijit.byId("stateInput"), "onChange", function(){});
}
But it's another story if you don't know your widget's id and want to use dojo.query to connect to multiple widgets.