Skip to content

Fix JavaScript example roulette#1757

Closed
wilzbach wants to merge 1 commit intodlang:masterfrom
wilzbach:fix-js-roulette
Closed

Fix JavaScript example roulette#1757
wilzbach wants to merge 1 commit intodlang:masterfrom
wilzbach:fix-js-roulette

Conversation

@wilzbach
Copy link
Contributor

As discussed here, to avoid page reflows, we should pick an example ASAP and render its HTML.

Unfortunately this required quite a bit of work as the original code heavily used jQuery for which we don't want to wait until it's loaded. I am not really happy with it, but I guess that's the necessary evil if we want to avoid the ugly page redraws (and don't have a server to do the selection).

I think it would be nice to have a drop down, like on the golang page. I remember back some time ago when I wanted to show one particular example to a friend that I had to reload the page a couple of times to get to it.

That was fairly trivial to add and requiring a title for an example and reduces the manual checking from @CyberShadow ;-)

The example selector still needs styling, hence I am marking this as "Needs work" for now.

index.dd Outdated
// Picking the frontpage example after the page has loaded leads to reflows
// and rerendering. Hence, we pick the example as soon as possible
/**
Bootstrap a parent node containing a <pre> element into an editor instance.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does "bootstrap" mean here?

index.dd Outdated
return btn;
}
// helper method to construct a textarea in vanilla JavaScript
function createTextArea(className, value, text) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This helper function is used only once, inline?

index.dd Outdated
return el;
}
// helper method to construct an editor overlay in vanilla JavaScript
// typically overlays are output,args,stdin and the editor instance
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does "overlay" mean here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everything that "lays over" the code box, e.g.

image
image
image

As mentioned, there weren't any comments, so I have chosen my own terminology. Better ideas?

index.dd Outdated
el.appendChild(inner);
return el;
}
// JavaScript filter for humans
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty sure this function is not used for filtering humans. :P

@CyberShadow
Copy link
Member

CyberShadow commented Jun 21, 2017

Good stuff. I hadn't thought of actually putting the code inline, but this approach does have merits. I forgot we're already loading all our JavaScript at the end of the HTML, so there will be no libraries to use at that point. I wonder though if it's possible to reduce code duplication (if there is any) and the number of things that are in index.dd, e.g. by putting the JavaScript in a separate file and including it as a DDoc macro (e.g. js/index-examples.js.ddoc).

It's not immediately obvious from the code (and I haven't looked at it in depth) whether this is what it already does, but doing just the bare minimum to show the example would be the goal for the inline JavaScript code. Showing the buttons and getting syntax highlighting for the editor working can be a task for the rest of the JavaScript code that's set up. The inline code's goal is to just do the bare minimum for the layout (ideally even avoid a re-layout later) and hand things over to the main code once it's loaded.

@wilzbach
Copy link
Contributor Author

I wonder though if it's possible to reduce code duplication (if there is any)

The first commit is a more or less literal translation of the code in js/run.js.
Sadly the only comment in there is from me (when I added an improvement to it).
Hence, some of the comments I added for better understanding might have a confusing terminology.

and the number of things that are in index.dd, e.g. by putting the JavaScript in a separate file and including it as a DDoc macro (e.g. js/index-examples.js.ddoc).

I thought about this as well, but I wasn't sure whether it's worth the extra step.
OTOH this would allow us to minify/uglify the inline JavaScript.

It's not immediately obvious from the code (and I haven't looked at it in depth) whether this is what it already does, but doing just the bare minimum to show the example would be the goal for the inline JavaScript code.

Yes, that was my goal.

Showing the buttons and getting syntax highlighting for the editor working can be a task for the rest of the JavaScript code that's set up.

You need the buttons - they will trigger a reflow as well ;-)
The code just creates an empty textarea into which the existing setupTextarea injects the CodeMirror editor.

The inline code's goal is to just do the bare minimum for the layout (ideally even avoid a re-layout later) and hand things over to the main code once it's loaded.

Yes it does exactly this - I basically moved the entire layout setup function inline.

@wilzbach wilzbach force-pushed the fix-js-roulette branch 2 times, most recently from c78067a to c966202 Compare June 21, 2017 23:50
@wilzbach
Copy link
Contributor Author

The example selector still needs styling, hence I am marking this as "Needs work" for now.

Done. I think this is styling should get us started:

image

e.g. by putting the JavaScript in a separate file and including it as a DDoc macro (e.g. js/index-examples.js.ddoc).

Done. I also tried to clean the script a bit further. For example, only the pre elementNode (i.e. the one with the code) is passed in and touched now.

@CyberShadow
Copy link
Member

Looks nice :) @andralex What do you think?

@CyberShadow
Copy link
Member

You need the buttons - they will trigger a reflow as well ;-)

Why? Just reserve space for them.

I don't understand why the code needs to do anything beyond displaying one of the divs that are hidden by default.

index.dd Outdated
D=<span class="d_inlinecode">$0</span>
EXAMPLE=$(TAG2 a, id="a$1-control" class="example-control", )$(TAG2 div, id="a$1" class="example-box", $(RUNNABLE_EXAMPLE $2))
EXTRA_EXAMPLE=<div class="your-code-here-extra" style="display:none">$(RUNNABLE_EXAMPLE $0)</div>
EXTRA_EXAMPLE=<div class="your-code-here-extra" style="display:none">
Copy link
Member

@PetarKirov PetarKirov Jun 24, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EXAMPLE_ROULETTE_ENTRY sounds better to me.

Be careful, Ddoc forbids certain patterns, e.g.:
- using < or >
- direct assignments (foo = bar) are interpreted as new Ddoc variables
)
Copy link
Member

@PetarKirov PetarKirov Jun 24, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What prevents adding this as a plain js file under e.g. js/example_roulette.js? Can ddoc string-import-mixin other files?
Is loading an external file via <script src="${DLANG_SITE}/js/example_roulette.js" type="text/javascript"></script> not an option? Or alternatively, mix the js script in, after ddoc processing, as part of the build.

@PetarKirov
Copy link
Member

PetarKirov commented Jun 24, 2017

Overall I really like the improvements in this PR (as an end-user), especially the added <select> box!
As an improvement, I would like to propose creating a homepage-examples folder so we can add plain *.d files inside it. This should make it easier for people not well versed with ddoc to contribute and it should be easier to maintain. golang also does it like that: https://golang.org/doc/play/, however the list of examples is hard-coded and they have the first example in-line (I guess in order to improve the page load time / prevent reflows), see https://github.com/golang/go/blob/master/doc/root.html#L35.
What's left is to decide whether the examples should be gathered as part of the build process or should fetch them dynamically with JS (like golang does for the rest of the examples).

@PetarKirov PetarKirov closed this Jun 24, 2017
@PetarKirov PetarKirov reopened this Jun 24, 2017
@dlang-bot
Copy link
Contributor

dlang-bot commented Jun 24, 2017

Thanks for your pull request, @wilzbach!

Bugzilla references

Your PR doesn't reference any Bugzilla issue.

If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog.

@PetarKirov
Copy link
Member

Sorry, for the close - wrong button :(

@wilzbach
Copy link
Contributor Author

Is loading an external file via <script src="${DLANG_SITE}/js/example_roulette.js" type="text/javascript"></script> not an option?

I thought about this for a while and while this approach has the disadvantage of potentially being a bit slower, it allows to use a proper JavaScript file (syntax-highlighting) and avoids the constant fights against Ddoc when writing inline JS.
Moreover, browsers do peek through the entire DOM and look for source tags while downloading the found ones in parallel.

The simulated page load waterfall for "Fast 3G" shows this nicely:

image

Of course the simulation, isn't perfect, but it does show that a lot of other stuff needs to be download before the page can be initially drown. DOMContentLoaded (the blue line) is at 4.13s

Or alternatively, mix the js script in, after ddoc processing, as part of the build.

This would add another step to the build process and currently @CyberShadow is opposed to this (see e.g. #1688 (comment)).

@wilzbach
Copy link
Contributor Author

As an improvement, I would like to propose creating a homepage-examples folder so we can add plain *.d files inside it. This should make it easier for people not well versed with ddoc to contribute and it should be easier to maintain.

Excellent idea! But let's do this in another PR. This one is already big enough.

el.appendChild(titleEl);
el.appendChild(document.createElement("br"));
var inner = document.createElement(type);
inner.className = className;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, the returned element and the inner "type" element have same class. Is that on purpose and why?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, because I tried to make a 1:1 copy of the jQuery version, see e.g. https://github.com/dlang/dlang.org/blob/master/js/run.js#L138

stdin = '<div class="d_code_stdin"><span class="d_code_title">Standard input</span><br>'
        + '<textarea class="d_code_stdin">'+stdin+'</textarea></div>';

Imho "translation" and refactoring shouldn't be mixed (even though the temptation is very strong).

@CyberShadow
Copy link
Member

OK, so, as I understand, this PR does three things right now:

  1. Fix the JS roulette
  2. Select an example during page load instead of after, to avoid a flash of the default noscript example
  3. Add a dropdown to select an example

IMO these should be separate PRs, or at least commits. Also I still have a strong suspicion that the second one could be much simpler as mentioned above.

@PetarKirov
Copy link
Member

PetarKirov commented Jun 25, 2017

@wilzbach something in this PR somehow broke the layout, see:

Before:
before

After:
after

(That's with Chrome 58 on Windows 10.)

Last time I tried the DAutoTest preview (I think it was yesterday) I didn't notice any layout problems.

@wilzbach
Copy link
Contributor Author

Fix the JS roulette

-> #1767

Select an example during page load instead of after, to avoid a flash of the default noscript example
Add a dropdown to select an example

Hehe. I initially had separate commits, but then squashed because it easier due to the several steps (inline Ddoc -> Ddoc as separate file -> separate Js).

I for now simply dropped the commit.
I will do this as follow-up

@wilzbach something in this PR somehow broke the layout, see:

Yeah, I realized this myself. Already fixed. Sorry.

// step 2: select the first <pre> element (of the .runnable-example <div>)
var el = findFirst(rouletteChildNode.children, function(e) { return e.nodeName == "PRE";});
// step 3: Construct runnable example layout within the <pre> element
buildRunnableExampleLayout(el);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CyberShadow

Also I still have a strong suspicion that the second one could be much simpler as mentioned above.

Yes, these lines without buildRunnableExampleLayout would - in theory - be enough. However, it still "jumps" due to the buttons added and just

  • adding the buttons isn't a good solution imho, because (1) it creates code entanglement (at another random, JS place the script needs to be aware of this) and (2) it creates code redundancy and makes it a lot harder if someone wants to change the overall layout
  • adding sth. el.style.marginBottom = "20px" is a similar hack (and still flashes/"jumps")

@wilzbach
Copy link
Contributor Author

Rebased.

@CyberShadow let's come to a conclusion here: are you okay with this now or do you want me to come up with an ugly hack that "fakes" the bottoms with sth. like marginBottom? As explained I would prefer to have one source of truth for the layout generation.

@CyberShadow
Copy link
Member

Less code is better. There is way too much code with a bus factor of 1 on dlang.org already. As far as I can see, index.dd just needs like 5 lines of JavaScript, at most. If you don't feel like doing that, I can put it on my list.

@wilzbach
Copy link
Contributor Author

Less code is better. There is way too much code with a bus factor of 1 on dlang.org already. As far as I can see, index.dd just needs like 5 lines of JavaScript, at most. If you don't feel like doing that, I can put it on my list.

Opened another PR with this approach

#1775

As mentioned the redraw flashes are still partially visible ...

@wilzbach
Copy link
Contributor Author

Closing this with regrets as it wasn't trivial to refactor the jQuery code, but in the end the smaller diffs won.

@wilzbach wilzbach closed this Jun 30, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants