I'm creating a portfolio, and in that portfolio there will be several cards, and the purpose of these cards will be for them to be redirected to another page with detailed information. And that's where the question comes in, instead of creating an html page for each card wouldn't it be advantageous to make a page only, and change the values between each card when clicked on js ?
A buddy told me that there is a rounter property of react, where it is possible to do this, but the project is simple for now, and I would like to solve this issue with js.
Below is a separate example (which is not part of the main project):
Main page:
Aqui
Teste
Page 2
<div id="main">
<h1 id="title">Lindo</h1>
</div>
Js
let a1 = document.getElementById('a1');
var aa = document.getElementById('aa');
var h1 = document.querySelector('#title');
var main = document.getElementById('main');
if (onclick == a1) {
title.innerText = "Leandro O cachorro";
main.style.backgroundColor = 'green';
}else if(onclick == aa){
console.log('era pra estar vermelho');
title.innerText = "Belle Belinha";
main.style.backgroundColor = 'red';
}
I tried to use the if-else sentence, but it only takes the first if, even clicking on link 2
instead of creating an html page for each card wouldn't it be advantageous to make a page only, and change the values between each card when clicked on js ?
Of course, everyone loves that way.
Frankly speaking, it is possible but you can't achieve this with just what you are showing above. I think you can't achieve what you want in that way.
So instead, I will recommend you to use react. In case you don't like it, take a look of alternatives below.
If you consider react is overkill, you can try a simpler solution: static site generator. Check: Hugo, Jekyll, Eleventy, etc... These approaches help you focus on site building instead of architecture and technical building.
If you want a lighter solution and prefer to do more stuff, then pick a bundler like webpack, rollup, parcel, etc... and then apply templating libraries likes Handlebar JS, Mustache JS, Liquid JS, EJS, etc... This approach will give you more chance to do low-level stuff and super lightweight.
Lastly, build everything from scratch. yes, still possible but sure unless you know most of the things and know where to go, and what to do.
The only one trick I could think of for exchanging information between different pages while only using front-end javascript: You can link to the other page using parameters at the end:
I.E.:
myUrl + "?myParameter1=myValue1&myParameter2=myValue2"
Now on that second page you could retrieve those parameters using:
var url = new URL(window.location.href);
url.searchParams.get("myParameter1"); // "myValue1"
url.searchParams.get("myParameter2"); // "myValue2"
//...
...and depending on the parameter(s) passed, you can now display different content on the page, which can even be bookmarked or even shared via the link (as a nice sideffect).
Edit: Sorry, I might have misunderstood your question, but maybe this helps you to find an alternate solution anyway (at least you won't have to make a new html page for EVERY card, just one).
Related
My understanding is that Monaco is optimized for editing, and for having showing one file at a time, with a fixed size editor that has its own scroll bar.
Instead I am trying to build one page with the diffs of multiple files below each other
allowing showing/hiding each file, up to ~100 files
hiding portions of the file that have not changed (and allowing to show them as context if desired)
not have one scrollbar per file, but one for the entire page
the files are usually view-only, but editing should be supported for one file at a time
I realize this is quite the departure from what Monaco was built for, but in the end it seems as if the same viewport and virtual rendering tricks would apply, so maybe it is somehow possible?
I tried creating one Monaco instance per file, but that starts getting really sluggish around 30 instances.
One pretty ugly workaround might be to have a single Monaco instance, concat all the files, and then work with ViewZones, custom line number providers and code folding providers to achieve the impression of multiple files. Is that as crazy as it sounds, or might that actually work?
Any other suggestions? Why does IStandaloneDiffEditor have standalone in the name? Does that mean there is another way to create many diff editors that is more efficient?
Citate from your question:
I tried creating one Monaco instance per file, but that starts getting really sluggish around 30 instances.
The solution for your question
As you mentioned the perfomance was sluggish. This is because your server or may be your client has not enough memory. You have to add more memory to the server or may be to the client for more perfomance. Because I do not have enough information I can not say it is the server or the client. But this method is not efficient.
Citate from your question:
Why does IStandaloneDiffEditor have standalone in the name? Does that mean there is another way to create many diff editors that is more efficient?
Nothing from it. In Wikipedia I found the answer what standalone means:
Standalone software may refer to:
Computer software that can work offline, i.e. does not necessarily require network connection to function.
Software that is not a part of some bundled software.
A program that run as a separate computer process, not an add-on of an existing process.
Standalone program, a program that does not require operating system's services to run.
A portable application, which can be run without the need for installation procedure.
This means that standalone has nothing to do with single instance and you could have multiple instances of this editor. But you have to have more memory on your computer(s) to create 100 instances from this editor. And this it not efficient because you have 100 big JavaScript objects more in your memory.
On other services for display the difference between changed files they make it with DOM objects only or with DOM objects + one big instance from big JavaScript object which creates this objects, but not additional 100 big instances from big JavaScript objects.
Accordingly to this princip in this case you could use code from my recommended solution below and in background cleate only one instance from this difference editor. Then you have to put to this instance all your 100 files one after other and copy in each case from one file following DOM objects:
<div class="editor original showUnused" ...
<div class="editor modified showUnused" ...
This could you do for example with following code:
var diffPartContainars = document.querySelector('#container').querySelectorAll('.showUnused'),
editorOriginalPartHTML,
editorModifiedPartHTML;
for(var i = diffPartContainars.length; i--;)
{
var obj = diffPartContainars[i],
cln = obj.className;
if(cln.indexOf('editor original') > -1)
{
obj.removeAttribute('style');
editorOriginalPartHTML = obj.outerHTML;
}
if(cln.indexOf('editor modified') > -1)
{
obj.removeAttribute('style');
editorModifiedPartHTML = obj.outerHTML;
}
}
Then you have to delete from each editorOriginalPartHTML and editorModifiedPartHTML following DOM objects:
<div class="invisible scrollbar horizontal" ...
<canvas class="decorationsOverviewRuler" ...
<div class="visible scrollbar vertical" ...
and all other objects which you can not use. You can do it when you add editorOriginalPartHTML and editorModifiedPartHTML to your DOM. Then you can add around each from it one div object with suitable width, height and style="overflow:auto". And one thing you could do more: for each from this div objects you could add one onclick or onmouseover listener and then replace this div object view with your difference editor instance.
This is only one way in my opinion to be more efficient. Good luck!
The recommended efficient solution
The quickly, comfortable and efficient way to have only one instance of this editor and load a new sources on the click on the file names like below.
var diffEditor = null;
var filesContent =
{
'SomeJavaScriptFile.js':
{
originalContent: 'alert("heLLo world!")',
modifiedContent: 'alert("hello everyone!")',
type: 'text/javascript'
},
'AnotherJavaScriptFile.js':
{
originalContent: 'function open(str)\n{\n\talert(str)\n}',
modifiedContent: 'function output(value)\n{\n\tconsole.log(value)\n}',
type: 'text/javascript'
}
};
document.querySelector('#files').addEventListener('change', function(e)
{
var fileName = this.options[this.selectedIndex].text,
file = filesContent[fileName];
openInDiffEditor(file);
});
function openInDiffEditor(file)
{
if(!diffEditor)
diffEditor = monaco.editor.createDiffEditor(document.querySelector('#container'));
diffEditor.setModel({
original: monaco.editor.createModel(file.originalContent, file.type),
modified: monaco.editor.createModel(file.modifiedContent, file.type)
});
}
//open the first file in select list:
var firstFileName = document.querySelector('#files').options[0].text;
openInDiffEditor(filesContent[firstFileName]);
<p>Please select one file on the left list to see the file differences after changes.</p>
<select id="files" size="3">
<option selected>SomeJavaScriptFile.js</option>
<option>AnotherJavaScriptFile.js</option>
</select>
<div id="container" style="height:100%;"></div>
And file contents you have to load over AJAX. But in the case if you do not understand how to load it then ask me and I will write it.
The screenshot of this recommended solution
I am trying to make my code more readable in portractor.
I want to store the css class in a variable and need to access that variable on click method.
element.all(by.css("div[ng-click=\"setLocation('report_road')\"]")).click();
element.all(by.css("div[ ng-click=\"mapFeedBack.editObject= mapFeedBack.createMapObjectModel();setLocation(mapFeedBack.noMap?'road_new':'choose_location_road_new/road_new')\"]")).click();
it("test browser should reach report road option",function() //spec2s
{
element.all(by.css("div[ng-click=\"setLocation('report_road')\"]")).click();
expect(browser.getCurrentUrl()).toContain("report_road");
browser.sleep(browser.params.sleeptime);
browser.sleep(browser.params.sleeptime);
});
it("test browser should reach report road missing",function() //spec3
{
element.all(by.css("div[ ng-click=\"mapFeedBack.editObject= mapFeedBack.createMapObjectModel();setLocation(mapFeedBack.noMap?'road_new':'choose_location_road_new/road_new')\"]")).click();
expect(browser.getCurrentUrl()).toContain("choose_location_road_new/road_new");
browser.sleep(browser.params.sleeptime);
browser.sleep(browser.params.sleeptime);
});
So I created two variables in my file :
var road_button ="\"div[ng-click=\"setLocation('report_road')\"]\"";
var road_missing= "\"div[ ng-click=\"mapFeedBack.editObject= mapFeedBack.createMapObjectModel();setLocation(mapFeedBack.noMap?'road_new':'choose_location_road_new/road_new')\"]\"";
And tried to access my css class using that variable:
element.all(by.css(road_button)).click();
element.all(by.css(road_missing)).click();
But some how I'm not able to access these variable value. Can you please provide me the right way of doing this?
Thank you
I think you are sort of reinventing the wheel. What you really need to follow is the guideline to use Page Objects. They would not only solve your problem of separating locators from the test flow and test scenario logic, but would also make the the code more modular and easy to adapt to never-ending changes on the application side.
Here is a great practical introduction to Page Objects in Protractor:
Page Objects
I have been writing a reusable script, let's call it a plugin although it's not jQuery, that can be initialised in a declarative way from the HTML. I have extremely simplified it to explain my question so let's say that if a user inserts a tag like:
<span data-color="green"></span>
the script will fire because the attribute data-color is found, changing the color accordingly.
This approach proved very handy because it avoids anyone using the plugin having to initialise it imperatively in their own scripts with something like:
var elem = document.getElementsByTagName('span')[0];
myPlugin.init(elem);
Moreover by going the declarative way I could get away without defining any global (in this case myPlugin), which seemed to be a nice side effect.
I simplified this situation in an example fiddle here, and as you can see a user can avoid writing any js, leaving the configuration to the HTML.
Current situation
The plugin is wrapped in a closure like so:
;(function(){
var changeColor = {
init : function(elem){
var bg = elem.getAttribute('data-color');
elem.style.background = bg;
}
};
// the plugin itslef looks for appropriate HTML elements
var elem = document.querySelectorAll('[data-color]')[0];
// it inits itself as soon as it is evaluated at page load
changeColor.init(elem);
})();
The page loads and the span gets the correct colour, so everything is fine.
The problem
What has come up lately, though, is the need to let the user re-evaluate/re-init the plugin when he needs to.
Let's say that in the first example the HTML is changed dynamically after the page is loaded, becoming:
<span data-color="purple"></span>
With the first fiddle there's no way to re-init the plugin, so I am now testing some solutions.
Possible solutions
Exposing a global
The most obvious is exposing a global. If we go this route the fiddle becomes
http://jsfiddle.net/gleezer/089om9z5/4/
where the only real difference is removing the selection of the element, leaving it to the user:
// we remove this line
// var elem = document.querySelectorAll('[data-color]')[0];
and adding something like (again, i am simplifying for the sake of the question):
window.changeColor = changeColor;
to the above code in order to expose the init method to be called from anywhere.
Although this works I am not satisfied with it. I am really looking for an alternative solution, as I don't want to lose the ease of use of the original approach and I don't want to force anyone using the script adding a new global to their projects.
Events
One solution I have found is leveraging events. By putting something like this in the plugin body:
elem.addEventListener('init', function() {
changeColor.init(elem);
}, false);
anybody will be able to just create an event an fire it accordingly. An example in this case:
var event = new CustomEvent('init', {});
span.dispatchEvent(event);
This would re-init the plugin whenever needed. A working fiddle is to be found here:
http://jsfiddle.net/gleezer/tgztjdzL/1/
The question (finally)
My question is: is there a cleaner/better way of handling this?
How can i let people using this plugin without the need of a global or having to initialise the script themselves the first time? Is event the best way or am I missing some more obvious/better solutions?
You can override Element.setAttribute to trigger your plugin:
var oldSetAttribute = Element.prototype.setAttribute;
Element.prototype.setAttribute = function(name, value) {
oldSetAttribute.call(this, name, value);
if (name === 'data-color') {
changeColor.init(this);
}
}
Pros:
User does not have to explicitly re-initialize the plugin. It will happen automatically when required.
Cons:
This will, of course, only work if the user changes data-color attributes using setAttribute, and not if they create new DOM elements using innerHTML or via some other approach.
Modifying host object prototypes is considered bad practice by many, and for good reasons. Use at your own risk.
I've noticed that RequireJS creates script tags in the tag as it loads modules.
Is there anyway to configure RequireJS to "tag" those elements w/ a class or an attribute of some kind that I could later target w/ jQuery later on?
e.g.:
var $requireJsScripts = $('script.require-script');
--UPDATE--
Ok.. I think I can get by on this little workaround for now. Thanks to this answer for the breadcrumb on require.s.contexts._.defined. I'd still like to hear if anyone knows of a way to configure RequireJS to do something similar to what was laid out in the original question...
var loadedRjsModules = Object.keys(require.s.contexts._.defined);
var $scripts = $('script');
$scripts.each(function () {
if ($(this).data('requiremodule') && $.inArray($(this).data('requiremodule'), loadedRjsModules)) {
console.log(this);
}
});
Looking at the source code, I don't see how RequireJS would allow adding anything custom to the script nodes at creation. The routine that creates them has no provision for it. The code that fleshes them out upon creation does not support it either.
There's an onResourceLoad hook considered part of the internal API. It could be used with the code you've put in your question instead of relying on require.s.contexts._.defined, which as far as I know is fully private and subject to change without notice.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 years ago.
Improve this question
I am working on a project where I need to dynamically build up web content coming from different sources (same domain). All the main libraries and css are managed centrally but depending on a configuration I have to add pages (html and embedded JavaScript) as 'tab content'.
My first approach was using iframes and it worked well besides the annoyance that I had some duplicte code (especially the header). I also had some issues with frame sizes but I could handle that in the end.
Now of course I read that iframes are evil but almost all the alternatives I found were using php or something else server-side. Unfortunately my web server does not provide anything like that, so I came across jQuery's load function. My first attempt is satisfying but what unsettles me now is managing (global) variables (and functions). Because jQuery.load() simply inserts the code into the DOM I always need to be extremely careful about naming here. For example I usually have a function called init() which is included in the body for the onload-Event. But every other page will need one of those as well.
The project is very likely to grow (maybe at some point even external developers will take part). I am now at a point where I have to decide which path to take and I am torn.
So my Question: Even though iframes are evil, im my experience it is a lot easier and secure than inserting the code via jQuery (in this particular case). Oh, that was not a question... The question would be: Is there any way to encapsulate variables and functions into the content that I load with jQuery? Or might there be a better way of handling variables/functions in this case anyways.
I am grateful for any suggestions.
I don't want to suggest to do it this way, but because you want to to know how this can be done, here some code to illustrate how it could be done (only rudimentary tested and should not be used without further testing).
While I'm not sure if you could (and i think you should not) structure your project that way i hope that it will help to solve your problem.
The main code of your page:
var app = {};
(function() {
var nextId = 1;
var elementById = {};
app.registerContent = function( id, initFunction ) {
try {
initFunction(elementById[id]);
} catch( e ) {
//if an error happend in the init script catch it so that the script will not break
console.error(e);
}
//remove the jquery element from the list
delete elementById[id];
}
//custom loader to handle init script
jQuery.fn.customLoader = function(url) {
var elm = $(this);
var id = nextId++;
//store the element where the data is loaded to in a list
elementById[id] = elm;
$.get(url,function(data) {
//replace the id in the script so that the element the data is loaded into
// can be passed to the init script later.
data = data.replace("%%unique-id%%",id);
elm.html(data); //append the data to the element
});
}
//a sample how you would load the content
$(function() {
$(".dest").customLoader("content.html");
});
})();
The code in your requested data:
<div>some content</div>
<!-- the script that correspons to that part -->
<script>
//create a scope using a function that is executed directly
(function() {
//using var to make the variables only visible to this place
var uniqueId = %%unique-id%%; //this will be replaced by the loader to identify the element where the data is loaded to
var someVar = 1;
app.registerContent(uniqueId, initFunctionForTheContent)
function initFunctionForTheContent(element) {
element.css("background-color", "red")
}
})();
</script>
Not having a back end with a huge app and many developers sounds like hell. Sometimes things are best fitted on the server side e.g php's include.
Either way I would go with an MVC solution.
I have experience with backbone and underscore so I will give an example with that but there are many other MVC solutions.
If all you need to do is load some data you can use underscores templating which is very lightweight
You can do stuff like:
In the model:
sum: function() {
Sum certain properties of your objects.
}
In index.html
<script type="text/template" id="sum-template">
sum: <% print(sum) %>
</script
In the view
sumTemplate: _.template($('#sum-template').html()),
this.$el.find('#sum').html(this.sumTemplate({sum:Expenses.sum()}));
As you can see you can get data from different places and load them to certain HTML elements. It easy to manage on the long run.
In order to persist data you can free and paid hosted data bases a simple google search for
redis hosting, mongodb hosting etc... so you are not dependent on your current server.