While trying to create a rich text editor, I cannot seem to add the js script for the buttons and form. Right now, most of the functions are inlined in the jade pages, but that is quite unelegant.
I have a layout.jade file:
doctype html
html
head
body
header
section.content
block content
And the create.jade script, where I want the rich text editor:
extends ../layout
block content
script(type='text/javascript', src='./css/js/wiz.js')
form(method="post", action=post ? '/post/edit/' + post.id : '/post/create', onload="iFrameOn()")
ul
p
input#title(name="title", placeholder="Title", value=post ? post.title : '', autocomplete='off')
p
#wysiwyg_cp(style="padding:8px; width:700px")
input(type="button", onClick="iBold();", value="B")
input(type="button", onClick="richTextField.document.execCommand(\"underline\",false,null);", value="U")
p
textarea#blogPostBody(style="display:none", name="blogPostBody", placeholder="Blog post text", rows="20", cols="50")
iframe#richTextField(name="richTextField", contentEditable="true", onLoad="richTextField.document.designMode = 'On';")
if(post)
| #{post.body}
else
| 	
p
input(onClick="javascript:submit_form();", type="button", name="myBtn", value="Save")
The structure of the project looks like:
Proj
- css
-- js
--- wiz.js
- middleware
- node_modules
- public
- routes
- views
-- post
--- create.jade
-- layout.jade
-- home.jade
-- login.jade
- app.js
- package.json
When trying to open the create section of my blog, I get the following error message, as can be seen in the image below:
Uncaught SyntaxError: Unexpected token <
See chrome stack: http://i.stack.imgur.com/JyHs2.png
The wiz.js file looks like this:
function iFrameOn() { richTextField.document.designMode = 'On'; }
function iBold() { richTextField.document.execCommand("bold",false,null); }
function iUnderline(){ richTextField.document.execCommand("underline",false,null); }
function iItalic(){ richTextField.document.execCommand("italic",false,null); }
function iImage() {
var imgSrc = prompt("EnterImageLocation", '');
if(imgSrc!=null) {
richTextField.document.execCommand("insertimage",false,imgSrc);
}
}
function submit_form() {
var theForm = document.getElementById("myForm");
theForm.elements("myTextArea").value = window.frames['richTextField'].document.body.innerHTML;
theForm.submit();
}
I have also tried adding
app.set('view options', {locals:{scripts:['wiz.js']}});
and/or adding
app.use(express.static("./css/js"));
In the app.js file or in the middleware.
But these did not help. The problem seem to be in the "create.jade" script, when referencing the 'text/javascript' script, because the same error was obtained even when referencing a non-existant js file.
Could anyone have any idea what the problem could be?
[EDIT] SOLUTION that was implemented:
script
include ./../../css/js/wiz.js
The following snippet of code worked:
script
include ./../../css/js/wiz.js
Express's static middleware will by default mount the files to the root. So, if you include the middleware as above, the wiz.js file will be accessible at "/wiz.js" when your server's running -- this is the path you should put in the src attribute of your script tag in the jade file, not the local path (since local files are not accessible to the client unless they are served). If you want to change the mountpath (so, for example, that the file will be accessible on "/js/wiz.js", rather than the root), you can add it as a first argument, like below:
var localDirectoryPath = "./css/js";
// Or if you happened to want an absolute path
// (though it doesn't make a difference here):
// var localDirectoryPath = require("path").join(__dirname, "css", "js");
app.use("/js", express.static(localDirectoryPath));
The temporary solution that you've found (using Jade's include function) is merely loading the file's contents on the server (when Jade compiles the template) and inserting the code in between script tags on your page, which works but is certainly not ideal, since it will slow down the initial loading of your HTML, especially as the wiz.js file gets bigger.
And I won't even ask why your javascript directory is inside your css directory!
Related
I'm new to Web Development (including JavaScript and HTML) and have a few issues within my personal project that seem to have no clear fixes.
Overview
My project is taking input from a user on the website, and feeding it to my back-end to output a list of word completion suggestions.
For example, input => "bass", then the program would suggest "bassist", "bassa", "bassalia", "bassalian", "bassalan", etc. as possible completions for the pattern "bass" (these are words extracted from an English dictionary text file).
The backend - running on Node JS libraries
trie.js file:
/* code for the trie not fully shown */
var Deque = require("collections/deque"); // to be used somewhere
function add_word_to_trie(word) { ... }
function get_words_matching_pattern(pattern, number_to_get = DEFAULT_FETCH) { ... }
// read in words from English dictionary
var file = require('fs');
const DICTIONARY = 'somefile.txt';
function preprocess() {
file.readFileSync(DICTIONARY, 'utf-8')
.split('\n')
.forEach( (item) => {
add_word_to_trie(item.replace(/\r?\n|\r/g, ""));
});
}
preprocess();
module.exports = get_words_matching_trie;
The frontend
An HTML script that renders the visuals for the website, as well as getting input from the user and passing it onto the backend script for getting possible suggestions. It looks something like this:
index.html script:
<!DOCTYPE HTML>
<html>
<!-- code for formatting website and headers not shown -->
<body>
<script src = "./trie.js">
function get_predicted_text() {
const autofill_options = get_words_matching_pattern(input.value);
/* add the first suggestion we get from the autofill options to the user's input
arbitrary, because I couldn't get this to actually work. Actual version of
autofill would be more sophisticated. */
document.querySelector("input").value += autofill_options[0];
}
</script>
<input placeholder="Enter text..." oninput="get_predicted_text()">
<!-- I get a runtime error here saying that get_predicted_text is not defined -->
</body>
</html>
Errors I get
Firstly, I get the obvious error of 'require()' being undefined on the client-side. This, I fix using browserify.
Secondly, there is the issue of 'fs' not existing on the client-side, for being a node.js module. I have tried running the trie.js file using node and treating it with some server-side code:
function respond_to_user_input() {
fs.readFile('./index.html', null, (err, html) => {
if (err) throw err;
http.createServer( (request, response) => {
response.write(html);
response.end();
}).listen(PORT);
});
respond_to_user_input();
}
With this, I'm not exactly sure how to edit document elements, such as changing input.value in index.html, or calling the oninput event listener within the input field. Also, my CSS formatting script is not called if I invoke the HTML file through node trie.js command in terminal.
This leaves me with the question: is it even possible to run index.html directly (through Google Chrome) and have it use node JS modules when it calls the trie.js script? Can the server-side code I described above with the HTTP module, how can I fix the issues of invoking my external CSS script (which my HTML file sends an href to) and accessing document.querySelector("input") to edit my input field?
I am trying to create a simple recipe-app with javascript.
I have 3 HTML files: index.html, create.html, edit.html
and 2 JS files.
I have a couple of DOM elements. One on index.html, Two on edit, Two on create.
Now when I open index.html I get an error:
recipe.js:14 Uncaught TypeError: Cannot set property 'value' of null
The problem here is that the code that the error is it's about edit.html not index.html.
Same problem on edit.html
Uncaught TypeError: Cannot read property 'appendChild' of null
at recipe.js:55
recipe.js code 55 is about index.html, not edit.html.
Is there a way to target an HTML file when using DOM.
document.getElementById("edit-body").value = getRecipeBody();
I want the code above to be only for edit.html, not for index.html or create.html.
edit-body is a form that is only on edit.html, not on index or create.html
document.getElementById('recipes').appendChild(recipeEl)
I want this code to be for index.html not for other HTML files because there is no #recipes ID on those files. The #recipes is only on index.html
I am using localStorage.
window.location.pathname would let you execute code for a specific path.
if(window.location.pathname === '/edit.html') {
document.getElementById("edit-body").value = getRecipeBody();
}
Note that if you load index.html by default (as in, http://localhost:80/ as opposed to http://localhost:80/index.html), then the pathname will simply be /, so make sure you handle both of those, i.e.:
if(window.location.pathname === '/index.html' || window.location.pathname === '/') {
document.getElementById('recipes').appendChild(recipeEl)
}
A better approach would be to code it defensively. Check that the element you're getting exists before running a function against it. For example:
var editEl = document.getElementById("edit-body");
if (editEl) {
editEl.value = getRecipeBody();
}
Instead of either of these, you could also just load index.js on index.html, edit.js on edit.html, etc., though you'd want to split out any shared functions into a separate (common) JS file.
I have a Node.JS/Express app that uses the Jade template engine. I have a Javascript file that is in the directory immediately above my views directory that contains my Jade views. Given the code below, I believe that the view should be able to find referenced server-side Javascript file named common_routines, but when the view is requested I get a 404 error saying that the external Javascript file could not be found. This confuses me because I thought Jade templates execute server side, so why would this trigger a 404 error from the Jade render() call?
Here is the Jade view:
block content
script(src="../common_routines.js")
div(style="padding:5px")
h4 #{product.name}
if (product.brand != null)
p Brand: #{product.brand}
div(class="img_and_desc")
img(src=product.imageUrl, class="product_image", align="left")
if (product.minimumPrice == product.maximumPrice)
p Price: #[strong #{product.minimumPrice}]
else
p Price: #[strong #{product.minimumPrice}] to #[strong #{product.maximumPrice}]
if (product.cashBack > 0)
p Cash Back amount: #[strong #{product.cashBack}]
if (product.freeShipping)
p(class="text-emphasized-1") *Free Shipping!
if (product.onSale)
p(class="text-emphasized-2") Discounted Item
Here is a snippet of the call from the route file that renders the view, where the if (err) branch is triggered:
res.render(
'ajax-product-details',
locals,
function (err, html) {
if (err)
// A rendering error occurred.
throw new Error("Rendering error(type:" + err.type + ", message: " + err.message);
else {
// Success. Send back the rendered HTML.
res.send(html);
return;
}
});
Here is the error I see in the WebStorm debugger Console window pane. Notice that it shows the file reference as /common_routines.js without the preceding ".." relative directory reference, despite the fact that preceding ".." relative directory reference is plainly visible in the Jade file reference ("script(src="../common_routines.js")):
[Express][127.0.0.1][GET][DESKTOP] 2016-07-20T10:07:34.940Z /common_routines.js?_=1469009245957
(development error handler) Status code(404) message: Not Found
Here is the common_routines.js content:
function truncateString(str, maxLen)
{
if (str.length > maxLen)
return str.substring(0, maxLen - 1) + '&hellip';
return str;
}
module.exports =
{
truncateString: truncateString
}
As I said, common_routines.js is in the directory immediately above the Jade view, so is this external file reference invalid for some reason?:
script(src="../common_routines.js")
You can only reference static files that are in your public folder(if you have set your static assets to be served from that folder, as you have), on your front-end side. You can put your common_routines.js file in a folder inside public e.g. public/js/.
Now, you can reference this in your view file using script(src="/js/common_routines.js")
In case, you want to use the functions in your jade file, not on front-end js files, you need to include that as a package using var commonRoutines = require('../common_routines.js'); inside your server side js file. Now, you can pass this object variable along with your context object to your view file. As you stated, you pass locals object to your view file while rendering, you can do it like:
var common_routines = require('../common_routines.js');
var locals = { common_routines: common_routines };
res.render( 'ajax-product-details', locals);
Now, you can use functions from common_routines.js in your ajax-product-details file.
We can modify the document root directory path for PHP using
$_SERVER['DOCUMENT_ROOT'] = "to/some/new/directory";
//Now the "/" which represent the ^(above) path
in .htaccess we have
RewriteBase "/to/some/new/directory"
Now, I need to modify the root directory path to use in javascript. How to do it?
Currently, I am declaring a variable containing static path to the my personalized root directory and using it as
var root = "../to/new/path";
document.location = root+"/somepage.php";
Scenario
I think i should tell a little bit about the scenario, for you guys to catch my idea
Default Web Root Directory
http_docs/
inside it contain a main folder
http_docs/application <-- contains the actual application
http_docs/js <-- contains the script
http_docs/index.html
Now, the application also contains ajax feature for updating, editing, loading new content, or other resources, which if accessed at "/" will represent at /some/path/i/called not /application/some/path/i/called,
To come around this problem
I can define a static variable like
var root = "application/";
and use it somewhere like
$.post(....., function(data) { $(body).append("<img src='"+root+"resources/img1.jpg"); });
But for a single use, defining the path as static, might not be a big deal, but, when the application grows, and certain modification would cause me to change all the paths i give in the js part. I thought, it would be sensible, just like, I do it in PHP, using <img src="/resources/img1.jpg" />
I tried my best to explain this question, if still is not understandable, please community, lets help them understand. I welcome you to edit my question.
EDITED: Trying to answer the updated question
Assuming the JavaScript is called included from the index.html file, if you insert a img tag and use relative urls, they will be relative to the path of the index file. So <img src='application/resources/img1.jpg'> would work just fine. If the script should work for several sublevels (e.g. if the page "application/etc/etc2/somePage.html" needs images from "application/resources/")it may be easier to use absolute urls, and you could include a javascript block on every page generated by php that holds the absolute url to the "root" of the application, like:
<!-- included by php in all html pages, e.g. in defautlHeadter.php -->
<script type="text/javascript">
var rootUrl = "<?= getTheRootUrl() ?>";
</script>
Where getTheRootUrl() is a method or server variable that gives the root url you need. If the url is translated/remapped (by apache etc. outside of what is visible to php) you may need to hardcode the root url in the php method but at least it will be only one file to change if you ever change the root directory.
Then you can use the root url to specify absolute paths anywhere in the application/website using rootUrl + "/some/relative/path" in anywhere in the application.
I once made something like this, to set
window.app_absolute = '<?php echo GetRelativePath(dirname(__FILE__)); ?>'
I also use something like this
static function GetRelativePath($path)
{
$dr = $_SERVER['DOCUMENT_ROOT']; //Probably Apache situated
if (empty($dr)) //Probably IIS situated
{
//Get the document root from the translated path.
$pt = str_replace('\\\\', '/', Server::GetVar('PATH_TRANSLATED',
Server::GetVar('ORIG_PATH_TRANSLATED')));
$dr = substr($pt, 0, -strlen(Server::GetVar('SCRIPT_NAME')));
}
$dr = str_replace('\\\\', '/', $dr);
return substr(str_replace('\\', '/', str_replace('\\\\', '/', $path)), strlen($dr));
}
... Something along those lines, hacked up for demonstration purposes.
is there a method in JavaScript by which I can find out the path/uri of the executing script.
For example:
index.html includes a JavaScript file stuff.js and since stuff.js file depends on ./commons.js, it wants to include it too in the page. Problem is that stuff.js only knows the relative path of ./commons.js from itself and has no clue of full url/path.
index.html includes stuff.js file as <script src="http://example.net/js/stuff.js?key=value" /> and stuff.js file wants to read the value of key. How to?
UPDATE: Is there any standard method to do this? Even in draft status? (Which I can figure out by answers, that answer is "no". Thanks to all for answering).
This should give you the full path to the current script (might not work if loaded on request etc.)
var scripts = document.getElementsByTagName("script");
var thisScript = scripts[scripts.length-1];
var thisScriptsSrc = thisScript.src;
If your script knows that it's called "stuff.js", then it can look at all the script tags in the DOM.
var scripts = document.getElementsByTagName('script');
and then it can look at the "src" attributes for its name. Kind-of a hack, however, and to me it seems like something you should really work out server-side.
script.aculo.us (source) solves a similar problem. here is the relevant code
var js = /scriptaculous\.js(\?.*)?$/;
$$('script[src]').findAll(function(s) {
return s.src.match(js);
}).each(function(s) {
var path = s.src.replace(js, ''),
includes = s.src.match(/\?.*load=([a-z,]*)/);
(includes ? includes[1] : 'builder,effects,dragdrop,controls,slider,sound').split(',').each(
function(include) { Scriptaculous.require(path+include+'.js') });
});
(some parts of this like .each require prototype)