Why is my .focus() going to the beginning of my `contenteditable` `div`? - javascript

I am creating a book writing website, mostly in php. I have a jQuery function that when the "New Chapter" button is clicked triggers an AJAX function as well as some other JS/jQuery events. Part of those events is that it should focus() my cursor to the END of the <div> and then focus the cursor to the end of it.
For whatever reason, after it appends the information, it moves my cursor to the beginning of the <div>
HTML
<section>
<aside>
<h3>Table of Contents</h3>
<ul id='toc_base'><?php json_toc($meta); ?></ul>
</aside>
<article>
<div contenteditable='false' id='metadata'>
<?php
$bookTitle = htmlspecialchars($bookTitle);
$author = htmlspecialchars($meta->book->metadata->author);
$startyear = htmlspecialchars($meta->book->metadata->startyear);
$isbn = ($meta->book->metadata->isbn != "" && $meta->book->metadata->isbn != null)
? htmlspecialchars($meta->book->metadata->isbn)
: "Not Listed";
$endyear = ($meta->book->metadata->endyear != "" && $meta->book->metadata->endyear != null)
? htmlspecialchars($meta->book->metadata->endyear)
: "TBD";
echo "Title: $bookTitle | Written By: $author | ISBN: $isbn<br />Start Year: $startyear | End Year: $endyear";
?>
</div>
<nav style='grid-column: 1 / span 10'>
<button style='font-weight:bold' id='bold'>B</button>
<button style='font-style:italic' id='italic'>I</button>
<button style='text-decoration:underline' id='underline'>U</button>
<!-- BUTTON IS HERE -->
<button style='font-weight:bold' onclick='addChapter()'>Chapter Title</button>
<!-- BUTTON ENDS HERE -->
<button class='tooltip'>
<i class="fa-sharp fa-solid fa-person-circle-plus"></i>
<span class='tooltiptext'>Add new character to the panel on the right.</span>
</button>
<button class='tooltip' onclick='autosave()'>
<i class="fa-sharp fa-solid fa-floppy-disk"></i>
<span class='tooltiptext'>Be like Jesus - Save!</span>
</button>
<button class='tooltip'>
<i class="fa-sharp fa-solid fa-database"></i>
<span class="tooltiptext">Edit metadata such as author information, and ISBN.</span>
</button>
<div id='update'>...Saved...</div>
</nav>
<!-- CONTENT IS HERE -->
<div contenteditable='true' id='content' type='text' autocomplete='off'></div>
<!-- CONTENT ENDS HERE -->
</article>
<aside>
<h3>Characters</h3>
<ul id='char_base'><?php json_characters($meta); ?></ul>
</aside>
</section>
jQuery and JS
function addChapter() {
var end;
let chapter = prompt("New Chapter Name?");
if (chapter != null) {
$.get("editor.php?newchapter=" + chapter, function (data, status) {
$("#toc_base").html("<li>" + chapter + "</li>");
$("#content").append("[b]" + chapter + "[/b]\n\n");
var div = document.getElementById('content');
div.focus();
});
}
}
Errors/Exceptions
There are no errors or exceptions displayed on the page or the console_log.
What I've Done
I did check a number of SO questions and all of them seemed to say to just use focus(), which, I have (in many different ways). I've tried it in a timeout function, which did the exact same thing as above. I did it in jQuery: $("#update").focus(); and there was no difference.
Browser
Chrome Version 107.0.5304.62 (Official Build) (64-bit)
What I'm Hoping to Avoid
I'd like to avoid using textarea if possible. All the extra white-space it has the potential to create when using php is just annoying. I can do it if necessary, but I'd like to stay away from it.
SMALL CHANGE TO WHAT'S HAPPENING
If I were to manually move the cursor to the end after one input of the chapter title, then add a second, it focuses at the beginning of the most recent append.

I've given up on trying to get <div contenteditable='true'> to do what I want. I've changed it to <textarea> - I then updated my jQuery:
function addChapter() {
let chapter = prompt("New Chapter Name?");
if (chapter != null) {
let temp = $("#content").val();
$("#toc_base").append("<li>" + chapter + "</li>");
$("#content").val(temp + "[b]" + chapter + "[/b]\r\n\r\n");
$.get("editor.php?newchapter=" + chapter, function (data, status) { });
}
$("#content").focus();
}
This does appear to do what I want. I'm not happy that I need to save all the text to a var and then set the val() to the original + new.
If anyone has any ways to do this better, or with the <div> that I originally had, please let me know.
I was finally able to figure out how to do this...
HTML/PHP
<div id='content_container_1'>
<div id='content_container_2'>
<div contenteditable="true" id='content'><?php echo $book->content->writing; ?></div>
</div>
</div>
CSS
#content_container_1 {
height:80vh;
width: 100%;
overflow:auto;
border: 1px solid black;
background-color: white;
}
#content_container_2 {
min-height:100%;
display:table;
}
#content {
width:273px;
font-size: 18px;
font-weight: normal;
line-height: 18px;
outline: none;
vertical-align: middle;
display: table-cell;
position: relative;
-webkit-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
word-wrap: break-word;
overflow:hidden;
}
JS
$(document).ready(function () {
$("#content").focus(); /** <---- This part ---->
setInterval(autosave, 30000); //ignore
$("#update").removeClass("visible"); //ignore
});

Related

CSSStyleSheet.insertRule - trouble with indexing and dynamic variables using Javascript

I've read up on inserting css rules using Javascript and have managed to get it working (after some trial & error). So I have 2 questions:
Q.1 Why is an index < 1 not working - see Mozilla example (and many others) below:
// push a new rule onto the top of my stylesheet - doesn't work...
myStyle.insertRule("#blanc { color: white }", 0); // returns Uncaught DOMException: Failed to execute 'insertRule' on 'CSSStyleSheet': Failed to insert the rule.
// change the index and it works!
myStyle.insertRule("#blanc { color: white }", 1);
This article by David Walsh (very helpful), explains that the default for index is -1. He uses 1 in his example, which is what worked for me. Anything less than 1, ie 0 or -1 (as per the default) threw the following errors:
Index -1 error:
Uncaught DOMException: Failed to execute 'insertRule' on 'CSSStyleSheet': The index provided (4294967295) is larger than the maximum index (2071).
Index 0 error:
Uncaught DOMException: Failed to execute 'insertRule' on 'CSSStyleSheet': Failed to insert the rule.
It's not a huge problem, but it does mean I can't control specificity. I can use !important or rework the css/inserted rule so that it overrides any existing styles, but as I'm just learning JavaScript, I'd really like to know why it's not working as expected. Does anyone have any ideas?
Q.2 Having got it to work using index 1, I now want to pull in values dynamically. I have the item names in an array, which is used to create multiple objects, inside which are the property values I want to use for the individual style rules for that object.
Basically what I'm trying to output is this (which works):
styleSheet.insertRule("#item { border-top-color: #000000; border-right-color: #ffffff; }", 1);
But using variables, something like this:
styleSheet.insertRule("[itemName] { border-top-color: [itemName.value1]; border-right-color: [itemName.value2]; }", 1); // itemName.valueX being the object's array item
I've tried heaps of things, but I can't get the array item bit to work, ie colour and colour4 should actually be itemName.value1/2 or a var that equals the same. This is the closest I've got so far...
styleSheet.insertRule("#" + name + " { border-top-color: " + colour + "; border-right-color: " + colour4 + " !important; }", 1); // 1st rule works, 2nd doesn't show anything
It all works lovely if I write it manually (as per the 1st example), but how to do it dynamically? I've found info on insertRule, but not using dynamic values - can anyone help/point me in the right direction?
Many thanks in advance!
Expanded WIP for more clarity:
function itemColours() {
for (i = 3; i < itemsArray.length; i++) {
let name = itemsArray[i];
let colour = #000000;
console.log(item1.value); // returns the hex value I want to use in the rule
styleSheet.insertRule("#" + name + " { border-top-color: " + colour + "; border-right-color: " + name + ".value !important; }", 1);
// rule 1 works, rule 2 doesn't...
}
Update 2
This demo:
can accept user data to use insertRule() and deleteRule().
has an Add Set button which will create and append a clone of the <form> part of the document
has 3 styleSheets Bootstrap [0], CSSOM, and the <style> tag.
Demo 4
// Just for demo
.as-console-wrapper {
width: 170px;
max-height: 40px;
transform: translateX(340px)
}
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<link href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css' rel='stylesheet' title='Bootstrap-3.3.7'>
<link href='https://glpjt.s3-us-west-1.amazonaws.com/ir/cssom.css' rel="stylesheet" title='CSSOM'>
<style title='Tag'>
body {
max-width: 96%;
visibility: hidden;
font: 400 16px/1.2 Verdana
}
ul.x-list.x-list {
margin-left: 0;
padding-left: 1em;
text-indent: -1em;
list-style-type: none;
}
li.x-item.x-item {
list-style: none;
line-height: 1.5;
}
.x-flex.x-flex {
display: flex;
justify-content: center;
align-items: center;
}
.x-col {
display: flex;
flex-direction: column
}
option::before {
content: attr(name)
}
#idx {
max-width: 6ch;
}
label {
margin: 10px auto 10px -15px
}
#add {
position: fixed;
z-index: 1;
top: 50px;
left: 20px
}
</style>
</head>
<body>
<main class='container' style='padding:50px 0 20px 20px'>
<form id='cssom' class='row x-col'>
<div class='btn-group col-sm-12'>
<input id='add' class='btn-lg btn-success' type='button' value='Add Set'>
</div>
<section class='row'>
<!--=================================[0]-->
<fieldset class='set0 col-sm-12'>
<hr>
<div class='row x-flex'>
<!--=================================[1]-->
<label class='form-control-label col-sm-2'>CSS Rule</label>
<textarea id='rul0' class='form-control col-sm-10' rows='2'>li.x-item.x-item::before {content: '\1f539\00a0';list-style:none;font-size:small;position:relative;bottom:2px}</textarea>
</div>
<div class='form-inline row'>
<label class='form-control-label col-sm-2'>Stylesheet</label>
<select id='sht0' class='form-control col-sm-4'>
<optgroup label="LINK">
<!--================================[3]-->
<option value='0' selected name='Bootstrap-3.3.7'> [0]</option>
<option value='1' name='CSSOM'> [1]</option>
</optgroup>
<optgroup label="STYLE">
<option value='2' name='Tag'> [2]</option>
</optgroup>
</select>
<label class='form-control-label col-sm-1'>Rule Index</label>
<input id='idx0' class='form-control col-sm-1' type='number' min='-1' value='0'>
<!--==========[4]-->
<div class="btn-group col-sm-4">
<!--=====[5]-->
<input id='ins0' class='btn btn-primary' type='button' value='Insert Rule' onclick='modRule(this)'>
<!--======[6]-->
<input id='del0' class='btn btn-danger' type='button' value='Delete Rule' onclick='modRule(this)'>
</div>
</div>
</fieldset>
</section>
<hr><br>
</form>
<hgroup class='x-inline'>
<!--====================================[hgroup.x-inline]-->
<h1 class='h1'>CSSStyleSheet</h1>
<h2 class='h2'>.insertRule()</h2>
</hgroup>
<article class='text-primary'>
<blockquote class='blockquote'>
<h3 id="Restrictions" class='h3'>Restrictions</h3>
<p>CSS stylesheet rule-lists have a number of intuitive and not-so-intuitive <a class="external" href="https://drafts.csswg.org/cssom/#insert-a-css-rule">restrictions</a> affecting how and where rules can be inserted. Violating these will likely
cause an error raised as a <a title="The DOMException interface represents an abnormal event (called an exception) which occurs as a result of calling a method or accessing a property of a web API." href="https://developer.mozilla.org/en-US/docs/Web/API/DOMException"><code>DOMException</code></a> </p>
<!--==========================[ul.x-list | li.x-fade]-->
<ul id='list' class='list-group-flush x-list'>
<li class='list-group-item-text fade x-fade x-item'>If index > number of rules in the stylesheet (the <a title="A CSSRuleList is an (indirect-modify only) array-like object containing an ordered collection of CSSRule objects." href="https://developer.mozilla.org/en-US/docs/Web/API/CSSRuleList"><code>CSSRuleList</code></a>.length),
then aborts with IndexSizeError.</li>
<li class='list-group-item-warning x-item'>If rule cannot be inserted at index 0 due to some CSS constraint, then aborts with HierarchyRequestError.</li>
<li class='list-group-item-danger x-item'>If more than one rule is given in the rule parameter, then aborts with SyntaxError</li>
<li class='list-group-item-text x-fade x-item'>If trying to insert an #import at-rule after a style rule, then aborts with HierarchyRequestError.</li>
<li class='list-group-item-text x-fade x-item'>If rule is #namespace at-rule and list has more than just #import at-rules and/or #namespace at-rules, then aborts with InvalidStateError.</li>
</ul>
</blockquote>
<footer class='blockquote-footer'>
<!--===============================[[cite.x-cite]-->
<cite class='x-cite'><a href='https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/insertRule#Restrictions'>CSSStyleSheet.insertRule() - Wed APIs #Restrictions | MDN</a></cite> </footer>
</article>
</main>
<script>
var cnt = 0;
var form = document.forms[0];
var add = document.getElementById('add');
function modRule(ID) {
var e = window.event;
var i = ID.id.split('').pop();
console.log('ruleIndex: ' + i);
var sheets = document.styleSheets;
var sheet = document.getElementById('sht' + i);
var rulez = document.getElementById('rul' + i);
var index = document.getElementById('idx' + i);
var vSht = parseInt(sheet.value, 10);
var vIdx = parseInt(index.value, 10);
var vRul = rulez.value;
if (e.target.value === 'Delete Rule') {
switch (vSht) {
case 0:
sheets[0].deleteRule(vIdx);
break;
case 1:
sheets[1].deleteRule(vIdx);
break;
case 2:
sheets[2].deleteRule(vIdx);
break;
default:
sheets[0].deleteRule(vIdx);
break;
}
} else if (e.target.value === 'Insert Rule') {
switch (vSht) {
case 0:
sheets[0].insertRule(vRul, vIdx);
break;
case 1:
sheets[1].insertRule(vRul, vIdx);
break;
case 2:
sheets[2].insertRule(vRul, vIdx);
break;
default:
sheets[0].insertRule(vRul, vIdx);
break;
}
} else {
return;
}
}
add.addEventListener('click', addSet, false);
function addSet(e) {
cnt++;
var set = document.querySelector('.set0');
var opt = document.options
var dupe = set.cloneNode(true);
dupe.className = 'set' + cnt;
var fields = Array.from(dupe.querySelectorAll('[id]'));
var ids = fields.map(function(ID, idx) {
var zero = ID.id.lastIndexOf("0");
ID.id = ID.id.slice(0, zero);
ID.id = ID.id + cnt;
if (ID.id === 'rul' + cnt) {
ID.textContent = 'p {color:red}';
}
console.log('id: ' + ID.id + ' val: ' + ID.value);
return ID.value;
});
form.appendChild(dupe);
}
</script>
</body>
</html>
Update
If the stylesheet is big, wouldn't it be more efficient to dynamically create a new stylesheet for them?
Yes, but not an external <link> it's far more efficient and a ton more easier to dynamically manipulate a <style> block at the bottom of the </head>.
Not only is it simple, it's powerful since it's at the bottom of the cascade it'll override anything from an external <link>.
Also another thing to consider is that a HTTP request isn't needed unlike an external file which requires it and that' adds to your website's latency.
Demo 3 features 3 functions:
injectCSS() will be called mere microseconds before it's counterpart injectJS() at window.onload. It will create a <style> block within the </head> along with whatever styles we want initially.
injectJS() loads after injectCSS() because as a general rule style should always load before script. It will create a <script> tag and append it as the last child of the <body> tag specifically right before the closing </body> tag. Just like injectCSS() it may have anything within its tags that's script.
inject() calls both injectCSS() and injectJS() asynchronously to ensure that the former will always load before the latter.
As far as this demo relates to the OP, injectCSS() is the function that we should concern ourselves with as was already explained previously.
Details are commented in the demo
For faster loading time, please review the PLUNKER instead.
Demo 3
<!DOCTYPE html>
<html>
<head>
<style>
html {
font: 400 100%/1 Consolas;
}
html,
body {
height: 100%;
width: 100%;
}
main {
height: auto;
width: 100%;
padding: 10px;
}
section {
height: auto;
width: 100%;
padding: 10px
}
fieldset {
min-width: 70%;
margin: 20px 0;
padding: 10px;
}
var {
background: rgba(0, 0, 0, .7);
color: lime;
}
</style>
</head>
<body>
<h1></h1>
<main>
<p><var>injectCSS =</var> All paragraphs will be red</p>
<section>
<form id='test0'>
<fieldset>
<legend>Test Log</legend>
<label for='msg0'>injectCSS()...:
<output id='msg0'></output></label>
<br>
<label for='msg1'>injectJS()....:
<output id='msg1'></output></label>
</fieldset>
</form>
</section>
<section>
</section>
</main>
<footer>
<p>Review this page with Firebug/DevTools and we'll see an extra <style> tag in the <head> and we'll see an extra <script> tag right before the closing <\body> tag.</p>
</footer>
<script>
// HTMLFormControlsCollection★
var x0 = document.forms[0].elements;
var m0 = x0.msg0;
var m1 = x0.msg1;
// Input strings of styles and scripts that are to be injected
var css = "p {color:red}";
var js = "document.querySelector('h1').innerHTML = '<var>injectJS =</var> H1 HTML'";
/* This function manages injectCSS() and injectJS() functions by using
|| the async/await★ keywords. Times are provided by
|| performance.now()★ method.
*/ //* ✎ Delete/add the first * to disable/enable this version of inject().
// The proceeding edit ✎ must be done as well.
var inject = async function() {
var wait0 = injectCSS.call(this, css);
var wait1 = injectJS.call(this, js);
m0.value = performance.now();
var init1 = await wait1;
m1.value = performance.now()
return false;
};
/*/// ✎ Delete/add the first / to enable/disable this version of inject().
// The previous edit ✎ must be done as well.
var inject = function() {
injectCSS.call(this, css);
m0.value = performance.now();
injectJS.call(this, js);
m1.value = performance.now()
return false;
};
/* These 2 functions do the same thing but with different content.
|| They could be refactored into one function but I made them
|| separately to show injectCSS() sepatately for QA SO46985099.
|| Both creates a tag, then writes the code in it, and then appends
|| it to DOM.
*/
function injectCSS(style) {
var sty = document.createElement("style");
sty.innerHTML = style;
document.querySelector('head').appendChild(sty);
}
function injectJS(script) {
var scr = document.createElement("script");
scr.innerHTML = script;
document.body.appendChild(scr);
}
/* The main function inject() is called at window.load. This is the last
|| loading event possible in which we are able call our function. This
|| ensures that specific CSS is loaded before specific JS is called.
|| This is the last step in the loading process, therefore there should be
|| no more styles to render or script that blocks.
*/
window.onload = inject;
</script>
</body>
</html>
Demo Outline
Collects all <link> and <style> into a styleSheetList using document.stylesheets
Converts it into an array called sheetArray with Array.from()
The target is the 3rd <link> which is sheetArray[2]
Then there are 2 rules inserted at index 0 and index 1 successfully.
This is done through a for loop and arrays as the dynamic parameters and interpolation of Template Literals.
This demo does not function on SO, go to PLUNKER for a functioning demo.
Note: In the demo content is an excerpt from MDN that defines the restrictions of insertRule(). The highlighted items may apply to your specific errors.
Demo 1 - index.html [Review PLUNKER for a working demo]
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<link href='https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css' rel='stylesheet'>
<link href="style.css" rel="stylesheet">
<link href='cssom.css' rel="stylesheet">
</head>
<body>
<main class='container'>
<hgroup class='x-inline'>
<!--====================================[hgroup.x-inline]-->
<h1 class='h1'>CSSStyleSheet</h1>
<h2 class='h2'>.insertRule()</h2>
</hgroup>
<article class='text-primary'>
<blockquote class='blockquote'>
<h3 id="Restrictions" class='h3'>Restrictions</h3>
<p>CSS stylesheet rule-lists have a number of intuitive and not-so-intuitive <a class="external" href="https://drafts.csswg.org/cssom/#insert-a-css-rule">restrictions</a> affecting how and where rules can be inserted. Violating these will likely
cause an error raised as a <a title="The DOMException interface represents an abnormal event (called an exception) which occurs as a result of calling a method or accessing a property of a web API." href="https://developer.mozilla.org/en-US/docs/Web/API/DOMException"><code>DOMException</code></a> </p>
<!--==========================[ul.x-list | li.x-fade]-->
<ul class='list-group-flush x-list'>
<li class='list-group-item-text x-fade'>If index > number of rules in the stylesheet (the <a title="A CSSRuleList is an (indirect-modify only) array-like object containing an ordered collection of CSSRule objects." href="https://developer.mozilla.org/en-US/docs/Web/API/CSSRuleList"><code>CSSRuleList</code></a>.length),
then aborts with IndexSizeError.</li>
<li class='list-group-item-warning'>If rule cannot be inserted at index 0 due to some CSS constraint, then aborts with HierarchyRequestError.</li>
<li class='list-group-item-danger'>If more than one rule is given in the rule parameter, then aborts with SyntaxError</li>
<li class='list-group-item-text x-fade'>If trying to insert an #import at-rule after a style rule, then aborts with HierarchyRequestError.</li>
<li class='list-group-item-text x-fade'>If rule is #namespace at-rule and list has more than just #import at-rules and/or #namespace at-rules, then aborts with InvalidStateError.</li>
</ul>
</blockquote>
<footer class='blockquote-footer'>
<!--===============================[[cite.x-cite]-->
<cite class='x-cite'><a href='https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/insertRule#Restrictions'>CSSStyleSheet.insertRule() - Wed APIs #Restrictions | MDN</a></cite> </footer>
</article>
</main>
<script>
var sheets = document.styleSheets;
var sheetArray = Array.from(sheets);
var sel = ['ul.x-list', 'li::before'];
var dec = [`margin-left: 0; padding-left: 1em; text-indent: -1em;`, `content: '🔹';`];
var idx = [0, 1];
var qty = idx.length;
for (let r = 0; r < qty; r++) {
sheetArray[2].insertRule(`${sel[r]} {${dec[r]}}`, idx[r]);
}
</script>
</body>
</html>
Deno 2 - cssom.css
/*0*/
hgroup.x-inline {display:flex; justify-content:center; align-items: center;}
/*1*/
ul.x-list.x-list {list-style: none;}
/*2*/
li.x-fade.x-fade {color:rgba(0,0,0,.3);}
/*3*/
cite.x-cite.x-cite {position:relative; left:60%}
/*4*/
cite.x-cite.x-cite::before {content:"\2014 \00A0"}
/*5*/
.blockquote-footer::before {content: '';}
/*6*/
li.list-group-item {line-height:1.5}
/*7*/
a {text-shadow: 2px 5px 3px rgba(192,192,192,.8)}

How to use the jQuery Selector in this web application?

I am trying to work out to select a delete icon in my own web application. delectIcon
HTML
<main>
<div class="container">
<div class="tabs">
<p><span class="active">Newest</span></p><a href=""><p>
<span>Oldest</span></p></a><p><span>Add</span></p>
</div>
<div class="content">
<ul>
<li>
<span class="itemLeft">Answer emails</span>
<span class="itemMiddle">12-31-2016</span>
<span class="itemRight">1</span>
<b class="deleteIcon"> X </b>
</li>
<li>
<span class="itemLeft">Prep for Monday's class</span>
<span class="itemMiddle">12-31-2016</span>
<span class="itemRight">5</span>
<b class="deleteIcon"> X </b>
</li>
</ul>
</div>
</div>
</main>
JavaScript
$(".deleteIcon").on("click", function () {
alert("Oh, clicked!");
return false;
});
I failed to do so by writing it myself. So I used Chrome Web Developer Tool to find the CSS path. I tried to use the XPath($"[/html/body/main/div/div[2]/ul/li[ 1 ]/b]") and CSS Path ($"(pathbody > main > div > div.content > ul > li:nth-child(1) > b)"). Neither of them worked.
I tried to mark it with an ID and made only one "li" exists. The CSS selector worked all right. But when I clicked the deleteIcon$"(#deleteIcon)", nothing happened.
#deleteIcon{
float:right;
font-weight: bold;
padding: 0 3px 0 3px;
border-radius: 5px;
background: #ccc;
cursor: pointer;
margin-left: 5px;
font-size: 1.3em;
text-align: center;
}
I also tried to select my title. I found the following worked out.
$(".container h1").on("click", function () {
alert("Oh, no!");
return false;
});
I do not what to do now. Can anyone help me out here?
Thank you! I would be really appreciate if you can answer my question.
Adding more details:
I did actually add the deleteIcon into the HTML by JavaScript. I do not know whether this can have an effect on my selector.
Actual HTML
<main>
<div class="container">
<div class="tabs">
<p><span class="active">Newest</span></p><a href=""><p>
<span>Oldest</span></p></a><p><span>Add</span></p>
</div>
<div class="content">
</div>
</div>
</main>
JavaScript (The important part listed below)
function Item(name,dueDate,type){
this.name=name;//1
this.dueDate=dueDate;//input2
this.type=type;//3
};
$(".tabs a span").toArray().forEach(function (element) {
var $element = $(element);
// create a click handler for this element
$element.on("click", function () {
var $content,
$input,
$button,
i;
if ($element.parent().parent().is(":nth-child(1)")) {
// newest first, so we have to go through
// the array backwards
$content = $("<ul>");
for (i = Task.length-1; i >= 1; i--) {
// $buttondelete = $("<buttonDelete>").text("X");
var txt1 = Task[i].toStringName();
var txt2 = Task[i].toStringDate();
var txt3 = Task[i].toStringType();
//alert(txt3);
$content.append('<li> <span class="itemLeft">'+txt1+'</span> <span class="itemMiddle">'+txt2+'</span> <span class="itemRight">'+txt3+'</span><b class="deleteIcon"> X </b>');
}
}
$("main .content").append($content);
return false;
});
});
If you are creating the items inside ul dynamically you should bind the click event like this :
$(".content").on("click", ".deleteIcon", function()
{
alert("clicked") ;
return false;
}
) ;
The class selector starts with a . (just like the example you say you have that works).
Try
$(".deleteIcon").on("click", function () {
alert("Oh, clicked!");
return false;
});

How to make UI more responsive for other screen sizes?

I have an html page in which I have a textbox (Type your text) and TextArea list. I need to type into the textbox and then click Add button so that whatever is there in textbox goes to my TextArea list. I need to type in this below format in the textbox.
Name=Value
This textbox will be used by the user to quickly add Name Value pairs to the list which is just below that textbox. let's say if we type Hello=World in the above textbox and click add, then in the below list, it should show as
Hello=World
And if we again type ABC=PQR in the same textbox, then in the below list, it should show like this so that means it should keep adding new Name Value pair just below its original entry.
Hello=World
ABC=PQR
But if the syntax is incorrect like if it is not in Name=Value pair then it should not add anything to the list and instead show a pop up that wrong input format. Names and Values can contain only alpha-numeric characters. I also have three more buttons Sort by name, Sort by value and Delete button. Once I click either of these buttons, then it should sort entries in TextArea list using either name or value and delete entries as well. Now I have all above things working fine without any issues.
Here is my jsfiddle. I need to use plain HTML, CSS and Javascript, I don't want to use any library yet as I want to keep it simple as I am still learning. Now I am trying to see whether we can make UI more responsive like the UI should adjust based on what screen size is viewing it. For example, if viewed on a mobile phone (i.e. Android or iPhone), the page should automatically adjust to present the layout in a better way. This also applies to re-sizing the browser on desktop, and viewing the page on a tablet.
What are the changes I need to make in my CSS or HTML to make it more responsive? Any improvements I can make here? Since my UI is very simple so there should be some easy way or some improvements I can make here.
Below is my code:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Test</title>
<style type="text/css">
.main{
background:white;
padding: 35px;
border-radius: 5px;
}
#my-text-box {
font-size: 18px;
height: 1.5em;
width: 585px;
}
#list{
width:585px;
height:300px;
font-size: 18px;
}
.form-section{
overflow:hidden;
width:700px;
}
.fleft{float:left}
.fright{float:left; padding-left:15px;}
.fright button{display:block; margin-bottom:10px;}
html, body {
height: 100%;
font-family: "Calibri";
font-size: 20px;
}
html {
display: table;
margin: auto;
}
body {
display: table-cell;
vertical-align: middle;
background-color: #5C87B2;
}
</style>
<script language="javascript" type="text/javascript">
document.getElementById('add').onclick = addtext;
function addtext() {
var nameValue = document.getElementById('my-text-box').value;
if (/^([a-zA-Z0-9]+=[a-zA-Z0-9]+)$/.test(nameValue)){
var x = document.getElementById("list");
var option = document.createElement("option");
option.text = nameValue;
x.add(option);
}
else
alert('Incorrect Name Value pair format.');
}
document.getElementById('btnDelete').onclick = deleteText;
function deleteText(){
var myList = document.getElementById('list');
var i;
for (i = myList.length - 1; i>=0; i--) {
if (myList.options[i].selected) {
myList.remove(i);
}
}
}
document.getElementById('sortByValue').onclick = sortByValue;
function sortByValue(){
var myList = document.getElementById('list');
var values = new Array();
for (var i=0;i<myList.options.length;i++) {
values[i] = myList.options[i].text;
}
values.sort(function(a, b){
if(a != "" && b != ""){
return a.split('=')[1].localeCompare(b.split('=')[1])
} else {
return 0
}
});
clearList(myList);
fillList(myList, values);
}
document.getElementById('sortByName').onclick = sortByName;
function sortByName(){
var myList = document.getElementById('list');
var values = new Array();
for (var i=0;i<myList.options.length;i++) {
values[i] = myList.options[i].text;
}
values.sort(function (a, b){
if(a != "" && b != ""){
return a.split('=')[0].localeCompare(b.split('=')[0])
} else {
return 0
}
});
clearList(myList);
fillList(myList, values);
}
function clearList(list) {
while (list.options.length > 0) {
list.options[0] = null;
}
}
function fillList(myList, values){
for (var i=0;i<values.length;i++) {
var option = document.createElement("option");
option.text = values[i];
myList.options[i] = option;
}
}
</script>
</head>
<body>
<div class = 'main'>
<h3>Test</h3>
<label for="pair">Type your text</label></br>
<div class="form-section">
<div class="fleft">
<input type='text' id='my-text-box' value="Name=Value" />
</div>
<div class="fright">
<button type="button" id='add' onclick='addtext()'>Add</button>
</div>
</div>
<label for="pairs">Name/Value Pair List</label></br>
<div class="form-section">
<div class="fleft">
<select id="list" multiple></select>
</div>
<div class="fright">
<button type="button" id='sortByName' onclick='sortByName()'>Sort by name</button>
<button type="button" id='sortByValue' onclick='sortByValue()'>Sort by value</button>
<button type="button" id='btnDelete' onclick='deleteText()'>Delete</button>
<button type="button">Show XML</button>
</div>
</div>
</div>
</body>
</html>
W3 have a number of resources on responsive web design:
http://www.w3schools.com/html/html_responsive.asp
http://www.w3schools.com/css/css_responsive_intro.asp
Without using PHP to detect the browser/user agent, your responsive design will typically involve ensuring the site is more fluid and flowing, allowing for changing browser widths (as in the first example above) and/or by delivering differing stylesheets depending on the viewport size and media type in CSS (second example).

CSS Lines Appearing

Edit: the issue seems to only appear on OSX Mavericks w/ Latest Google Chrome (for me)
I have an event log that posts messages from the top down, and with every message, small black lines are appearing at the bottom right of each message and I can't figure out why.
Here is a working version of my game, click "Hunt for Blood" and when a few event log messages stack up, you'll see what I'm talking about.
http://codepen.io/RUJordan/pen/dcwLC
Here's a picture as well:
Here is my CSS relevant to the log div and msg div
.msg {
float: left;
width:auto;
overflow:auto;
padding: 5px;
font-size: small;
}
.column {
padding:3px;
float: left;
width:30%;
border:1px solid black;
background-color:#222222;
} /* Hidden Elements */
.hp, .cycle, .gold, .log, .middleCol,
.battle, .hiddenCounter {
display:none;
}
And here is my HTML schema.
<!DOCTYPE html>
<html>
<head>
<title>A Vampire's Hunt</title>
<link rel="stylesheet" href="vamp.css">
</head>
<body>
<h1 class="title">A Vampire's Hunt</h1>
<div class="main">
<div id="stats" class="column">
<div>
<h3 class="miniTitle">Stats</h3>
<hr />
<span id="spanCounter" class="hiddenCounter noRed">You have been dead for <span id="counter">0</span> hour<span id="singularHours" class="noRed"></span>..</span>
<span id="spanInitMsg" class="spanInitMsg noRed">You are dead!</span>
</div>
<div id="divCycle" class="cycle">It is currently: <span id="cycle"></span></div>
<div>Blood: <span id="blood">0</span></div>
<div class="hp" id="hpDiv">HP: <span id="hp">20</span></div>
<div class="gold" id="goldDiv">Gold: <span id="gold">0</span></div>
<h3 class="miniTitle">Actions</h3>
<hr />
</div>
<div id="middleCol" class="column middleCol">
<div id="shop" class="shop">
<h4 class="miniTitle">A Dark Alleyway</h4>
<hr />
Herp Derp Derp
</div>
<div id="battle" class="battle">
<hr />
</div>
</div>
<div id="log" class="log column">
<h3 class="miniTitle">Event Log</h3>
<hr />
<div id="msg" class="msg"></div>
</div>
</div>
<script src="player.js"></script>
<script src="element.js"></script>
<script src="engine.js"></script>
<script src="vampire.js"></script>
<div class="footer">
Follow This Project on Github!
</div>
</body>
</html>
I do not think the JavaScript is the culprit, but just in case, here is the event log function, along with the functions it calls.
eventMsg : function(txt) {
this.addBorder("log");
this.showElement("log","block");
var msg = document.getElementById("msg");
txt = "-"+txt+"<br />"+msg.innerHTML;
msg.innerHTML = txt;
},
addBorder : function(id) {
document.getElementById(id).style.border = "1px solid black";
},
showElement : function(id,style) {
document.getElementById(id).style.display = style;
},
This appears to work on FireFox and Safari, but not on Chrome.
Try using display: inline-table on div id "log". Note that it uses inline CSS that is reset on each click, so you'll have to overwrite this, otherwise it won't work.
EDIT : display: table should work too.
The lines seem to appear because you had border-width: 0 0 1px 0; instead of this:
hr:before {
border-width: 0;
}
Although, I have not tested on other browsers, but it seems to work with chrome.
Chrome Version 31.0.1650.57 m
fixed the line issue by changing:
eventMsg : function(txt) {
this.addBorder("log");
this.showElement("log","block");
var msg = document.getElementById("msg");
txt = "-"+txt+"<br />"+msg.innerHTML;
msg.innerHTML = txt;
},
addBorder : function(id) {
document.getElementById(id).style.border = "1px solid black";
},
showElement : function(id,style) {
document.getElementById(id).style.display = style;
},
to:
eventMsg : function(txt) {
this.showElement("log","block");
var msg = document.getElementById("msg");
txt = "-"+txt+"<br />"+msg.innerHTML;
msg.innerHTML = txt;
},
addBorder : function(id) {
document.getElementById(id).style.border = "1px solid black";
},
showElement : function(id,style) {
document.getElementById(id).style.display = style;
},
Have another potential fix for you, change the css from:
.msg {
float: left;
width:auto;
overflow:auto;
padding: 5px;
font-size: small;
}
to
.msg {
float: left;
width:100%;
overflow:auto;
padding: 5px;
font-size: small;
}

how can I dynamically resize an html textbox to avoid overflowing?

I would like to know how to resize a textbox to prevent text nearby it from overflowing.
I have 3 elements in a row, a label, a text box, and a button. the label however, can have words of varying lengths. if the word is too long it will move the text input too far to the side and the button will overflow onto the next line. to preserve the style of the page, I would prefer that the button stays on the same line as the other 2 elements.
I am trying to get the text box to shrink only as much as necessary to allow room for the other elements.
can I do this with JQuery?
Edit: here's the JFiddle thing:
http://jsfiddle.net/425ve/2/
and here's the main code:
<html>
<head>
<style>
body{
background-color:#000000;
color:#cccccc;
}
#chatbox{
width:100%;
height:85%;
border-style:solid;
border-color:#000000;
overflow:auto;
}
#mainchat{
width:82%;
float:left;
margin:0;
}
#sidebar{
float:left;
height:97%;
width:17%;
border-style:dashed;
border-width:1px;
border-color:#AAAAAA;
border-right:0;
border-top:0;
border-bottom:0;
overflow:auto;
}
#topbar{
border-style:dashed;
border-width:1px;
border-color:#AAAAAA;
border-left: 0;
border-top: 0;
float:left;
width:82%;
}
a{
color:#cccccc;
text-decoration:none;
}
a:hover{
color:#CCCCEE;
background-color:111122;
}
#topbarname{
float:right;
}
#message{
width: 90%;
background-color:#000000;
border-color:#CCCCCC;
border-style:solid;
border-width: 1px;
color:CCCCCC;
}
#submitbutton{
background-color:#000000;
border-color:#CCCCCC;
border-style:solid;
border-width: 1px;
color:#CCCCCC;
}
</style>
<script>
function getCookie(name) {
var dc = document.cookie;
var prefix = name + "=";
var begin = dc.indexOf("; " + prefix);
if (begin == -1) {
begin = dc.indexOf(prefix);
if (begin != 0) return null;
}
else
{
begin += 2;
var end = document.cookie.indexOf(";", begin);
if (end == -1) {
end = dc.length;
}
}
return unescape(dc.substring(begin + prefix.length, end));
}
function doSomething() {
var myCookie = getCookie("IceID");
if (myCookie == null) {
window.location="login.php"
}
else {
// do cookie exists stuff
}
}
doSomething();
</script>
</head>
<body>
<div id="topbar">
| Information | Logs | characters | Profile | Private logs | Messages | Logout |
</div>
<div id="mainchat">
<div id="chatbox">
<?php
include("getpost.php");
//improve this with AJAX!
?>
</div>
<div id="input">
<form id="inputchat">
<b id="name">
<?php
echo $_COOKIE['IceID'];
?>
</b>
<input type="text" name="message" id="message"></input>
<input type="submit" id="submitbutton" value="say"></input>
</form>
</div>
<div id="utools">
</div>
</div>
<div id="sidebar">
<div id="title">
A
</div>
<div id="list">
</div>
</div>
</body>
</html>
Edit:to clarify, the name doesn't actively change while the page is being used(only right before being displayed), but it will be different depending on who loads the page. their username fits into that label.
You don't need jQuery. jQuery could make it much simpler though. I prefer vanilla.
var left = document.getElementById('name')
var resizable = document.getElementById('message')
var right = document.getElementById('submitbutton')
realign()
window.addEventListener('resize', realign)
function realign() {
resizable.style.width = '0'
var extraWidth = getWidth(resizable) // Measure the border and padding on it's own.
resizable.style.width = getWidth(resizable.parentNode) - getWidth(left) - getWidth(right)
function getWidth(element) { // Superior to offsetWidth because it measures fractions of a pixel which is even more relevant when using the browser zoom feature.
var rect = element.getBoundingClientRect() // A accurate way to measure location on the screen.
return rect.right - rec.left // The accurate width.
}
}
The only adjustment you need would be to fix my typo(s) if I made any and then if you want to support older versions of IE, you need to use the alternative to addEventListener, Google it.

Categories

Resources